lunes, 29 de diciembre de 2014

Ficheros DAO, delegación de métodos de acceso a persistencia

Hola de nuevo:

Ya conoceréis los ficheros DAO como método de acceso delegado a persistencia. En este blog he desarrollado algunos ejemplos.

Normalmente hemos de ubicar los ficheros DAO en la carpeta "models" de nuestras fuentes. Tampoco sería muy incorrecto que se sitúen en la carpeta "controllers", ya que estos ficheros en realidad son un intermediario entre la capa de negocio y la capa de persistencia.

Lo normal es que en nuestros ficheros DAO coloquemos exclusivamente los métodos de acceso a la información que se halla situada en nuestra capa de datos. Y eso nos lleva a plantear la conveniencia de construir uno o varios interfaces que sea implementado por nuestros ficheros DAO.

Supongamos el siguiente interface:

public interface UsuariosInterface {

    public boolean createUser(Usuarios user);
    public Usuarios readUser(long idUser);
    public boolean updateUser(long idUser, Usuarios user);
    public boolean deleteUser(long idUser);
    public Usuarios identifyUser(String login, String password);

}

Este interface nos va a dar el "molde" necesario para la preparación de métodos de acceso a una base de datos, al objeto de hacer operaciones CRUD (crear, leer, modificar y borrar) en la capa de datos.

El interface se va a asegurar que tendremos que implementar, al menos, los métodos en él definidos. Y eso hará que nuestro código, si alguna vez es modificado, sustituido o reemplazado, tendrá que obligatoriamente contener estos métodos en su código.



Este "corsé", aparentemente incómodo, es sin embargo muy útil. Debido que el desarrollo de nuestra aplicación está apoyada en una serie de métodos, es razonable pensar que si esos métodos desaparecen o son modificados, nuestro código puede verse seriamente afectado.

Si implementamos un interface en cada fichero DAO, nos aseguramos que, ante un cambio de capa de datos que obligue a modificar la capa de persistencia, la aplicación no se verá afectada en el resto de capas. Si apoyamos las operaciones de la capa de negocios en los métodos desarrollados en un fichero DAO debido a la implementación de un interface, conseguiremos que nuestra aplicación sea más robusta y más fácil de modificar.

Anterior tema                                                                                         Siguiente tema

domingo, 28 de diciembre de 2014

Java SE, persistencia en ficheros

Hola jabatos:

Cuando la capa de datos consiste en un almacenamiento en ficheros, entonces emplearemos la java io biblioteca. Esta API incluye las clases necesarias para las labores de entrada y salida relacionadas con ficheros.

Los flujos de entrada y salida de datos los podemos dividir en flujos de datos raw (bytes) o datos de caracteres unicode. Nos centraremos en estos últimos.

Los flujos de caracteres se pueden leer mediante las clases de tipo Reader, y se pueden escribir y/o modificar mediante clases tipo Writer.

Para leer un fichero de manera eficiente podemos utilizar las clases FileReader y BufferedReader; para escritura, podemos utilizar FileWriter y BufferedWriter. La utilización de buffers optimizará los flujos de entrada y salida, por ejemplo, permitiendo leer y/o escribir de línea en línea en lugar de hacerlo de carácter en carácter.

He aquí algo de código que muestra la diferencia en una lectura:

Ejemplo con BufferedReader
try {
   FileReader reader=new FileReader("file.txt");
   BufferedReader buff=new BufferedReader(reader);
   try {
      String lineRead=buff.readLine();
      while (lineRead!=null) {
         // lo que corresponda hacer con la lectura de línea
      }
   } catch (IOException ex1) {
   } finally {
      buff.close();
   }
} catch (IOException ex2) {
}

Ejemplo sin BufferedReader
try {
   FileReader reader=new FileReader("file.txt");
   try {
      int charRead=reader.read();
      while (charRead!=-1) {
         // lo que corresponda hacer con el carácter leído
      }
   } catch (IOException ex1) {
   } finally {
      reader.close();
   }
} catch (IOException ex2) {
}

La diferencia de utilizar buffer y de no hacerlo es, no solo la optimización de los procesos de E/S, sino un mejor manejo de la información. Sin el buffer, tendremos que comprobar la longitud de las líneas, y procesar los caracteres uno a uno hasta encontrar un fin de línea. Ni es óptima la lectura ni es óptimo el manejo. En el caso de escritura/reescritura, las ventajas del buffer son incluso mayores.

Los objetos StringReader y StringWriter nos sirven para crear objetos String con la misma metodología con las que crearíamos objetos FileReader o FileWriter. Por lo tanto hablaríamos de leer/escribir en memoria en lugar de hacerlo en ficheros.

Normalmente, cuando operamos con ficheros hemos de asegurarnos de:
  • Comprobar que el fichero existe.
  • Es legible o reescribible, o ejecutable, etc., o sea, manipulable.
  • Capturar las excepciones que pudieran producirse.
  • Trabajar con buffers, que optimizan las operaciones I/O.
Si quisiéramos borrar un fichero, podemos utilizar:

