Skip to main content
The MySQL SQL Editor implements the classic Model-View-Controller (MVC) design pattern to separate data management, user interface, and application logic.

MVC Architecture Overview

The MVC pattern ensures that changes to the UI don’t affect data handling, and business logic remains independent of the presentation layer.

Model Layer

Model.java - Data Access Layer

The Model is responsible for all database interactions and maintains the connection state. Location: com.app.Model.Model

Key Responsibilities

Connection Management

Establishes and maintains MySQL database connections

Query Execution

Executes SQL queries and returns results as table models

Metadata Retrieval

Fetches database and table information

Data Transformation

Converts ResultSet to DefaultTableModel for UI display

Core Methods

public class Model {
    private Connection conexion;
    
    // Establish connection to a specific database
    public void conectar(String host, String database, 
                        String usuario, String password) throws SQLException
    
    // Close active database connection
    public void desconectar() throws SQLException
    
    // Retrieve all available databases (excluding system DBs)
    public List<String> obtenerTodasLasBasesDatos(String host, 
                                                   String usuario, 
                                                   String password) throws SQLException
    
    // Get tables from currently connected database
    public List<String> obtenerTablasDeBaseDatos() throws SQLException
    
    // Execute SQL query and return results as table model
    public DefaultTableModel ejecutarConsulta(String query) throws SQLException
}
public void conectar(String host, String database, String usuario, String password) 
    throws SQLException {
    conexion = DriverManager.getConnection(
        "jdbc:mysql://" + host + ":3306/" + database + "?useSSL=false",
        usuario,
        password
    );
}
This method constructs a JDBC URL and establishes a connection using DriverManager.
The ejecutarConsulta() method handles both SELECT and data modification queries:
public DefaultTableModel ejecutarConsulta(String query) throws SQLException {
    try (Statement stmt = conexion.createStatement()) {
        String trimmedQuery = query.trim().toLowerCase();
        DefaultTableModel modelo = new DefaultTableModel();

        if (trimmedQuery.startsWith("select")) {
            // Handle SELECT queries - return result set
            try (ResultSet rs = stmt.executeQuery(query)) {
                ResultSetMetaData meta = rs.getMetaData();
                int columnas = meta.getColumnCount();
                
                // Add column headers
                for (int i = 1; i <= columnas; i++) {
                    modelo.addColumn(meta.getColumnName(i));
                }
                
                // Add data rows
                while (rs.next()) {
                    Object[] fila = new Object[columnas];
                    for (int i = 0; i < columnas; i++) {
                        fila[i] = rs.getObject(i + 1);
                    }
                    modelo.addRow(fila);
                }
            }
        } else {
            // Handle INSERT, UPDATE, DELETE, etc.
            int filasAfectadas = stmt.executeUpdate(query);
            // Extract table name and show updated data
            String tabla = extraerNombreTabla(trimmedQuery);
            // ... returns table contents or affected rows count
        }
        
        return modelo;
    }
}
The method intelligently returns either query results or the updated table contents after modifications.

View Layer

The application has two distinct views, both built with Java Swing:

View.java - Login Interface

Location: com.app.View.View The login view handles database connection setup.

UI Components

  • JComboBox cmbServidores - Server selection (localhost, 127.0.0.1)
  • JTextField txtUsuario - Username input
  • JPasswordField txtPassword - Password input
  • JComboBox cbBasesDatos - Database selection dropdown
  • JButton btnConectar - Initiates connection
  • JButton btnActualizarBases - Refreshes database list
  • JButton btnSalir - Exits application
  • JLabel lblEstado - Displays error messages

Key Methods

public class View extends JFrame {
    // Getters for user input
    public String getUsuario()
    public String getPassword()
    public String getServidor()
    public String getBaseDatosSeleccionada()
    
    // Button accessors for controller
    public JButton getBtnConectar()
    public JButton getBtnSalir()
    public JButton getBtnActualizarBases()
    
    // UI update methods
    public void setBasesDatos(List<String> bases)
    public void mostrarError(String mensaje)
    public void limpiarEstado()
    public void bloquearInterfaz(boolean bloquear)
}
The view uses custom color scheme matching the brand:
private final Color COLOR_PRIMARIO = new Color(0, 120, 215); // #0078D7
private final Color COLOR_SECUNDARIO = new Color(100, 180, 255);
private final Color COLOR_TEXTO = new Color(60, 60, 60);
Buttons include hover effects for better UX:
boton.addMouseListener(new java.awt.event.MouseAdapter() {
    public void mouseEntered(java.awt.event.MouseEvent evt) {
        boton.setBackground(primario ? COLOR_PRIMARIO.darker() : 
                            COLOR_SECUNDARIO.darker());
    }
    public void mouseExited(java.awt.event.MouseEvent evt) {
        boton.setBackground(primario ? COLOR_PRIMARIO : COLOR_SECUNDARIO);
    }
});

