Duda sobre como implementar MVC ..El Controlador
Hola amigos.
luego de leer los fantásticos tópicos que hay aquí,como invitado,me anime a preguntar,ya que veo una gran experiencia en la comunidad. Quisiera salir de la ignorancia sobre como se utiliza el patrón MVC cuando se trabaja con Java,específicamente aplicaciones de escritorio.
He visto bastantes videotutoriales en Youtube sobre como se utiliza MVC en Java,pero la duda que me queda es como usar el controlador para "conectar" el modelo y la parte gráfica(solo he usado MVC en PHP).
Tengo tres paquetes:modelo,vista y controlador. el modelo lo he subdividido en dos paquetes uno para la parte de la persistencia(modelo.dao) y otro donde solo tengo las entidades con sus métodos accesores(modelo.vo). La distribución de las clases en los paquetes me queda asi
modelo.vo:Estudiante
modelo.dao:EstudianteDao
controlador:De momento sin ninguna clase
vista:AlumnoVentana
Ademas de una clase conexion,para crear la conexión al gestor de base de datos y poder realizar las consultas a través de las clases DAO.
Este es el código de cada clase
Conexion.Java
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package modelo;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
*
* @author ASUS
*/
public class Conexion {
private final String usuario="root";
private final String password="CXT982mw";
private final String url="jdbc:mysql://localhost:3306/bd";
private Connection con;
public Conexion() {
try {
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager.getConnection(url,usuario,password);
System.out.println("Conexion realizada con exito!");
}
catch(ClassNotFoundException e){
System.out.println("No se entro el Driver: "+e);
}
catch(SQLException e){
System.out.println("No se pudo realizar la conexion: "+e);
}
catch (Exception e) {
System.out.println("Error: "+e);
}
}
//Devuelve la cadena de conexion
public Connection getConexion(){
System.out.println("Enviando conexion");
return con;
}
//Cerrar la conexion a la BD
public void cerrarConexion(){
try {
if(con!=null){
con.close();
System.out.println("Conexion cerrada con exito!");
}
else{
System.out.println("La conexion no existe.");
}
} catch (Exception e) {
System.out.println("No se pudo cerrar la conexion");
}
}
}
EstudianteDao.java
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package modelo.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import modelo.Conexion;
import modelo.vo.Estudiante;
/**
*
* @author ASUS
*/
public class EstudianteDao {
private Conexion conexion=new Conexion();
/**
* Insertar un Estudiante en la BD
* @param est
*/
public void insertar(Estudiante est){
//obteto del tipo Connection para hacer ejecutar las querys a la BD.
Connection con=conexion.getConexion();
String sql="INSERT INTO expedientealumno(carnet,codcarrera,nombres,apellido1,apellido2,apellcasad,edad,nui)"
+ " VALUES(?,?,?,?,?,?,?,?)";
try {
PreparedStatement pst=con.prepareStatement(sql);
pst.setString(1, est.getCarnet());
pst.setString(2, est.getCodigoCarrera());
pst.setString(3, est.getNombres());
pst.setString(4, est.getPrimerApellido());
pst.setString(5, est.getSegundoApellido());
pst.setString(6, est.getApellidoCasada());
pst.setInt(7, est.getEdad());
pst.setString(8, est.getNui());
if (pst.executeUpdate()>0) {
System.out.println("registro insertado con exito");
}
} catch (SQLException e) {
System.out.println("No se pudo insertar el registro: "+e);
}
catch(Exception e){
System.out.println("Ocurrido el siguiente error: "+e);
}
finally{
conexion.cerrarConexion();
}
}
}
Estudiante
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package modelo.vo;
/**
*
* @author ASUS
*/
public class Estudiante {
private String carnet;
private String codigoCarrera;
private String nombres;
private String primerApellido;
private String SegundoApellido;
private String apellidoCasada;
private int edad;
private String nui;
/**
* Constructor por defecto
*/
public Estudiante() {
}
/**
* Constructor con parametros estudiante.
* @param carnet
* @param codigoCarrera
* @param nombres
* @param primerApellido
* @param SegundoApellido
* @param apellidoCasada
* @param edad
* @param nui
*/
public Estudiante(String carnet, String codigoCarrera, String nombres, String primerApellido, String SegundoApellido, String apellidoCasada, int edad, String nui) {
this.carnet = carnet;
this.codigoCarrera = codigoCarrera;
this.nombres = nombres;
this.primerApellido = primerApellido;
this.SegundoApellido = SegundoApellido;
this.apellidoCasada = apellidoCasada;
this.edad = edad;
this.nui = nui;
}
//Inicio metodos accesores
public String getCarnet() {
return carnet;
}
public void setCarnet(String carnet) {
this.carnet = carnet;
}
public String getCodigoCarrera() {
return codigoCarrera;
}
public void setCodigoCarrera(String codigoCarrera) {
this.codigoCarrera = codigoCarrera;
}
public String getNombres() {
return nombres;
}
public void setNombres(String nombres) {
this.nombres = nombres;
}
public String getPrimerApellido() {
return primerApellido;
}
public void setPrimerApellido(String primerApellido) {
this.primerApellido = primerApellido;
}
public String getSegundoApellido() {
return SegundoApellido;
}
public void setSegundoApellido(String SegundoApellido) {
this.SegundoApellido = SegundoApellido;
}
public String getApellidoCasada() {
return apellidoCasada;
}
public void setApellidoCasada(String apellidoCasada) {
this.apellidoCasada = apellidoCasada;
}
public int getEdad() {
return edad;
}
public void setEdad(int edad) {
this.edad = edad;
}
public String getNui() {
return nui;
}
public void setNui(String nui) {
this.nui = nui;
}
//Fin metodos accesores
}
AlumnoVentana.java
De esta clase no coloco el codigo por que es el que me genera netbeans,pero allí no he colocado ningún otro código,lo único que hice(como vi en los vídeos de youtube) fue dejar todos los campos y botones como publico..aun que no se si es correcto.
he probado cada clase pro separado y funciona...Pero en el controlador es donde tengo la gran interrogante. como conecto la vista y el modelo? es a través del constructor? que debería enviar de la vista y el modelo? en algunos vídeos lo hacen mas o menos asi:
}
pero en otros vídeos que vi crean otra clase,que contiene el metodo main, alli instancian el modelo,la vista y el controlador . De lo que he programado con MVC, tengo un poco de experiencia en PHP, y cuando lo hacia era en el controlador que creaba la instancia del modelo (dao y vo) y una vez obtenia el resultado ese lo enviaba a la vista,la cual trabajaba con un sistema de plantilla.
Pero en Java me pierdo :( y ya que estoy aprendiendo quisiera hacerlo de la mejor forma posible, les agradecería de mucho su orientaciona l respecto.
- Inicie sesión o regístrese para enviar comentarios
Implementar MVC
Tengo ya un tiempo utilizando esto: MVC con Java 8 (FXML para javaFX , HikariCP (pool de conexiones) y JDBC) y me ha funcionado de maravilla.
Tu clase de conexión se me hace mal implementada porque aunque utilizas un método cerrarConexion() no es la manera que se recomienda hacer esto. Utiliza mejor esta manera:
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = // Retrieve connection
stmt = conn.prepareStatement(// Some SQL);
rs = stmt.executeQuery();
} catch(Exception e) {
// Error Handling
} finally {
try { if (rs != null) rs.close(); } catch (Exception e) {};
try { if (stmt != null) stmt.close(); } catch (Exception e) {};
try { if (conn != null) conn.close(); } catch (Exception e) {};
}
Si te das cuenta, hay que cerrar TODOS los recursos ....(Resultset, Statement y Connection)
Aunque si ya estas utilizando Java 8 es mejor usar try with resources (Que esta disponible desde Java 7) de esta manera:
try (PreparedStatement pstmt = connection.preparedStatement("some query")) {
pstmt.setString ...
pstmt.setInt ...
try (ResultSet resultSet = pstmt.executeQuery())) {
// Do stuff with the result set.
}
}
}
Como ves el código resultante es mucho mas corto, elegante y fácil de entender (Ademas que los recursos son liberados en forma automática)
En mi caso (que por cierto, no utilizo MySQL sino que Firebird), haría lo siguiente:
1.- Creo una clase de conexión que utiliza el patrón singleton (me asegura que solo se cree un objeto de esta clase y desde aquí el pool de conexiones se encarga de proporcionar una conexión cada que se requiere).
2.- Crear una clase EstudianteService (solicito una conexión y manejo las transacciones (para que en caso de querer hacer una inserción en varias tables y ocurra un error no queden registros huérfanos, es decir, o todo se inserta o actualiza o hago un rollback a la transacion ) ), y paso dicha conexión a mi clase Mapper (que explico a continuación)
3.-Crear una clase EstudianteMapper (donde ejecuto la(s) queries PreparedStatements y Resultsets ... y enlazo el resultado a mi dominio (o pojo Estudiante).
Notas:
a) EstudianteMapper propaga el error (en caso que lo hubiera) a la clase superior que la llama, es decir EstudianteService y de esta manera centralizo el manejo de errores en una solo clase (tengo un método estático en la clase Connecction).
b) Utilizo variables y listas observables (Java 8) para "pintar" el resultado en la Vista, en conjunto con:
CompletableFuture.supplyAsync(Supplier<T>).thenAccept(Consumer<U>);
para que la vista quede responsiva, es decir, esto se ejecuta en diferentes Hilos ..Bueno pues creo que es todo, espero que te ayude en algo ... saludos ...
Gracias por ayudarme a
Gracias por ayudarme a mejorar el codigo,voy a mejorar esa parte,de la clase de conexion,y lo volvere a postear.
Desviándome un poco del tema inicial,cuando hablas de singleton,hasta donde habia leido no lo recomendaban cuando se trataba de hacer conexiones a la bd,y esto lo digo sin conocimiento de causa,pero por otro lado donde lei eso recomendaba usar pools de conexiones. Específicamente me refiero a este post. Es mas recomendable entonces utilizar singleton+pool de conexiones,aun que se cierren las conexiones de la bd y se liberen los recursos?
De lo que me has aconsejado,aun tengo muucho que leer jeje,pero me parece muy interesante como se hace a un nivel Pro.
si yo hago esto en una clase controlador,lo tome de ejemplo en un codigo que estoy analizando:
formulario.setTitle("Agregar Persona - TrucosJenJen");
//posicion del formulario en la pantalla
formulario.setLocationRelativeTo(null);
formulario.setVisible(true);
//addActionListeners para los botones
this.formulario.btnAgregarImagen.setActionCommand("btnAgregarImagen");
this.formulario.btnAgregarImagen.addActionListener(this);
this.formulario.btnConsultar.setActionCommand("btnConsultar");
this.formulario.btnConsultar.addActionListener(this);
this.formulario.btnGuardar.setActionCommand("btnGuardar");
this.formulario.btnGuardar.addActionListener(this);
//Aqui se agregan todos los componentes que va a interactuar
//por ejemplo botones, tablas, jlist, etc.
}
this.formulario = formulario;
this.modelo=modelo;
}
</code
Estaria bien hacerlo,desde el punto de vista MVC?
Respuesta ...
Es mas recomendable entonces utilizar singleton+pool de conexiones,aun que se cierren las conexiones de la bd y se liberen los recursos?
En realidad cuando estás utilizando un "pool" de conexiones la conexión no se cierra en forma física, sino que al "cerrar" esta se retorna al "pool". Acuérdate que al crear un "pool" tu decides cuantas conexiones vas a tener abiertas (dicho en otras palabras: cuando vas a utilizar una conexión solamente se la pides prestada al "pool" y al desocuparla se la regresas ).
Por otro lado, no comentas que biblioteca gráfica estás utilizando (por mi parte yo estoy utilizando JavaFX). Si me das un mes (ahorita ando muy ocupado), voy a subir una "turorial" para poner un ejemplo práctico, ¿te parece? (Pienso utilizar JavaFX, JDBC y Firebird y el patrón MVC, por supuesto).
Por mi parte, creo muy importante primero aprender los conceptos y después aplicarlos en nuestro desarrollo (lo digo desde mi muy humilde punto de opinión, sin haber cursado una carrera de programación, en realidad me recibí de Ingeniero en Electrónica y Comunicaciones).
Saludos,
Gerardo Suárez Trejo
Controlador
Básicamente el patrón MVC clasifica a los componentes de una aplicación en: modelo (los datos de la aplicación), vista (la presentación visual de los datos y controlador (manejo de entrada de datos, actualización del modelo, lógica de la aplicación y producir salidas).
Hablando particularmente de un controlador para la pantalla que compartes en tu publicación, ésta clase Java tendría que hacer los siguiente:
- Manejo de entradas de datos: recibir como entrada los datos del estudiante a registrar.
- Actualización del modelo: registrar los datos del estudiante en la base de datos.
- Lógica de la aplicación: También llamada lógica de negocio, en esta parte no veo nada particular que hacer, ya que el controlador sólo se encargar de actualizar el modelo de datos. Lo que tendrías que hacer en esta parte sería manejar las excepciones que se puedan presentar al guardar los datos. Te sugiero leer sobre el manejo de excepciones.
- Producir salidas: en éste caso no aplica cómo tal. Simplemente si la llamada al controlador no produce alguna excepción significa que todo salió bien. ¿En que caso un controlador produce una salida? Cuando se realiza una operación de consulta, por ejemplo, obtener la lista de alumnos que se llamen Juan.
Ya tienes definido el modelo (Estudiante y EstudianteDAO), no estoy seguro si el DAO forme parte del modelo, porque ese patrón no representa los datos de la aplicación sino que es un componente que permite manejar las operaciones DE datos.
La vista es el formulario, supongo que es un panel o frame de Swing.
Los componentes MVC no deben estar atados a una librería o API en particular, es decir que un cambio en el uso de alguna de éstas haga que los componentes dejen de funcionar o se tenga que invertir mucho tiempo en hacer las adecuaciones correspondientes. Por ejemplo, el código que sugieres para crear el controlador:
this.formulario = formulario;
this.modelo=modelo;
}
En éste caso estarías atando el controlador a un formulario del tipo Form, si llegaras a cambiar de Swing a JavaFX, muy probablemente el controlador ya no funcionaría.
En tu caso lo ideal seria crear una instancia del controlador dentro de la clase del formulario. El controlador debe tener un método que reciba los datos de la vista (del estudiante) y éste debe ser llamado en el componente correspondiente, los datos los puedes enviar por separado o, idealmente, hay que encapsularlos en una clase, en tu caso, en una clase del modelo: Estudiante.
private EstudianteController estudianteController;
public Formulario() {
initComponents();
estudianteController = new EstudianteController();
}
//Mucho codigo por aqui
private void jButtonNuevoActionPerformed(java.awt.event.ActionEvent evt){
Estudiante estudiante = new Estudiante():
estudiante.setNombres(jTextFieldNombres.getText());
//Getters faltantes
estudianteController.guardar(estudiante);
}
El controlador por su parte necesita tener una instancia del DAO que se usa para guardar los datos.
private EstudianteDAO estudianteDAO;
public EstudianteControlador(){
estudianteDAO = new EstudianteDAO();
}
public void guardar(Estudiante estudiante){
try{
estudianteDAO.insertar(estudiante);
} catch (Exception e ){
//TODO manejar excepcion
}
}
}
De esta manera si cambias de Swing a JavaFX solo tendrías que hacer adecuaciones en la vista y no en el controlador.
Obviamente hay muchos mas detalles en cuanto a la creación de la conexión con la base de datos, manejo de excepciones, etc.
Tal vez me extendí mucho pero no encontré otra forma de resumirlo. El código no esta probado ni se debe usar así como está, es meramente ilustrativo.
Gracias
Me parece Excelente la propuesta de un tutorial.
Perdon,creo que omiti colocar que estaba haciendo la app con Java Swing.
De MVC,he tenido un primer acercamiento,solo que en otro lenguaje PHP. Pero donde entro en conflicto es por el hecho que en frameworks como Laravel,para enviar la informacion del modelo desde el controlador hacia la vista se hace algo asi:
Pero en el caso de las aplicaciones de escritorio que funcionan con eventos es donde me deja con un gran signo de interrogación la parte del controlador.Que como bien dices mejor aprender bien desde un inicio para no tener malas practicas a futuro.
Y,gracias por explicar lo del pool de conexiones,me queda mas claro :)
Gracias cuauhpilli
cuauhpilli.
Extensa explicación pero,muy detallada,justo como me gusta para entender una idea :) .Me lo voy a leer nuevamente el post,pero me ha sido de gran ayuda la parte teórica. Voy a hacer los cambios que me dices haber como me va :)
Duda MVC
Estaba leyendo sobre MVC y observe tu comentario, no pude resistirme a crear una cuenta para poder preguntarte @cuauhpilli , yo manejo el concepto de MVC muy similar a como lo explicas, a excepcion de unos puntos en especifico, me gustaria saber si como lo hago esta bien estructurado o no, buscando como dice el compañero dueño del post, hacerlo de la mejor forma (de la forma mas profesional posible).
Yo en mi Clase Controlador uso metodos estaticos:
ClienteDAO clienteDAO = new ClienteDAO();
int resultado = clienteDAO.ingresarCliente(cliente);
return resultado;
}
PD: Se que el post es un poco antiguo pero guardo la esperanza de que me respondas :), no se que tan viable sea poder enviarte una parte del proyecto que he realizado donde se pueda ver mejor la estructura y uso de las Clases.