try {   
   Files.delete(path+file);
} catch (NoSuchFileException x) {
} catch (DirectoryNotEmptyException x) {
} catch (IOException x) {
}

Podemos comprobar si existe un fichero mediante el método exists():

File fichero=new File("fichero.txt");
if (!fichero.exists()) {
  try {
fichero.createNewFile();
 } catch (IOException e) {
 }
}

En este ejemplo, si el fichero "fichero.txt" no existe procede a crearlo.


Los objetos persistidos solo crean campos con los datos que se almacenan. Los métodos y constructores no serán persistidos. Si algún objeto no es serializable, debe marcarse como "transient" para evitar que se provoque una excepción al persistirlo.

Podemos serializar objetos mediante las clases FileOutputStream y ObjectOutputStream.writeObject(). La lectura usa las clases FileInputStream y ObjectInputStream.readObject(). En el caso de la lectura hay que hacer un casting, porque se recibe un objeto de clase object.

En el ejemplo JavaSE: aplicación MVC - III se puede ver un caso práctico del manejo de ficheros de caracteres según lo expuesto en este post.

Anterior tema                                                                                         Siguiente tema

miércoles, 24 de diciembre de 2014

Persistencia: Implementación DAO - práctica II


Hola Jabatos, seguimos...

Como hemos visto en el ejemplo anterior, hemos creado una aplicación mediante el patrón de arquitectura MVC. En esa aplicación Java SE la capa de datos era una base de datos mysql, y la capa de persistencia era JPA. Además, para separar de forma eficiente la capa persistencia de la capa negocios, implementábamos un fichero DAO.

Como continuación a esa práctica, y para estudiar la utilidad del DAO, vamos cambiar la capa de datos. Ahora la capa de datos consistirá en un fichero de texto, que llamaremos "userdata.txt".

La inmediata consecuencia de este cambio en la base de datos es que deberemos realizar cambios en nuestra capa de persistencia (el model de la aplicación). La correcta implementación efectuada en la aplicación, con separación de funciones, evitará tener que modificar más ficheros que aquellos que corresponden a la capa persistencia:
  • EjemploBeanDAO, que es el DAO.
  • Userdata, que es el model.

Transformación del entity JPA en simple pojo

Modificaremos los ficheros models, (anteriormente llamados entities) para convertirlos en ficheros pojo normales. El cambio consiste simplemente en quitarles las anotaciones que los convertían en ficheros entity:

package models;

public class Userdata implements Serializable {

private static final long serialVersionUID = 1L;

private String id;
private String email;
private String login;
private String name;
private String password;

// AÑADIR GETTERS Y SETTERS
}

Como habéis observado, el trabajo ha sido mínimo. Alguno podrá preguntarse ¿para qué mantener objetos Userdata si vamos a grabar simplemente texto? Pues porque trabajamos con OOP (programación orientada a objetos) además de para mantener la coherencia del proyecto, ya que es más fácil transformar en el DAO el objeto a texto y viceversa, que acometer las modificaciones pertinentes en diversos puntos de la aplicación.

Modificación del fichero DAO

Realizaremos la modificación de los todos métodos afectados por el cambio de la implementación de capa de datos. Aquí las modificaciones son profundas, ya que hay que transformar los métodos por completo.

La clave consiste en que los métodos deben mantener los mismos nombres y argumentos, así como el objeto de retorno. Es por ello que hemos utilizado un interface, para obligar a implementar esos métodos. De esta manera no se afectan las otras capas, y por lo tanto se evita trabajar de más y la posibilidad de cometer errores.

El fichero EjemploBeanDAO, que realizará la lectura de los datos contenidos en fichero txt será el siguiente:

public class EjemploBeanDAO implements EjemploBeanInterface {

public synchronized Userdata getUserData(String login, String password) {

Userdata newUser=null;
FileReader fr=null;
BufferedReader bf=null;
String leeUser;
Scanner sc=null;

try {

fr=new FileReader(new File("userdata.txt"));
bf=new BufferedReader(fr);

while ((leeUser=bf.readLine())!=null) {
// leemos la linea entera y descompondremos en campos
sc=new Scanner(leeUser);
sc.useDelimiter("\\|");

// leemos cada campo
String id=sc.next();
String log=sc.next();
String pas=sc.next();
String name=sc.next();
String email=sc.next();

if (login.equals(log) && password.equals(pas)) {
newUser=new Userdata();
newUser.setId(Long.parseLong(id));
newUser.setLogin(log);
newUser.setPassword(pas);
newUser.setName(name);
newUser.setEmail(email);
break;
}
}
} catch (IOException e) {
System.err.println("Error leyendo el fichero");
return null;
} finally {
try {
bf.close();
fr.close();
} catch (IOException e) {
// error cerrando el buffer y el fichero
System.err.println("Error de buffer o cierre fichero");
}
}
return newUser;
} // fin del metodo
}

Creación del fichero de texto

Con el block de notas, o cualquier otro programa semejante, debéis proceder a crear el fichero llamado "userdata.txt", y guardais la siguiente información:

1|pepeuser|123456|Jose user user|pepeuser@gmail.com|

Es decir, la misma información que teníamos en la base de datos, la tenemos ahora en un fichero de texto. Poned atención en el separador. He remarcado en verde la línea de código donde se declara el separador de los campos del fichero. Podéis cambiarlo, siempre y cuando (lógicamente) lo hagáis también en el fichero.

Proceded a ubicar el fichero de texto dentro de la raíz de la aplicación:



Ejecutando la aplicación...

Ejecutamos la aplicación, y nuevamente deberá mostrar esta información:

DATOS DEL USUARIO:
NOMBRE: Jose user user
EMAIL: pepeuser@gmail.com


Conclusiones

Mucho trabajo para un resultado tan poco espectacular... pero supongamos... imaginad en términos escalares. Las modificaciones en el DAO están localizadas y pueden acometerse con rápidez y precisión. Por el contrario, sin el DAO, en una aplicación con múltiples ficheros, habría que localizar, modificar y testear en diversos puntos, multiplicando el trabajo y la posibilidad de errores.

Pienso que por eficacia, sencillez, y coherencia con el patrón MVC, es importante utilizar ficheros DAO. No son imprescindibles, pero ayudan mucho para una eficiente separación de capas.

 Anterior tema

lunes, 22 de diciembre de 2014

Persistencia: Implementación DAO - práctica I

Hola jabatos:

Vamos a realizar un pequeño ejemplo de aplicación JavaSE implementando MVC, utilizando un fichero DAO para separar de manera eficiente la capa de persistencia de la capa de negocio.



Construiremos una sencilla aplicación que consiste en realizar una consulta a una base de datos: suministraremos un login y un password, y se nos retornará el nombre y el email del usuario o un mensaje en caso de no existir el usuario buscado.

El objetivo es mostrar la utilidad de un DAO como intermediario entre persistencia y negocio. Para ello, inicialmente construiremos un proyecto cuya capa de datos será una base de datos mysql. Probaremos y comprobaremos que funciona una consulta a mysql. A continuación, cambiaremos del proyecto la capa de datos, que pasará a ser un fichero .txt que leeremos mediante java.io.*

Definamos inicialmente el objeto userdata:

OBJETO USERDATA

  •  long id, no puede ser nulo o vacío.
  •  String login hasta 15 caracteres, no puede ser nulo o vacío.
  •  String password hasta 15 caracteres, no puede ser nulo o vacío.
  •  String name hasta 100 caracteres, no puede ser nulo o vacío.
  •  String email hasta 100 caracteres, no puede ser nulo o vacío.
Datos para crear la base mysql

Estas son las instrucciones sql para crear el schema, la tabla, e insertar un usuario en mysql:

  • CREATE SCHEMA `dbPruebas` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
  • CREATE  TABLE `dbPruebas`.`userdata` ( `id` BIGINT NOT NULL AUTO_INCREMENT ,  `login` VARCHAR(15) NOT NULL ,  `password` VARCHAR(15) NOT NULL ,  `name` VARCHAR(100) NOT NULL , `email` VARCHAR(100) NOT NULL ,  PRIMARY KEY (`id`) );
  • INSERT INTO `dbPruebas`.`userdata` (`login`, `password`, `name`, `email`) VALUES ('pepeuser', '123456', 'Jose user user', 'pepeuser@gmail.com');

Después de ejecutar estas sentencias mysql, tendremos una base de datos llamada "dbPruebas", habremos creado la tabla "userdata" que replica un objeto userdata como el definido anteriormente; además habremos insertado un objeto userdata, es decir, un usuario llamado "Jose user user", con el login "pepeuser", etc.

Construcción del proyecto en java

El proyecto se llamará EjemploDAO. Tendrá 3 paquetes, controls, models y views, para implementar el modelo MVC. Ubicaremos en ellos los ficheros correspondientes.

De acuerdo con el gráfico superior, y según las especificaciones de nuestro proyecto, tendremos que construir 4 ficheros: la vista (que llamaremos, como el proyecto, EjemploDAO), el managedBean (EjemploBean), el fichero DAO (EjemploBeanDAO) y el entity (Userdata).

Crearemos un proyecto que se llame EjemploDAO. El proyecto será un proyecto java SE. A continuación, crearemos los 3 java package: "controls", "models", "views".

Creación de la vista

Dentro del paquete views ubicaremos nuestro fichero main, el cual, a efectos de simplificar el ejemplo, hará las funciones de la vista mediante el objeto System.out.

Generaremos una consulta al javaBean que controle nuestra aplicación en el mismo fichero de acceso a la aplicación (el cual llamaremos EjemploDAO.java) . Después, imprimiremos el mensaje recibido desde el javaBean.

package views;

public class EjemploDAO {

private static EjemploDAO ejemplo;

public static void main(String[] args) {

String mensaje;
ejemplo=new EjemploDAO();
mensaje=ejemplo.executeQuery("pepeuser","123456");
System.out.println(mensaje);
}

private String executeQuery(String login, String password) {

String respuesta="";
EjemploBean eBean=new EjemploBean();
respuesta=eBean.getUser(login,password);
return respuesta;
}
}