SqlEditorView.java - SQL Editor Interface

Location: com.app.View.SqlEditorView The main editor view for writing and executing SQL queries.

UI Components

  • JTextArea txtConsulta - SQL query editor with monospace font
  • JTable tblResultados - Displays query results
  • JList listaTablas - Shows available tables (double-click to generate SELECT)
  • JTextField txtBaseDeDatos - Shows current database connection
  • JButton btnEjecutar - Executes the SQL query
  • JButton btnLimpiar - Clears editor and results
  • JButton btnRefrescarTablas - Refreshes table list
  • JButton btnCambiarBD - Disconnects and returns to login
  • JLabel lblMensajeSistema - Shows query execution status

Key Methods

public class SqlEditorView extends JFrame {
    // Input retrieval
    public String getConsulta()
    
    // Button accessors
    public JButton getBtnEjecutar()
    public JButton getBtnLimpiar()
    public JButton getBtnRefrescarTablas()
    public JButton getBtnCambiarBD()
    
    // UI updates
    public void setResultados(javax.swing.table.TableModel model)
    public void setMensajeSistema(String mensaje)
    public void limpiar()
    public void setBaseDeDatos(String nombreBD)
    public void actualizarListaTablas(List<String> tablas)
}
Double-clicking a table name auto-generates a SELECT query:
listaTablas.addMouseListener(new MouseAdapter() {
    public void mouseClicked(MouseEvent evt) {
        if (evt.getClickCount() == 2) {
            String tablaSeleccionada = listaTablas.getSelectedValue();
            if (tablaSeleccionada != null) {
                String consulta = "SELECT * FROM " + tablaSeleccionada + " LIMIT 100";
                txtConsulta.setText(consulta);
            }
        }
    }
});

Controller Layer

Controller.java - Application Logic Coordinator

Location: com.app.Controller.Controller The Controller bridges the Model and Views, handling all user interactions.

Initialization

public class Controller {
    private final View loginView;
    private final SqlEditorView editorView;
    private final Model modelo;

    public Controller(View loginView, SqlEditorView editorView, Model model) {
        this.loginView = loginView;
        this.editorView = editorView;
        this.modelo = model;

        configurarListeners();
        loginView.setVisible(true);
    }
}
The Controller receives references to both views and the model, then registers event listeners for all user actions.

Event Handling

The Controller configures listeners for all interactive components:
1

Login View Listeners

loginView.getBtnActualizarBases().addActionListener(e -> {
    loginView.limpiarEstado();
    validarCamposLogin(true);
    cargarBasesDatos();
});

loginView.getBtnConectar().addActionListener(e -> {
    loginView.limpiarEstado();
    if(validarCamposLogin(false)) {
        conectarABaseDatos();
    }
});

loginView.getBtnSalir().addActionListener(e -> System.exit(0));
2

Editor View Listeners

editorView.getBtnEjecutar().addActionListener(e -> ejecutarConsulta());
editorView.getBtnLimpiar().addActionListener(e -> limpiarEditor());
editorView.getBtnRefrescarTablas().addActionListener(e -> refrescarTablas());

editorView.getBtnCambiarBD().addActionListener(e -> {
    int confirm = JOptionPane.showConfirmDialog(editorView,
        "¿Desea desconectarse y cambiar de base de datos?",
        "Confirmar",
        JOptionPane.YES_NO_OPTION);

    if (confirm == JOptionPane.YES_OPTION) {
        desconectar();
    }
});

Core Controller Methods

