
Modelo Vista Controlador en Java y MySQL. Nivel Básico
18 noviembre, 2014 ◆ 16 comentariosConoces los fundamentos de la programación orientada a objetos y has trabajado con bases de datos. También has implementado interfaces para interactuar con los usuarios de tus aplicaciones. Pero hasta ahora lo has hecho sin separar metodológicamente cada uno de estos componentes. Si quieres ir un paso más allá y mejorar en arquitectura software, has llegado al lugar adecuado.
En este ejemplo se tratarán las directrices básicas para afrontar metodología MVC (Modelo – Vista – Controlador), un modelo maduro que ha demostrado su validez a lo largo de los años en todo tipo de aplicaciones y sobre multitud de lenguajes y plataformas de desarrollo.
En concreto, en esta guía rápida se empleará como base de datos MySQL y como lenguaje de programación, Java.
Supuesto Práctico
Se tendrá una base de datos con una tabla donde se almacenará la información de los clientes de nuestro negocio. Desde una interfaz gráfica, un usuario podrá listar, añadir, modificar o eliminar los diversos clientes.
La información a almacenar de los clientes será: nombre, apellidos y NIF
Tanto la vista como el controlador estarán implementados en Java mediante Eclipse. Sin embargo, debido a su simplicidad, el modelo se implementará en MySQL. Se hará uso de procesos almacenados (stored procedures) en la base de datos.
Debido a que el proyecto Java accederá a una base de datos MySQL, se debe usar un fichero .jar que se encargará de registrar y emplear el driver adecuado para tal conexión. El fichero se puede descargar a través del siguiente enlace (en inglés).
Los temas a tratar, son los siguiente:
- Creación del proyecto
- Codificación de la clase Bd
- Codificación de la clase View
- Codificación de la clase Controller
- Codificación de la clase Principal
- Creación del modelo
- Prueba del proyecto MVC
- Creación de un ejecutable
Creación del proyecto
Se llevará a cabo la creación de un proyecto Java en Eclipse
- Una vez abierto Eclipse, se debe acceder a File > New > Java Proyect
- En el cuadro de diálogo que aparece, se indica el Project name kadumMVC y se selecciona JavaSE-1.7 como execution enviroment JRE.
- Se pulsa sobre el botón Next para añadir el fichero mysql-connector-java-5.0.8-bin.jar que previamente se ha debido descargar.
- En el cuadro de diálogo, se selecciona la pestaña Libraries y se pulsa el botón Add External JARs.
- Se busca y se selecciona el fichero mysql-connector-java-5.0.8-bin.jar
- Una vez añadido el fichero, se pulsa Finish
Una vez creado el proyecto, se procederá a la construcción de las diversas clases que compondrán nuestro proyecto. En los siguientes apartados, se describirá cómo llevar a cabo esta tarea. Las clases que se construirán serán:
- Bd. Se encargará de realizar la conexión al servidor MySQL.
- View. Se encargará de la creación de la interfaz gráfica.
- Principal. Contendrá la función main que se encargará de la creación de los objetos necesarios y de la ejecución de los diversos métodos.
- Controller. Se encargará de recoger los eventos desencadenados por la interfaz gráfica, se los comunicará a nuestro modelo y actualizará si fuere necesario la interfaz. Como se puede comprobar, actúa como intermediario entre la vista y el modelo.
Puesto que el modelo se tratará de forma diferente, no se generará ninguna clase para tal fin. Como se indicó anteriormente, el modelo estará implementado en la base de datos MySQL.
Codificación de la clase Bd
La clase Bd se encargará de realizar la conexión a nuestro servidor. Se tendrán dos métodos, el constructor y un método que devolverá un objeto de la clase Connection. Dicho objeto se empleará en la clase Controller.
- Dentro del proyecto creado, se accede al menú File > New > Class
- En el cuadro de diálogo que aparece, tras escribir el nombre de la clase Bd, se pulsa sobre el botón Finish.
- El código de la clase será el siguiente:
package kadumMVC; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class Bd { private String maquina = "localhost"; private String usuario = "root"; private String clave = ""; private int puerto = 3306; private String servidor = ""; private static Connection conexion = null; //CONSTRUCTOR //Recibe el nombre de la base de datos Bd(String baseDatos){ this.servidor="jdbc:mysql://"+this.maquina+":"+ this.puerto+"/"+baseDatos; //Registrar el driver try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { System.err.println("ERROR AL REGISTRAR EL DRIVER"); System.exit(0); //parar la ejecución } //Establecer la conexión con el servidor try { conexion = DriverManager.getConnection(this.servidor, this.usuario, this.clave); } catch (SQLException e) { System.err.println("ERROR AL CONECTAR CON EL SERVIDOR"); System.exit(0); //parar la ejecución } System.out.println("Conectado a "+baseDatos); } //Devuelve el objeto Connection que se usará en la clase Controller public static Connection getConexion() { return conexion; } }
Se observa que se realizará la conexión a localhost a través del puerto 3306, mediante el usuario root, el cual no tiene contraseña. En el momento de la creación de un objeto de la clase Bd, se le indicará el nombre de la base de datos a la que se desea conectar.
Codificación de la clase View
La clase View se encarga de la generación de la interfaz gráfica de la aplicación. La clase View extenderá la clase JFrame. Para llevar a la creación de los distintos componentes de la interfaz, se hará uso de la librería javax.swing. La colocación de los componentes se implementará mediante la clase SpringLayout
El resultado que se obtendrá será la generación de una ventana como la mostrada a continuación, donde el usuario podrá ver la relación de clientes almacenados en la base de datos. Tendrá la posibilidad de añadir nuevos clientes o editar o borrar clientes existentes.
Componentes
Los componentes que se emplearán son los mostrados en la imagen siguiente:
Todos los componentes irán colocados sobre un objeto JPanel.
Las tres etiquetas se definirán como objetos de la clase JLabel.
Los tres cuadros de texto se definirán como objetos de la clase JTextField.
Los tres botones se definirán como objetos de la clase JButton.
Para la construcción de la tabla, se emplearán varios elementos:
- Un objeto JScrollPane.
- En el interior del objeto JScrollPane, se colocará un objeto de la clase JTable, el cual representa la tabla propiamente dicha.
- La tabla se construirá a partir de un elemento JDefaultTableModel, el cual unirá la cabecera de la tabla con el cuerpo de la tabla propiamente dicho.
- Para la cabecera de la tabla, se empleará un array de cadenas de caracteres – String[].
- Para el cuerpo propiamente dicho, se empleará una matriz de Object – Object[][].
Posiciones de los componentes
Tal y como se ha comentado, las posiciones de los componentes se realizarán mediante la función putConstraint() de la clase SpringLayout.
Dicha función recibe 5 parámetros:
putConstraint(lado, delComponente, separado, delLado, deOtroComponente)
- Los dos primeros parámetros (lado, delComponente) hacen referencia a uno de los 4 lados de un componente en concreto.
- El tercer parámetro (separado) indica la separación en píxeles que se desea tener respecto a… Es aquí donde entran en función el cuarto y quinto parámetro.
- Los dos últimos parámetros (delLado, deOtroComponente), indican respecto a qué lado de qué componente se desea realizar la separación indicada.
El valor para los parámetros 1 y 4 podrán ser NORTH, SOUTH, EAST, WEST.
El valor para la separación podrá ser positivo o negativo. Cuando el 4 parámetro sea SOUTH o WEST, en nuestro caso, las cantidades serán negativas, ya que se toma como referencia el contenedor.
Para más información de la función putConstraint, visitar el enlace siguiente en inglés:
Por simplicidad, todos los componentes se separarán una cantidad de píxeles en función del contenedor principal.
Se observa la separación de las tres etiquetas respecto al contenedor principal.
Componentes | NORTH | WEST |
---|---|---|
Etiqueta para el nombre | 10 px | 10 px |
Etiqueta para los apellidos | 50 px | 10 px |
Etiqueta para el NIF | 90 px | 10 px |
Para el caso de los cuadros de texto, las separaciones serán:
Componentes | NORTH | WEST | EAST |
---|---|---|---|
Cuadro de texto para el nombre | 10 px | 100 px | 300 px |
Cuadro de texto para los apellidos | 50 px | 100 px | 300 px |
Cuadro de texto para el NIF | 90 px | 100 px | 300 px |
Para el JScrollPane (que contendrá en su interior la tabla)
Componentes | NORTH | WEST | EAST | SOUTH |
---|---|---|---|---|
JScrollPane | 120 px | 10 px | -10 px | -50 px |
Por último, para el caso de los botones:
Componentes | SOUTH | WEST |
---|---|---|
Botón Añadir | -10 px | 60 px |
Botón Borrar | -10 px | 190 px |
Botón Editar | -10 px | 310 px |
Eventos
La interfaz debe comunicarse con el futuro controlador (clase Controller) cuando el usuario pulse sobre los botones Añadir, Borrar o Editar, además de cuando pulse sobre una fila de la tabla.
Para los botones se empleará el método addActionListener. Para que el controlador pueda distinguir qué botón se ha pulsado, también se empleará el método setActionCommand.
Para la tabla se usará el método addMouseListener. El controlador actuará cuando se haga click sobre una fila.
Se observa que no se añadirán las acciones a realizar, simplemente cuándo se realizan.
Código
Se está en disposición para la creación de la clase View.
En el constructor se crearán los componentes de la interfaz y en el método conectaControlador, la definición de los eventos.
De forma análoga a la creación de la clase anterior:
- Dentro del proyecto creado, se accede al menú File > New > Class
- En el cuadro de diálogo que aparece, tras escribir el nombre de la clase View, se seleccionará la superclass.
- Se debe pulsar sobre el botón Browser asociado a Superclass. En el cuadro de diálogo que aparece, se escribe JFrame y se selecciona la sugerencia aportada por Eclipse.
- Por último, se pulsa sobre el botón Finish.
El código de la clase será el siguiente.
Si se copia el código, se observa que el parámetro de entrada del método conectaControlador genera un error, ya que la clase Controller aún no está definida.
package kadumMVC; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SpringLayout; import javax.swing.JLabel; import javax.swing.JTextField; import javax.swing.JButton; import javax.swing.JScrollPane; import javax.swing.table.DefaultTableModel; import javax.swing.JTable; public class View extends JFrame { /**************** ATRIBUTOS ***************************/ //CONTENEDOR PRINCIPAL private JPanel contenedor; //DEFINICIÓN DE LAS ETIQUETAS private JLabel lblNombre; private JLabel lblApellido; private JLabel lblNIF; //DEFINICIÓN DE LOS CUADROS DE TEXTO protected JTextField txtNombre; protected JTextField txtApellido; protected JTextField txtNIF; //DEFINICIÓN DE LOS BOTONES protected JButton btnAdd; protected JButton btnDel; protected JButton btnUpd; //DEFINICIÓN DE LOS OBJETOS PARA LA TABLA private JScrollPane scroll; //Panel de scroll que contiene la tabla protected Object[][] datos; //Cuerpo de la tabla protected String[] cabecera; //Cabecera de la tabla protected DefaultTableModel dtm;//Unión de la cabecera y la tabla protected JTable tabla; //Tabla propiamente dicha /**************** MÉTODOS ***************************/ //CONSTRUCTOR View(){ //Métodos de la JFrame setBounds(100, 100, 450, 300);//Definir las dimensiones de la ventana setTitle("GESTIÓN DE CLIENTES - KADUM"); //Barra de título setDefaultCloseOperation(EXIT_ON_CLOSE); //Acción al pulsar salir //CREAR EL CONTENEDOR PRINCIPAL Y AÑADIRLO A LA VENTANA contenedor = new JPanel(); getContentPane().add(contenedor); //INDICAR QUE SE QUIERE USAR SPRINGLAYOUT SpringLayout sp = new SpringLayout(); contenedor.setLayout(sp); //Vamos al lío /**************** BOF ETIQUETAS vvvvvvvvvvvvvvvv **/ //ETIQUETA NOMBRE lblNombre = new JLabel("Nombre:"); //Crear el objeto contenedor.add(lblNombre); //Añadirlo al contenedor sp.putConstraint(SpringLayout.NORTH, lblNombre, 10, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, lblNombre, 10, SpringLayout.WEST, contenedor); //ETIQUETA APELLIDOS lblApellido = new JLabel("Apellidos:"); contenedor.add(lblApellido); sp.putConstraint(SpringLayout.NORTH, lblApellido, 50, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, lblApellido, 10, SpringLayout.WEST, contenedor); //ETIQUETA NIF lblNIF = new JLabel("NIF:"); contenedor.add(lblNIF); sp.putConstraint(SpringLayout.NORTH, lblNIF, 90, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, lblNIF, 10, SpringLayout.WEST, contenedor); /**************** EOF ETIQUETAS ^^^^^^^^^^^^^^^^ **/ /**************** BOF CUADROS DE TEXTO vvvvvvvvv **/ //CUADRO DE TEXTO PARA EL NOMBRE txtNombre = new JTextField(); contenedor.add(txtNombre); sp.putConstraint(SpringLayout.NORTH, txtNombre, 10, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, txtNombre, 100, SpringLayout.WEST, contenedor); sp.putConstraint(SpringLayout.EAST, txtNombre, 300, SpringLayout.WEST, contenedor); //CUADRO DE TEXTO PARA EL NIF txtApellido = new JTextField(); contenedor.add(txtApellido); //añadir al contenedor sp.putConstraint(SpringLayout.NORTH, txtApellido, 50, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, txtApellido, 100, SpringLayout.WEST, contenedor); sp.putConstraint(SpringLayout.EAST, txtApellido, 300, SpringLayout.WEST, contenedor); //CUADRO DE TEXTO PARA LOS APELLIDOS txtNIF = new JTextField(); contenedor.add(txtNIF); sp.putConstraint(SpringLayout.NORTH, txtNIF, 90, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, txtNIF, 100, SpringLayout.WEST, contenedor); sp.putConstraint(SpringLayout.EAST, txtNIF, 300, SpringLayout.WEST, contenedor); /**************** EOF CUADROS DE TEXTO ^^^^^^^^^ **/ /**************** BOF TABLA vvvvvvvvvvvvvvvvvvvv **/ scroll = new JScrollPane(); cabecera = new String[] {"ID","NOMBRE","NIF"}; dtm = new DefaultTableModel(datos,cabecera); tabla = new JTable(dtm); scroll.setViewportView(tabla); //y ahora se coloca el scrollpane... contenedor.add(scroll); //añadir al contenedor sp.putConstraint(SpringLayout.NORTH, scroll, 120, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, scroll, 10, SpringLayout.WEST, contenedor); sp.putConstraint(SpringLayout.EAST, scroll, -10, SpringLayout.EAST, contenedor); sp.putConstraint(SpringLayout.SOUTH, scroll, -50, SpringLayout.SOUTH, contenedor); /**************** EOF TABLA ^^^^^^^^^^^^^^^^^^^^ **/ /**************** BOF BOTONES vvvvvvvvvvvvvvvvvv **/ //BOTÓN AÑADIR btnAdd = new JButton("Añadir"); contenedor.add(btnAdd); sp.putConstraint(SpringLayout.SOUTH, btnAdd, -10, SpringLayout.SOUTH, contenedor);//colocarlo sp.putConstraint(SpringLayout.WEST, btnAdd, 60, SpringLayout.WEST, contenedor); //BOTÓN BORRAR btnDel = new JButton("Borrar"); contenedor.add(btnDel); sp.putConstraint(SpringLayout.SOUTH, btnDel, -10, SpringLayout.SOUTH, contenedor); sp.putConstraint(SpringLayout.WEST, btnDel, 190, SpringLayout.WEST, contenedor); //BOTÓN MODIFICAR btnUpd = new JButton("Editar"); contenedor.add(btnUpd); sp.putConstraint(SpringLayout.SOUTH, btnUpd, -10, SpringLayout.SOUTH, contenedor); sp.putConstraint(SpringLayout.WEST, btnUpd, 310, SpringLayout.WEST, contenedor); /**************** EOF BOTONES ^^^^^^^^^^^^^^^^^^^^ **/ //Se hace visible la ventana setVisible(true); } public void conectaControlador( Controller c ){ btnAdd.addActionListener(c); btnAdd.setActionCommand("INSERTAR"); btnDel.addActionListener(c); btnDel.setActionCommand("BORRAR"); btnUpd.addActionListener(c); btnUpd.setActionCommand("MODIFICAR"); tabla.addMouseListener(c); //sólo se permite pulsar una fila a la vez. tabla.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } }
Se observa que en el constructor, los objetos que se construyen siguen cierto patrón. A modo de detalle, nos centramos en la construcción del cuadro de texto para el nombre:
//CUADRO DE TEXTO PARA EL NOMBRE txtNombre = new JTextField(); contenedor.add(txtNombre); sp.putConstraint(SpringLayout.NORTH, txtNombre, 10, SpringLayout.NORTH, contenedor); sp.putConstraint(SpringLayout.WEST, txtNombre, 100, SpringLayout.WEST, contenedor); sp.putConstraint(SpringLayout.EAST, txtNombre, 300, SpringLayout.WEST, contenedor);
Por motivos de claridad en el código, se ha omitido la referencia mediante this.
En la primera línea se construye el objeto, definido previamente.
txtNombre = new JTextField();
El objeto creado, se añade al contenedor principal.
contenedor.add(txtNombre);
Mediante el empleo de la función putConstraint del objeto sp (de la clase SpringLayout) se posiciona el objeto txtNombre en el contenedor.
Primero se posiciona el lado norte:
sp.putConstraint(SpringLayout.NORTH, txtNombre, 10, SpringLayout.NORTH, contenedor);
A continuación, el lado oeste:
sp.putConstraint(SpringLayout.WEST, txtNombre, 100, SpringLayout.WEST, contenedor);
y por último, el este:
sp.putConstraint(SpringLayout.EAST, txtNombre, 300, SpringLayout.WEST, contenedor);
Puede resultar curioso que primero se añade al contenedor principal y posteriormente, se posiciona. Se obtendría el mismo resultado si tras la construcción del objeto, se posicionara y por último, se añadiera al contenedor.
En el método conectaControlador, se indica que al pulsar los botones se desea que se realice una acción, que vendrá definida en la clase Controller.
Para que Controller sepa qué botón se ha pulsado, se emplea el método setActionCommand.
- Para el botón añadir, se le asocia el comando INSERTAR
- Para borrar, se le asocia BORRAR
- Para editar, MODIFICAR
Codificación de la clase Controller
La clase Controller se encarga de la comunicación entre la interfaz gráfica y la base de datos, el modelo.
Cuando el usuario interactúe con la Vista, desencadenará la ejecución de ciertos eventos que serán recogidos por el Controlador y lanzados al Modelo. Cuando el modelo devuelva la información, se actualizará la interfaz. Por ese motivo, la clase controlador tendrá un objeto de la clase View.
Puesto que debe codificar eventos de ratón y de acción, la clase implementará dos interfaces de Java, así se simulará la herencia múltiple en Java. Las interfaces que se implementarán serán: ActionListener, MouseListener
Nota: No se debe confundir el concepto de interfaz de Java (se podría considerar como una clase abstracta) con la idea de interfaz gráfica de usuario (cuya creación se ha llevado a cabo en el apartado anterior).
Cuando se realice la implementación de la interfaz ActionListener, se tendrá que sobrescribir obligatoriamente el método actionPerformed.
Al realizar la implementación de la interfaz MouseListener, se tendrán que sobrescribir obligatoriamente varios métodos: mouseClicked, mouseEntered, mouseExited, mousePressed y mouseReleased. En nuestro caso, sólo se añadirá código en el primer método, sin embargo, la declaración del resto de métodos es obligatoria.
actionPerformed
Este método se ejecutará cuando el usuario pulse sobre alguno de los tres botones de la Ventana, tal y como se especificó en el método conectaControlador de la clase View.
En función del comando que se ejecute:
- INSERTAR
- BORRAR
- MODIFICAR
Se llamará a un procedimiento almacenado en la base de datos. Se ha decidido implementar el modelo como procedimientos almacenados en la base de datos.
Para ejecutar los procedimientos almacenados, se empleará la interfaz CallableStatement dentro de java.sql
Se usará el método prepareCall para invocar a los procedimientos almacenados. Dicho procedimiento pertenece a la interfaz Connection. Se obtendrá un objeto de dicha interfaz mediante la invocación al método estático getConexion definido en nuestra clase Bd.
Cada vez que el usuario pulse un botón, se limpiarán los cuadros de texto de la ventana (método limpia) y se volcarán los datos existentes en la tabla de clientes en el objeto JTable creado en la clase View (método cargaTabla).
mouseClicked
Este método se ejecutará cuando el usuario pulse sobre una fila de la tabla.
Cuando se pulse sobre una fila, se invocará a un procedimiento almacenado de la base de datos que obtiene el nombre, apellido y NIF del cliente marcado y cargará los datos en los cuadros de texto de la ventana.
Para obtener la información devuelta por el procedimiento almacenado, se hará uso de la interfaz ResultSet.
El resto de métodos cuya definición es obligatoria al implementar la interfaz MouseListener, no contendrán código alguno.
Código
En el constructor se realizará la vinculación entre el controlador y la ventana. A continuación, se invocará el método cargaTabla, el cual se encarga de añadir la información de la base de datos a la ventana.
Se creará la clase Controller:
- Dentro del proyecto creado, se accede al menú File > New > Class
- En el cuadro de diálogo que aparece, tras escribir el nombre de la clase Controller, se seleccionarán las interfaces a implementar.
- Se debe pulsar sobre el botón Add asociado a Interfaces. En el cuadro de diálogo que aparece, se buscarán y añadirán ActionListener y MouseListener.
- Por último, se pulsa sobre el botón Finish.
El código de la clase será el siguiente.
package kadumMVC; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.sql.CallableStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Vector; public class Controller implements ActionListener, MouseListener { private View view; //CONSTRUCTOR Controller( View view ){ this.view = view; cargarTabla(); } @Override public void actionPerformed(ActionEvent arg0) { //Objeto para ejecutar los procedimientos almacenados // en la base de datos CallableStatement cs; //COMANDO EJECTUADO String comando = arg0.getActionCommand(); //Deberá coincidir con alguno de los parámetros // indicados en setActionCommand invocado en la // clase View switch (comando) { case "INSERTAR": try{ //Preparar la llamada cs = Bd.getConexion().prepareCall( "{CALL insertarCliente(?,?,?)}"); //Indicar qué información se pasa al // procedimiento cs.setString(1, this.view.txtNombre.getText()); cs.setString(2, this.view.txtApellido.getText()); cs.setString(3, this.view.txtNIF.getText()); //Ejecutar el procedimiento cs.execute(); }catch (SQLException e) { System.err.println("Error en la INSERCIÓN"); } break; case "BORRAR": //Recoger qué fila se ha pulsado int filaPulsada = this.view.tabla.getSelectedRow(); //Si se ha pulsado una fila if(filaPulsada>=0){ //Se recoge el id de la fila marcada int identificador = (int)this.view.dtm.getValueAt(filaPulsada, 0); try{ //Preparar la llamada cs = Bd.getConexion().prepareCall( "{CALL borrarCliente(?)}"); //Indicar qué información se pasa al procedimiento cs.setInt(1, identificador); //Ejecutar el procedimiento cs.execute(); //System.out.println(this.view.dtm.getValueAt(filaPulsada, 0)); }catch (SQLException e) { System.err.println("Error en el BORRADO"); } } break; case "MODIFICAR": //Recoger qué fila se ha pulsadao en la tabla filaPulsada = this.view.tabla.getSelectedRow(); //Si se ha pulsado una fila if(filaPulsada>=0){ //Se recoge el id de la fila marcada int identificador = (int)this.view.dtm.getValueAt(filaPulsada, 0); try{ //Preparar la llamada cs = Bd.getConexion().prepareCall( "{CALL modificarCliente(?,?,?,?)}"); //Indicar qué información se pasa al procedimiento cs.setInt(1, identificador); cs.setString(2, this.view.txtNombre.getText()); cs.setString(3, this.view.txtApellido.getText()); cs.setString(4, this.view.txtNIF.getText()); //Ejecutar el procedimiento cs.execute(); //System.out.println(this.view.dtm.getValueAt(filaPulsada, 0)); }catch (SQLException e) { System.err.println("Error en la MODIFICACION"); } } break; default: System.err.println("Comando no definido"); break; } //limpiar el formulario limpia(); //refrescar la tabla cargarTabla(); } //Método para limpiar los campos de la ventana private void limpia(){ this.view.txtNombre.setText(""); this.view.txtApellido.setText(""); this.view.txtNIF.setText(""); } //Método que recarga los datos de la tabla de la base de datos // en la tabla de la clase View protected void cargarTabla(){ //Objeto para ejecutar los procedimientos almacenados en la base de datos CallableStatement cs; //Objeto para recoger los datos devueltos por el procedimiento almacenado ResultSet rs; //Objeto para recorrer el resultado del procedimiento almacenado y // añadirlo a la tabla definida en la clase View Vector<Object> fila; //Limpiar los datos de la tabla for(int i=this.view.dtm.getRowCount(); i>0; i--){ this.view.dtm.removeRow(i-1); } //Cargar datos en la tabla try { //Preparar la llamada cs = Bd.getConexion().prepareCall( "{CALL getClientes()}"); //Ejecutarla y recoger el resultado rs = cs.executeQuery(); //Recorrer el resultado while(rs.next()){ //Añadir registro a registro en el vector fila = new Vector<Object>(); fila.add(rs.getInt("id")); fila.add(rs.getString("nombre")); fila.add(rs.getString("nif")); //Añadir el vector a la tabla de la clase View this.view.dtm.addRow(fila); } } catch (SQLException e) { System.err.println("Error al CARGAR DATOS"); } } @Override public void mouseClicked(MouseEvent arg0) { //Objeto para ejecutar los procedimientos almacenados en la base de datos CallableStatement cs; //Objeto para recoger los datos devueltos por el procedimiento almacenado ResultSet rs; //Recoger qué fila se ha pulsadao en la tabla int filaPulsada = this.view.tabla.getSelectedRow(); //Si se ha pulsado una fila if(filaPulsada>=0){ //Se recoge el id de la fila marcada int identificador= (int)this.view.dtm.getValueAt( filaPulsada, 0); try{ //Preparar la llamada cs = Bd.getConexion().prepareCall( "{CALL getCliente(?)}"); //Indicar qué información se pasa al procedimiento cs.setInt(1, identificador); //Ejecutar el procedimiento rs = cs.executeQuery(); //Cargar los datos devueltos en los cuadros de texto if(rs.next()){ this.view.txtNombre.setText(rs.getString(1)); this.view.txtApellido.setText(rs.getString(2)); this.view.txtNIF.setText(rs.getString(3)); } //System.out.println(this.view.dtm.getValueAt(filaPulsada, 0)); }catch (SQLException e) { System.err.println("Error al CARGAR UN CLIENTE"); } } } @Override public void mouseEntered(MouseEvent arg0) {} @Override public void mouseExited(MouseEvent arg0) {} @Override public void mousePressed(MouseEvent arg0) {} @Override public void mouseReleased(MouseEvent arg0) {} }
Nota: No se ha entrado en detalle en el control de la congruencia de los datos. Por ejemplo, no se verifica que un cliente ya exista en la base de datos antes de insertarlo. Tampoco se controla la letra de su NIF.
Codificación de la clase Principal
Ya se tienen definidas las clases en Java que se usarán; se necesita generar una clase que contenga el procedimiento main que se encargue de construir los objetos necesarios.
En eclipse:
- Dentro del proyecto creado, se accede al menú File > New > Class
- En el cuadro de diálogo que aparece, tras escribir el nombre de la clase Principal, se marca el checkbox correspondiente a public static void main
- Por último, se pulsa sobre el botón Finish.
El código de esta clase es:
package kadumMVC; public class Principal { public static void main(String[] args) { //Invocar al constructor de la clase Bd new Bd("kadummvc"); //Crear un objeto de la clase View View vista = new View(); //Crear un objeto de la clase Controller Controller controlador = new Controller(vista); //Vincular la vista y el controlador vista.conectaControlador(controlador); } }
Como nota, observar que se realiza la vinculación entre la clase Controller y View en dos sentidos.
- En la construcción del objeto controlador, se pasa como parámetro el objeto vista.
- Se invoca a un método de la clase vista, que recibe como parámetro el objeto controlador.
Creación del modelo
El modelo se implementará, no como clases en Java, sino como procedimientos almacenados (stored procedures) en la base de datos MySQL de nuestro proyecto.
- Se crea la base de datos kadummvc. Como se observa, es el mismo nombre que recibe como parámetro de entrada la invocación al constructor Bd en la clase Principal.
- Se crea la tabla cliente. Con los campos id, nombre, apellido y NIF
CREATE TABLE `cliente` ( `id` int(10) NOT NULL AUTO_INCREMENT, `nombre` varchar(50) DEFAULT NULL, `apellido` varchar(50) DEFAULT NULL, `nif` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`) );
- A continuación, se crean los 5 procedimientos almacenados invocados desde la clase Controller:
- borrarCliente. Recibe un entero como parámetro de entrada. Representa el identificador del cliente a eliminar.
- getCliente. Recibe un entero como parámetro de entrada. Representa el identificador del cliente a cargar. Se obtiene el nombre, apellido y NIF de dicho cliente.
- getClientes. No recibe parámetros de entrada. Se obtiene el identificador, nombre y NIF de todos los clientes de la tabla ordenados por nombre.
- insertarCliente. Recibe como parámetros de entrada el nombre, apellido y NIF del cliente a insertar.
- modificarCliente. Recibe como parámetro de entrada el identificador del cliente a modificar, el nuevo nombre, apellido y NIF.
Los códigos de los procedimientos almacenados (stored procs) son:
#BORRAR CLIENTE POR ID DELIMITER $$ DROP PROCEDURE IF EXISTS borrarCliente$$ CREATE PROCEDURE borrarCliente(id INT(10)) BEGIN DELETE FROM cliente WHERE cliente.id = id; END $$ #OBTENER EL NOMBRE, APELLIDO Y NIF DE UN CLIENTE DELIMITER $$ DROP PROCEDURE IF EXISTS getCliente$$ CREATE PROCEDURE getCliente(id INT(10)) BEGIN SELECT nombre, apellido, nif FROM cliente WHERE cliente.id= id; END $$ #OBTENER EL ID, NOMBRE Y NIF DE TODOS LOS CLIENTES DELIMITER $$ DROP PROCEDURE IF EXISTS getClientes$$ CREATE PROCEDURE getClientes() BEGIN SELECT id, nombre, nif FROM cliente ORDER BY nombre; END $$ #INSERTAR UN CLIENTE DELIMITER $$ DROP PROCEDURE IF EXISTS insertarCliente$$ CREATE PROCEDURE insertarCliente(nombre VARCHAR(50), apellido VARCHAR(50), nif VARCHAR(10)) BEGIN INSERT INTO cliente VALUES(NULL,nombre,apellido,nif); END $$ #MODIFICAR CLIENTE POR ID DELIMITER $$ DROP PROCEDURE IF EXISTS modificarCliente$$ CREATE PROCEDURE modificarCliente(id INT(10), nombre VARCHAR(50), apellido VARCHAR(50), nif VARCHAR(10)) BEGIN UPDATE cliente SET cliente.nombre = nombre, cliente.apellido= apellido, cliente.nif = nif WHERE cliente.id= id; END $$
Una vez compilados los códigos en nuestra base de datos, sólo queda cruzar los dedos (:-) y probar el proyecto.
Prueba del proyecto MVC
Ya se tienen creadas las clases en Java, la base de datos en MySQL con su tabla y procedimientos almacenados.
- Se debe acceder en Eclipse a la clase Principal que contiene la función main y ejecutarla, pulsando la tecla F11.
Se mostrará un mensaje en la consola indicando que se ha conectado a la base de datos kadummvc, tal y como se especificó en la creación del objeto bd dentro de la clase Principal.
Y aparecerá la ventana en las coordenadas 100, 100 con un tamaño de 450×300 tal y como se especificó en el constructor de la clase View.
Añadir clientes
- Se rellenan los campos de texto.
- Se pulsa Añadir.
- Se añadirá la información a la base de datos, se limpiarán los campos de texto y se reflejará en la tabla el cliente insertado.
Modificar clientes
- Para modificar un cliente, se pulsa sobre la fila del registro a modificar. De esa forma, se cargan los datos para ese cliente.
- Se reescribe la información correctamente y se pulsa sobre Editar.
- Se realizará la modificación en la base de datos, se limpian los campos de texto y se actualiza la tabla de la ventana.
Borrar clientes
- Para eliminar un cliente que no sea de nuestro cuento, se pulsa sobre la fila del registro a borrar. Se cargarán sus datos en los cuadros de texto.
- Se pulsa sobre Borrar.
- Se eliminará de la base de datos, se limpian los cuadros de texto y se refleja la eliminación en la tabla de la ventana.
Creación de un ejecutable
Una vez comprobada que la aplicación funciona según lo previsto, se lleva a cabo la construcción del fichero ejecutable en Eclipse.
- Acceder a File > Export…
- En el cuadro de diálogo que aparece, desplegar la carpeta Java y marcar Runnable JAR file
- Tras pulsar Next, se mostrará un cuadro de diálogo donde se configura qué clase es la que contiene el método main que interesa ejecutar. Dónde se desea almacenar el ejecutable y la opción de incluir las bibliotecas necesarias en el fichero ejecutable.
- Se pulsa Finish y se obtendrá un fichero .jar ejecutable
Antes de finalizar esta guía básica sobre MVC en Java y MySQL me gustaría, por un lado, agradecer la paciencia de mis alumnos/as del curso de Programación de Aplicaciones Orientadas a Objetos y Administración de Bases de Datos, por la tardanza en finalizar este artículo; y por otro lado, si os ha resultado útil esta guía y deseáis acceder directamente a los archivos que componen la explicación, te pedimos que te hagas seguidor de nuestro Facebook pulsando sobre la parte izquierda (el icono de la F) del siguiente botón:
Gracias por hacerte seguidor, pulsa aquí para acceder a los archivos descargables. Descomprime el archivo con la clave: mvckadum
Código fuente disponible para descargar?
Para acceder al código fuente, debes hacerte seguidor de Kadum desarrollo web en facebook pulsando sobre el botón que aparece al final del artículo.
Si no ves el botón, quizás no tengas una versión adecuada del navegador o, lo que es más probable, emplees extensiones-complementos tipo AdBlockPlus. En este último caso, deberías deshabilitar el complemento para poder ver el botón.
Exelente programa !!
solo tengo una pregunta ?
Como podria hacer un procedimiento para buscar un cliente mediante el id me muestre los datos para posteriormente modificarlo o eliminarlo..
Espero su pronta respusta..
muchas gracias..
Saludos!!
Hola John:
Muchas gracias por tu comentario. La respuesta a tu pregunta es lo suficientemente amplia como para responderte creando un nuevo artículo al respecto. En este enlace puedes ver la ampliación necesaria para filtrar clientes por id.
Espero que te sea de ayuda.
Muchas gracias!!!
Me sirvió bastante y justo ahora me encuentro realizando una aplicación utilizando lo aprendido,nuevamente reitero mis agradecimientos por compartir sus conocimientos.
Espero y sigan publicando contenido de programación.
Saludos!!
¡Muchas gracias por leernos!
Si nos sigues en las redes sociales, cuando publicamos artículos nuevos, los enlazamos. Así podéis estar al día.
Un saludo
Eres un crack hermano muy buen tuto
Muchas gracias Jesús por tu apoyo.
Muy buen trabajo.
Tengo unas preguntas, ¿como trabajaría el proyecto usando Maven ? ¿El ejemplo corre con la GUI en Linux Red Hat?
Hola, RaGuAngel:
Gracias por tu comentario. Lamentablemente no podemos ayudarte a responder tus preguntas porque no hemos trabajado hasta el momento con Maven.
Gracias Laura, de todas maneras estaré al pendiente del blog para seguir aprendiendo y seguire investigando como trabajarlo con MAVEN y cuando le de al clavo, lo comparto en el blog.
Saludos. XD
Hola gracias por compartir el tutorial.
Quiero saber como debo de gestionar varias ventanas con MVC,, por ejemplo si tuviera un menu una ventana principal y quiero que dentro de la misma me muestre diferentes contenidos. Lo debo de hacer en el controlador de esa ventana principal con el actionListener o lo hago en el main de la aplicacion,
Uso frame o panel?
Muchas gracias por su ayuda, si tiene algun codigo o link para ver el ejemplo seria perfecto.
Hola, David:
Muchas gracias por tu valoración positiva de este tutorial.
Hemos estado entretenidos entregando un proyecto de web multi-idioma y siguiendo el Joomla days de Granada. A ver si disfrutamos pronto de este evento en Córdoba 🙂
El caso, que te respondo algo tarde confiando en que la respuesta te sea útil.
Para continuar con la estructura MVC, lo idóneo es llevar a cabo la gestión de los elementos del menú en el Controlador que implementa el ActionListener. Si se añadiera en el main, ya no estariamos haciendo uso de de la metología MVC.
Respecto al uso de frame o panel para mostrar las distintas pantallas.
Se tendrían varias alternativas pero pasan por tener un único JFrame. Si optaras por utilizar varios Jframe, en la barra de tareas del sistema operativo se abrirían diversas ventanas (tantas como JFrame instanciados), por lo que dificultaría la usabilidad. Así pues:
Nos parece muy interesante tu pregunta, por lo que hemos decidido preparar otro tutorial donde se explique cómo utilizar las clases JMenuBar, JMenu y JMenuItem para la generación de un menú integrando distintos JInternalFrame donde mostrar contenidos diferentes.
Si te interesa el tema, publicaremos en la web y en las redes sociales el enlace cuando esté terminado, así que te recomendamos que nos sigas.
https://www.facebook.com/kadumweb/
Un saludo
Hola, buenas días! Excelente ejemplo, muy completo.
Muchas gracias!
Muy completo solo que como soy principiante el java como hago los procedures?
Hola Vanessa:
En este tutorial, los procedures no se realizan en Java, sino que se desarrolla en MySQL.
Dentro del apartado Creación del modelo, descrito en este tutorial se presenta el código fuente para la construcción de los procedimientos almacenados en la base de datos.
Esperemos que te sea de ayuda.