Así pues, nuestra vista consistirá simplemente en un mensaje con el resultado de la consulta.

Creación del javaBean de control

El javaBean de control que llamaremos EjemploBean, será creado en el directorio controls. El objetivo es implementar la capa de negocio de nuestra aplicación.

El javaBean realizará las comprobaciones pertinentes, delegará en el DAO la gestión de la capa de persistencia, y devolverá la consulta.  El sistema de acceso delegado incrementa un poco la complejidad pero a cambio mejora la independencia entre las 3 capas de nuestro proyecto.

package controls;

public class EjemploBean {

public EjemploBean() {
}

public String getUser(String login, String password) {

if (login==null || login.isEmpty()) { return "USUARIO ERRÓNEO O NULO"; } if (password==null || password.isEmpty()) { return "PASSWORD ERRÓNEO O NULO"; }

String respuesta;
Userdata user=new Userdata();
EjemploBeanDAO dao=new EjemploBeanDAO();

user=dao.getUserData(login,password);

if (user!=null) {
respuesta="DATOS DEL USUARIO:\n"+"NOMBRE: "+user.getName()+"\n"+"EMAIL: "+user.getEmail();
} else {
respuesta="NO HAY NINGÚN USUARIO CON ESE LOGIN-PASSWORD";
}
return respuesta;
}
}

Creación del entity JPA

Dentro del package models, creamos un entity JPA, que básicamente consiste en añadir las correspondientes anotaciones a un fichero pojo normal.

package models;

@Entity
@Table(name="userdata")
public class Userdata implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private String id;
@Column(name = "email", nullable = false)
private String email;
@Column(name = "login", nullable = false)
private String login;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "password", nullable = false)
private String password;

// AÑADIR LOS GETTERS Y SETTERS
}

Creación del fichero persistence.xml

El fichero persistence.xml es el fichero que se encarga de configurar las conexión y transacciones de datos con la capa de datos, a través de la creación de un EntityManagerFactory:

<?xml version="1.0" encoding="UTF-8"?><persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 
<persistence-unit name="ejemploDAO" transaction-type="RESOURCE_LOCAL">  
<class>models.Userdata</class> <properties>  
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbPruebas"/>  
<property name="javax.persistence.jdbc.user" value="root(poner aquí vuestro mysql user)"/>  
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>  
<property name="javax.persistence.jdbc.password" value="xxxxxx (poner aquí vuestro mysql password)"/>  
</properties> 
</persistence-unit>
</persistence>

Creación del interface EjemploBeanInterface

Construiremos un interface para obligar a implementar los métodos que consideremos necesarios. En este caso concreto, solo será un método:

public interface EjemploBeanInterface {

public Userdata getUserData(String login, String password);

}

Creación del DAO EjemploBeanDAO

Finalmente solo nos queda un último paso en la construcción de nuestra aplicación: la creación del fichero DAO.

Antes de definir el DAO, vamos a recordar el sentido de su creación. El DAO va a intermediar entre la capa de persistencia y la de negocio, ayudándonos a aislar las capas, y a facilitarnos los cambios en la arquitectura de nuestro proyecto.

Este es nuestro EjemploBeanDAO.java :

package models;

public class EjemploBeanDAO implements EjemploBeanInterface {

public EjemploBeanDAO() {

}

public Userdata getUserData(String login, String password) {

Userdata user;
 // este no es el metodo más eficiente de crear un EntityManagerFactory y gestionar la factory
EntityManagerFactory emf;
emf=Persistence.createEntityManagerFactory("ejemploDAO");
        
        EntityManager em=null;
        EntityTransaction tx=null;
        Query q=null;
        
        em=emf.createEntityManager();
        tx=em.getTransaction();
        tx.begin();
             
        try {
        user = (Userdata)em.createQuery("SELECT c FROM Userdata c WHERE c.login LIKE :custLogin AND c.password LIKE :custPass").setParameter("custLogin", login).setParameter("custPass", password).getSingleResult();
            tx.commit();
        } catch (NoResultException nr) {
            tx.rollback();
            System.err.println("No existen datos");
            nr.printStackTrace();
            return null;
        } catch (Exception e) {
            tx.rollback();
            System.err.println("Se ha producido un error durante la busqueda");
            e.printStackTrace();
            return null;  
        } finally {
            try {
                em.close();
            } catch (Exception fl) {
                // do nothing
                System.err.println("Se ha producido un error cerrando database");
            }
        }
        return user;
}
}



Estructura del proyecto y ficheros

Para que funcione JPA, será necesario añadir a las librerías del proyecto EclipseLink y mysql-connector.java. Una vez agregadas, así quedará la estructura final:




















Ejecutando la aplicación...

Ejecutamos la aplicación y el resultado es :

DATOS DEL USUARIO:
NOMBRE: Jose user user
EMAIL: pepeuser@gmail.com

Éxito total ... ;-)