private boolean validarCamposLogin(boolean soloCredenciales) {
    if(loginView.getUsuario().isEmpty()) {
        loginView.mostrarError("El usuario es requerido");
        return false;
    }

    if(loginView.getPassword().isEmpty()) {
        loginView.mostrarError("La contraseña es requerida");
        return false;
    }

    if(!soloCredenciales && loginView.getBaseDatosSeleccionada() == null) {
        loginView.mostrarError("Debe seleccionar una base de datos");
        return false;
    }

    return true;
}
Validates user input before attempting database operations.
private void cargarBasesDatos() {
    if(!validarCamposLogin(true)) return;

    loginView.bloquearInterfaz(true);

    SwingWorker<List<String>, Void> worker = new SwingWorker<>() {
        @Override
        protected List<String> doInBackground() throws Exception {
            return modelo.obtenerTodasLasBasesDatos(
                loginView.getServidor(),
                loginView.getUsuario(),
                loginView.getPassword()
            );
        }

        @Override
        protected void done() {
            loginView.bloquearInterfaz(false);
            try {
                List<String> bases = get();
                if (bases.isEmpty()) {
                    loginView.mostrarError("No se encontraron bases de datos");
                } else {
                    loginView.setBasesDatos(bases);
                    JOptionPane.showMessageDialog(loginView,
                        "Bases de datos actualizadas correctamente",
                        "Éxito", JOptionPane.INFORMATION_MESSAGE);
                }
            } catch (Exception ex) {
                loginView.mostrarError("Error: " + ex.getMessage());
            }
        }
    };

    worker.execute();
}
Uses SwingWorker to load databases asynchronously, preventing UI freeze.
private void conectarABaseDatos() {
    loginView.bloquearInterfaz(true);

    SwingWorker<Boolean, Void> worker = new SwingWorker<>() {
        @Override
        protected Boolean doInBackground() throws Exception {
            modelo.conectar(
                loginView.getServidor(),
                loginView.getBaseDatosSeleccionada(),
                loginView.getUsuario(),
                loginView.getPassword()
            );
            return true;
        }

        @Override
        protected void done() {
            loginView.bloquearInterfaz(false);
            try {
                get();
                String nombreBD = loginView.getBaseDatosSeleccionada();
                editorView.setBaseDeDatos(nombreBD);

                List<String> tablas = modelo.obtenerTablasDeBaseDatos();
                editorView.actualizarListaTablas(tablas);

                loginView.setVisible(false);
                editorView.setVisible(true);
            } catch (Exception ex) {
                editorView.setBaseDeDatos(null);
                editorView.actualizarListaTablas(Collections.emptyList());
                loginView.mostrarError("Error de conexión: " + ex.getMessage());
            }
        }
    };

    worker.execute();
}
Connects to the database and transitions from login view to editor view upon success.
private void ejecutarConsulta() {
    if(editorView.getConsulta().trim().isEmpty()) {
        JOptionPane.showMessageDialog(editorView,
            "Ingrese una consulta SQL",
            "Advertencia", JOptionPane.WARNING_MESSAGE);
        return;
    }

    SwingWorker<DefaultTableModel, Void> worker = new SwingWorker<>() {
        @Override
        protected DefaultTableModel doInBackground() throws Exception {
            return modelo.ejecutarConsulta(editorView.getConsulta());
        }

        @Override
        protected void done() {
            try {
                DefaultTableModel modelo = get();
                editorView.setResultados(modelo);
                editorView.setMensajeSistema("Consulta ejecutada correctamente");
            } catch (Exception ex) {
                editorView.setMensajeSistema("Error: " + ex.getMessage());
                JOptionPane.showMessageDialog(editorView,
                    "Error en la consulta: " + ex.getMessage(),
                    "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    };

    worker.execute();
}
Executes SQL queries asynchronously and updates the UI with results or errors.

Application Initialization

Main.java - Entry Point

Location: com.app.Main The Main class bootstraps the MVC components:
public class Main {
    public static void main(String[] args) {
        // Instantiate the model
        Model model = new Model();

        // Instantiate the login view
        View loginView = new View();

        // Instantiate the SQL editor view
        SqlEditorView editorView = new SqlEditorView();

        // Hide the editor initially
        editorView.setVisible(false);

        // Create the controller, connecting model and views
        new Controller(loginView, editorView, model);
    }
}
1

Model Creation

A single Model instance is created to manage all database operations.
2

View Instantiation

Both views are created upfront. The editor view is hidden initially.
3

Controller Wiring

The Controller receives all three components and sets up the event handling infrastructure.
4

Display Login

The Controller shows the login view, and the application is ready for user interaction.

MVC Interaction Sequence

Benefits of This MVC Implementation

Loose Coupling

Views don’t directly interact with the Model, only through the Controller

Single Responsibility

Each class has one clear purpose: data, UI, or coordination

Easy Testing

Model logic can be unit tested without UI dependencies

Async by Default

Controller uses SwingWorker for all I/O operations

Next Steps

Component Documentation

Explore detailed documentation for each component including all methods and their signatures

Build docs developers (and LLMs) love