Ahora cambiaremos la capa de datos, alterando el proyecto, y así comprobaremos la utilidad del DAO como intermediario entre capa de persistencia y capa de negocios.

                                                                                                                     Siguiente tema


viernes, 19 de diciembre de 2014

Ejemplo: JavaSE, aplicación MVC - X

Hola jabatos:

He aquí la conclusión ( ¡¡ por fin !! ) de este ejemplo. Solo nos quedan retoques...

Primeramente en la clase MainWindow, hemos de finalizar el método changeMainPanel, para que nos permita cambiar de panel de componente entre PanelCreacion(), PanelIdentificacion() y TabbedPrincipal().

public JFrame changeMainPanel(JComponent newComponent, JPanel title) {
this.north=title;
this.center= newComponent;
window=makeWindows(this.width,this.height,EjemploSwing.getEnter().getMainFrame().getY(), EjemploSwing.getEnter().getMainFrame().getX(), this.title, this.north, this.east, this.west, newComponent,this.south);
return window;
}

La línea que hemos incorporado hace básicamente lo siguiente: recupera el tamaño de la pantalla que habíamos definido al crearla, toma las ubicaciones actuales (y,x) donde se halla colocada la pantalla, y añadimos los componentes, tanto los anteriores (los que no cambian) como los recién suministrados,al objeto de construir una nueva pantalla JFrame y asignársela al componente window.

Hay que tener en cuenta que window en realidad es un objeto instanciado en EnterSandMan, y que éste a su vez es un componente static instanciado por EjemploSwing. Lo cual nos lleva a que el objeto window es un componente static por derivación y es por ello que "recuerda" su título y los componentes que le suministramos cuando fue creado.


Siguiente cosa, cambios en el panelCreación:
Teníamos pendiente de implementar el cambio de pantalla. Debemos incluir dentro del actionPerformed la invocación del método siguiente:
if (source.equals("Volver")) {
// volvemos a la pantalla de identificacion
EjemploSwing.getEnter().showIdentificationApp();
}

Y también en:
if (userDAO.createUser(usuario)) {
JOptionPane.showMessageDialog(null, "Usuario grabado correctamente", "Grabación de usuarios", JOptionPane.INFORMATION_MESSAGE);
// volvemos a la pantalla de identificacion
EjemploSwing.getEnter().showIdentificationApp();
} else {
JOptionPane.showMessageDialog(null, "Error grabando el usuario", "Grabación de usuarios", JOptionPane.ERROR_MESSAGE);
}

La explicación de la instrucción incluida es la siguiente: Hay que invocar el JFrame "actual", y eso se consigue desde la clase que lo creó "EjemploSwing". Recordad que en esa clase construimos un método getEnter() que nos proporciona acceso al JFrame, y a través de él a sus métodos y propiedades ;-). Hay otras formas, pero yo prefiero ésta...


Y sigamos por el panelIdentificacion:
Teníamos pendiente de implementar el cambio de pantalla. Debemos incluir dentro del actionPerformed:
  // crear nuevo usuario if (source.equals("Nuevo")) { EjemploSwing.getEnter().createUserApp(); }

Y también en:
    if (activeUser!=null) { // usuario identificado, pasamos el control al main EjemploSwing.getEnter().showTabbedPane(activeUser); } else { JOptionPane.showMessageDialog(null, "No existe el usuario/password suministrado", "Identificación de usuarios", JOptionPane.ERROR_MESSAGE); }

El proceso es semejante al caso anterior, pero aquí nos encontramos con dos posibilidades. Si queremos crear un usuario se emplearía el createUserApp(). Y si hemos procedido a la identificación, entonces hay que invocar el método showTabbedPane() con el parámetro del objeto Usuario conteniendo la información a mostrar.

Ya casi hemos terminado. Solamente nos quedaría (S.E.u.O.) un pequeño retoque. Tenemos varios JOptionPane en las clases PanelCreacion(), PanelIdentificacion(), PanelPersonal() y PanelProfesional(); esos JOptionPane están referenciados al objeto null, o sea, que salen en mitad de la pantalla de nuestro equipo, independientemente de donde tengamos ubicada la aplicación. Para evitar ese inadecuado comportamiento, hay que cambiar todos los
null por ==> EnterSandMan.getMainFrame()
lo cual habilita para que salgan centradas las pantallas con mensajes en nuestro JFrame principal.

Ya está. Creo... bueno, el aspecto de nuestro proyecto es el siguiente:




Fin de proyecto. Ejecutado, probado y funciona... el users.dat contendrá los datos de los usuarios que vayamos creando, en formato txt.

Gracias por vuestra paciencia...

Anterior tema                                                                                         

jueves, 18 de diciembre de 2014

Ejemplo: JavaSE, aplicación MVC - IX

Hola jabatos:

Hoy vamos a crear la clase principal de nuestra capa de negocios, "EnterSandMan". Estará ubicada, lógicamente, dentro del package "controls".

Esta clase EnterSandMan requiere explicaciones. La lógica de negocio consiste más o menos en:
  1. En la clase de entrada creamos un hilo. Y luego instanciamos la clase main (EnterSandMan) para dar inicio a la aplicación.
  2. Se crea un JFrame static. Ese JFrame será único para cada instanciación de la aplicación; lo que cambiará serán los contenidos y la información requerida o mostrada.
  3. La información se mostrará en el panel central del JFrame. Y es este panel central el que se cambiará, sustituyendo el componente que hubiera por el nuevo a incorporar. También se cambiará el título que está ubicado en el panel superior.
  4. El flujo sería: ( PanelIdentificacion <==> PanelCreacion ) ==> TabbedPrincipal 
  5. Se implementará un método para cambiar los paneles superior y central, según el punto anterior. Para cada panel se implementará un método, que deberá ser invocado desde clases distintas a EnterSandMan.
  6. También daremos a los JOptionPane que hay en las clases de la capa vista, un componente de referencia para que salgan bien centrados: el propio JFrame. Así evitaremos el molesto problema de que los mensajes salgan descentrados de la pantalla de la aplicación.
Y todo ello se consigue con este código:

public class EnterSandMan {

// ventana y paneles principales
private static JFrame mainFrame;
private JPanel mainPanel1;
private MainWindow window;
// objeto Usuarios
private Usuarios user;

public EnterSandMan() {
// CONSTRUCTOR
}

/**
* Este método inicializa la aplicación. Muestra la pantalla de identificación de usuarios.
*/
public void initApp() {
//instancia el panel principal responsable de la identificacion
PanelIdentificacion panel=new PanelIdentificacion();
mainPanel1=panel.makePanelIdentificacion();
// instancia el fabricador de la ventana principal
window=new MainWindow();
// crea el JFrame principal, con el constructor makeWindows
mainFrame=window.makeWindows(600,400,100,100,"ejemplo Swing versión 1.0",createTitlePanel(" IDENTIFICACIÓN "), createBorderPanel(), createBorderPanel(), mainPanel1, createBottomPanel());
// hace visible la ventana JFrame
mainFrame.setVisible(true);

}

/**
* Este método sustituye la vista de la pantalla de identificación
* por la vista de la pantalla de creación de nuevos usuarios
*/
public void createUserApp() {
mainFrame.setVisible(false);
//instancia el panel responsable de creacion usuarios
PanelCreacion panel=new PanelCreacion();
mainPanel1=panel.makePanelCreacion();
mainFrame=window.changeMainPanel(mainPanel1,createTitlePanel(" ALTA USUARIO "));
mainFrame.pack();
mainFrame.revalidate();
mainFrame.setVisible(true);
}

/**
* Este método sustituye la pantalla de creación de nuevos usuarios
* por la vista de la pantalla de identificación
*/
public void showIdentificationApp() {
mainFrame.setVisible(false);
//instancia el panel principal responsable de la identificacion
PanelIdentificacion panel=new PanelIdentificacion();
mainPanel1=panel.makePanelIdentificacion();
mainFrame=window.changeMainPanel(mainPanel1,createTitlePanel(" IDENTIFICACIÓN "));
mainFrame.pack();
mainFrame.revalidate();
mainFrame.setVisible(true);
}

/**
* Este método muestra la pantalla de información del usuario, mediante
* un menú de dos pestañas. 
* @param user - Objeto Usuarios con los datos del usuario.
*/
public void showTabbedPane(Usuarios user) {
this.user=user;
mainFrame.setVisible(false);
//instancia el panel principal responsable de mostrar info
TabbedPrincipal panel=new TabbedPrincipal();
mainFrame=window.changeMainPanel(panel.getDataPanel(this.user),createTitlePanel(" DATOS USUARIO "));
mainFrame.pack();
mainFrame.revalidate();
mainFrame.setVisible(true);
}

public static JFrame getMainFrame() {
// obtener el JFrame principal para mensajes
return mainFrame;
}

/*  ZONA DE PANELES PRIVADOS */

/**
* Este método crea el  panel del titulo
* @return JPanel
*/
private JPanel createTitlePanel(String title) {
// Creamos el titulo en un JLabel
JLabel lab=new JLabel(title);
lab.setFont(new Font("Arial",Font.BOLD,20));
lab.setAlignmentX(JLabel.CENTER_ALIGNMENT);
// creamos el JPanel del titulo, y lo rellenamos con labels vacios
// para el mejor ajuste del diseño
JPanel pan=new JPanel();
pan.setLayout(new BoxLayout(pan,BoxLayout.Y_AXIS));
pan.add(new JLabel(" "));
pan.add(lab);
pan.add(new JLabel(" "));
pan.add(new JLabel(" "));
pan.setVisible(true);
return pan;
}

/**
* Este método crea los paneles laterales
* @return JPanel
*/
private JPanel createBorderPanel() {
// Creamos el JPanel, y le añadimos un elementos JLabel relleno con los
// espacios necesarios para realizar el ajuste marginal necesario
JPanel pan=new JPanel();
pan.add(new JLabel("                                  "));
pan.setVisible(true);
return pan;
}

/**
* Este método crea el panel bottom
* @return JPanel
*/
private JPanel createBottomPanel() {
// Creamos el pie en un JLabel
JLabel lab=new JLabel(" Aplicación creada por musef2904@gmail.com ");
lab.setFont(new Font("Arial",Font.BOLD,12));
lab.setAlignmentX(JLabel.CENTER_ALIGNMENT);
// Creamos el JPanel, y le añadimos el elemento JLabel con el
// pie a mostrar
JPanel pan=new JPanel();
pan.setLayout(new BoxLayout(pan,BoxLayout.Y_AXIS));
pan.add(new JLabel(" "));
pan.add(new JLabel(" "));
pan.add(lab);
pan.add(new JLabel(" "));
pan.setVisible(true);
return pan;
}

} // ********** END OF CLASS


  • Método initApp(). Solamente será invocado una vez, desde la clase de entrada EjemploSwing. Crea el JFrame y lo llena con el JPanel de identificación
  • Método createUserApp().  Se invoca desde el JPanel de identificación a través de changeMainPanel(). Construye la estructura para la creación del User.
  • Método showIdentificacionApp(). Se invoca desde el JPanel de creación a través de changeMainPanel(). Reconstruye nuevamente el panel de Identificación.
  • Método showTabbedPane(). Se invoca desde JPanel de identificación a través de changeMainPanel(). Construye la estructura de pestañas con la información del usuario, una vez que éste se ha identificado correctamente.
  • Método getMainFrame(). Retorna el JFrame, al objeto de que puedan utilizar sus métodos las clases del package "views".
  • El resto de métodos privados de EnterSandMan se utilizan para la creación del resto de los componentes que se incluyen dentro del BorderLayout. Sirven para construir el aspecto de la ventana de la aplicación.
Ya solamente nos quedan los últimos retoques...

Anterior tema                                                                                         Siguiente tema

miércoles, 17 de diciembre de 2014

Ejemplo: JavaSE, aplicación MVC - VIII

Hola Jabatos:

Por fin, la última clase de la capa vista: la que muestra los datos personales dentro del tabbedPane.

public class PanelPersonal implements ActionListener {

// paneles auxiliares
private JPanel r0;
private JPanel r1;
private JPanel r2;
private JPanel r3;
private JPanel r4;
// componentes del formulario
private JTextField nameField;
private JTextField userField;
private JTextField passField;
private JTextField emailField;
// backgrounds del formulario
private final Color BACKGR0=Color.WHITE;
private final Color BACKGR1=Color.RED;
private final Font FONT1=new Font("Arial",Font.BOLD,16);
// objeto usuario
private Usuarios usuario;

public PanelPersonal() {
// CONSTRUCTOR
}

/**
* Este método fabrica un JPanel con los datos profesionales del usuario.
* @param user - Objeto Usuarios, con los datos del usuario.
* @return - JPanel
*/
public JPanel makePanel(Usuarios user) {

usuario=user;
JPanel panel=new JPanel();
panel.setLayout(new GridLayout(5,1));
// los componentes van colocados en tres paneles auxiliares
r0=new JPanel();
r1=new JPanel();
r2=new JPanel();
r3=new JPanel();
r4=new JPanel();

// creamos los componentes del panel 0
r0.setLayout(new GridLayout(1,2));
JLabel name=new JLabel(" Nombre");
name.setFont(FONT1);
nameField=new JTextField(usuario.getNombre());
nameField.setBackground(BACKGR0);
nameField.setToolTipText("Entre 3 y 20 caracteres");
// añadimos al panel con labels de margenes
r0.add(name);
r0.add(nameField);

// creamos los componentes del panel 1
r1.setLayout(new GridLayout(1,2));
JLabel login=new JLabel(" Usuario");
login.setFont(FONT1);
userField=new JTextField(usuario.getLogin());
userField.setBackground(BACKGR0);
userField.setToolTipText("Entre 6 y 20 caracteres");
// añadimos al panel con labels de margenes
r1.add(login);
r1.add(userField);

// creamos los componentes del panel 2
r2.setLayout(new GridLayout(1,2));
JLabel pass=new JLabel(" Contraseña");
pass.setFont(FONT1);
passField=new JTextField(usuario.getPassword());
passField.setBackground(BACKGR0);
passField.setToolTipText("Entre 6 y 20 caracteres");
// añadimos al panel con labels de margenes
r2.add(pass);
r2.add(passField);

// creamos los componentes del panel 3
r3.setLayout(new GridLayout(1,2));
JLabel email=new JLabel(" Email");
email.setFont(FONT1);
emailField=new JTextField(usuario.getEmail());
emailField.setBackground(BACKGR0);
emailField.setToolTipText("Entre 6 y 30 caracteres");
// añadimos al panel con labels de margenes
r3.add(email);
r3.add(emailField);

// creamos los componentes del panel 4
JButton button1=new JButton("Modificar");
button1.setToolTipText("Pulse para modificar el usuario");
JButton button2=new JButton("Borrar");
button2.setToolTipText("Pulse para eliminar el usuario");
JButton button3=new JButton("Salir");
button3.setToolTipText("Pulse para salir de la aplicación");
// añadimos al panel
r4.add(button1);
r4.add(button2);
r4.add(button3);

// activamos los listeners de los botones
button1.addActionListener(this);
button2.addActionListener(this);
button3.addActionListener(this);

// finalmente, componemos el JPanel para return
// añadiendo los paneles auxiliares y JLabels para separacion
panel.add(r0);
panel.add(r1);
panel.add(r2);
panel.add(r3);
panel.add(r4);

panel.setBorder(BorderFactory.createRaisedBevelBorder());
panel.setVisible(true);
return panel;
}

/**
* Este método realiza una simple comprobación de los datos del formulario
* @return boolean
*/
private boolean checkForm() {

// inicializamos por comprobacion de formulario
boolean result=true;
nameField.setBackground(BACKGR0);
userField.setBackground(BACKGR0);
passField.setBackground(BACKGR0);
emailField.setBackground(BACKGR0);

if (nameField.getText().trim().length()<3 || nameField.getText().trim().length()>20) {
nameField.setBackground(BACKGR1);
result=false;
}
if (userField.getText().trim().length()<6 || userField.getText().trim().length()>20) {
userField.setBackground(BACKGR1);
result=false;
}

if (passField.getText().trim().length()<6 || passField.getText().trim().length()>20) {
passField.setBackground(BACKGR1);
result=false;
}
if (emailField.getText().trim().length()<6 || emailField.getText().trim().length()>30) {
emailField.setBackground(BACKGR1);
result=false;
}
return result;
}

// Implementacion de las acciones de los JButtons
@Override
public void actionPerformed(ActionEvent e) {

String source=e.getActionCommand();

if (source.equals("Modificar")) {
// Modifica usuario en la aplicacion
if (checkForm()) {
UsuariosDAO userDAO=new UsuariosDAO();
usuario.setNombre(nameField.getText().trim());
usuario.setLogin(userField.getText().trim());
usuario.setPassword(passField.getText().trim());
usuario.setEmail(emailField.getText().trim());
if (userDAO.changeUser(usuario)) {
JOptionPane.showMessageDialog(null, "Usuario modificado correctamente", "Modificación de usuarios", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "Error modificando el usuario", "Modificación de usuarios", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(null, "Error en el formulario", "Modificación de usuarios", JOptionPane.ERROR_MESSAGE);
}
}

// salir de la aplicacion
if (source.equals("Borrar")) {
// borrar completamente el usuario
if (JOptionPane.showConfirmDialog(null, "¿Seguro que desea ELIMINAR el usuario?\nLos datos NO PODRAN SER RECUPERADOS", "Eliminar el usuario", JOptionPane.YES_NO_OPTION)==0) {
UsuariosDAO userDAO=new UsuariosDAO();
if (userDAO.deleteUser(usuario.getId())) {
JOptionPane.showMessageDialog(null, "Usuario eliminado correctamente\nAhora la aplicación se cerrará", "Eliminación de usuarios", JOptionPane.INFORMATION_MESSAGE);
System.exit(0);
} else {
JOptionPane.showMessageDialog(null, "Error eliminando el usuario", "Eliminación de usuarios", JOptionPane.ERROR_MESSAGE);
}
}
}

// salir de la aplicacion
if (source.equals("Salir")) {
// salir
if (JOptionPane.showConfirmDialog(null, "¿Seguro que desea abandonar la aplicación?", "Salir de la aplicación", JOptionPane.YES_NO_OPTION)==0) {
System.exit(0);
}
}
}
} // *************** END OF CLASS

El panel queda de esta manera, una vez dentro del JFrame --> JTabbedPane:



Como siempre, algunos detalles sobre el código:

Esta clase tiene también tiene 3 métodos, al igual que la anterior: el makePanel() que fabricador del panel; el checkForm() habitual; y el actionPerformed() para el tema de acciones de botones.

Notas:
  • makePanel confecciona el panel y muestra la información del usuario. Nada cambia.
  • La información se puede modificar. Al modificar, primero chequea el formulario, crea un objeto Usuario y lo graba con el método changeUser() del DAO. Muestra mensajes mediante JOptionPane con el resultado de la operación. Esto es lo mismo que en el panel datos profesionales.
  • Se puede borrar el usuario completamente. Al usar un JOptionPane de confirmación, nos aseguramos de que no hay errores pidiendo conformidad. Una vez dada, simplemente usamos el método deleteUser() del DAO, y se saca al usuario del sistema.
  • Igualmente que en otras partes, la salida se realiza "controlada" cuando se pulsa el botón salir.
Ya hemos terminado tanto con las clases del paquete views como con las clases del paquete models. Bueno, la verdad es que aún nos quedan algunos retoques en el paquete views porque nos han quedado algunas cosillas que implementar.

Finalmente, nos queda crear la clase principal, la clase que realmente implementa el "negocio" de nuestra aplicación, y que llamaremos con el nombre tan sugerente de "EnterSandMan".


Anterior tema                                                                                         Siguiente tema