sábado, 13 de diciembre de 2014

Ejemplo: JavaSE, aplicación MVC - III

Hola jabatos:

Vamos a implementar la capa de persistencia de nuestra aplicación "EjemploSwing".

La capa de datos será, en este caso, persistida en ficheros de texto. No obstante, la implementación de la capa de persistencia deberá abstraer a nuestra aplicación de la capa de datos totalmente, según el patrón de arquitectura MVC.

Para ello, vamos a utilizar dos clases java. La primera será un fichero pojo que será el model del objeto a persistir. Este model (entity en JPA) será manejado por nuestra aplicación en las distintas capas de la aplicación. La segunda clase que vamos a manejar será un fichero DAO. El fichero DAO es una clase que sirve para abstraer totalmente a la capa de negocio de la capa de persistencia. En este fichero se hallarán las operaciones CRUD (Create, Read, Update y Delete) que se realicen sobre capa de datos, pero adaptadas al tipo de capa. En nuestro caso, ficheros de texto.

Y sin más dilación, ahí va el modelo. La clase se llama Usuarios.java y por supuesto irá alojado en el package models:

public class Usuarios {

private long id;
private String nombre;
private String login;
private String password;
private String email;
private String sector;
private String profesion;
private int antiguedad;

public Usuarios() {

}

// AÑADIR LOS GETTERS AND SETTERS

}

No hay mucho que explicar aquí. La clase Usuarios tiene los atributos descritos en la definición de proyecto, más el id con el cual se identifica a cada objeto Usuarios grabado. Se accede a ellos mediante getter/setters (no incluidos por temas de espacio) públicos.

Y ahora, el fichero DAO. Se llamará UsuariosDAO.java, y también va alojado en el package models:

public class UsuariosDAO {

private final File FICHERO_USERS=new File("./users.dat");
private final File FICHERO_TEMP=new File("./data.temp");
private final char SEPARATOR='|';

/**
* El metodo constructor crea, si no existe, el ficheros de usuario. 
*/
public UsuariosDAO() {
// comprueba si el fichero users existe
if (!FICHERO_USERS.exists()) {
// si no existe el fichero de users, trata de crearlo
try {
FICHERO_USERS.createNewFile();
} catch (IOException e) {
// si hay error
e.printStackTrace();
}
}


/**
* Este metodo sirve para identificar a un usuario determinado.
* @param login - login del usuario a recuperar la informacion.
* @param password - password del usuario a recuperar la informacion.
* @return Devuelve como parametros objeto Usuario con los datos del usuario o null si errores. 
*/
public Usuarios identifyUsuario (String login, String password) {

// no comprobamos el idUser!!

String datosUsuario[]=new String[8];
Usuarios usuario=null;
FileReader fr=null;
BufferedReader bf=null;

try {
fr=new FileReader(FICHERO_USERS);
bf=new BufferedReader(fr);
String leeUser;
while ((leeUser=bf.readLine())!=null) {
// leemos la linea entera, aunque las dos primeras
// lecturas no valen
Scanner sc=null;
try {
sc=new Scanner(leeUser);
// asignamos el separador
sc.useDelimiter("\\"+SEPARATOR);
datosUsuario[0]=sc.next(); //id
datosUsuario[1]=sc.next(); //nombre
datosUsuario[2]=sc.next(); //login
datosUsuario[3]=sc.next(); // password
if (!(datosUsuario[2].equals(login) && datosUsuario[3].equals(password))) {
// siguiente lectura
continue;
} else {
// usuario localizado
// rellena el string[] con los datos del usuario
int n=4;
while (sc.hasNext()) {
datosUsuario[n]=sc.next();
n++;
}
usuario=new Usuarios();
break;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
sc.close();
}

} catch (IOException e) {
System.err.println("Error leyendo el fichero de usuarios");
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");
}
}

// construimos el objeto Usuarios a retornar
if (usuario!=null) {
try {
usuario.setId(Long.parseLong(datosUsuario[0]));
} catch (NumberFormatException nf) {
usuario.setId(Long.parseLong("0"));
}
usuario.setNombre(datosUsuario[1]);
usuario.setLogin(datosUsuario[2]);
usuario.setPassword(datosUsuario[3]);
usuario.setEmail(datosUsuario[4]);
usuario.setSector(datosUsuario[5]);
usuario.setProfesion(datosUsuario[6]);
try {
usuario.setAntiguedad(Integer.parseInt(datosUsuario[7]));
} catch (NumberFormatException nf) {
usuario.setAntiguedad(Integer.parseInt("1900"));
}
}
return usuario;


/**
* Este metodo crea un usuario nuevo, grabando los datos correspondientes en el fichero de usuarios.
* @param usuario - Objeto Usuarios, conteniendo el usuario a grabar. 
* @return boolean con el resultado de la operación.
*/
public synchronized boolean createUser(Usuarios usuario) {

// no hacemos comprobaciones sobre los datos a grabar !!

// buscamos el ultimo id y lo incrementamos
long idUser=getLastId();
idUser++;
// fabrica el String a grabar
String graba2 = String.valueOf(idUser)+SEPARATOR+usuario.getNombre()+SEPARATOR+
usuario.getLogin()+SEPARATOR+usuario.getPassword()+SEPARATOR+usuario.getEmail()+SEPARATOR
+usuario.getSector()+SEPARATOR+usuario.getProfesion()+SEPARATOR+String.valueOf(usuario.getAntiguedad())
+SEPARATOR+System.lineSeparator();

// graba el usuario y contraseña en fichero
FileWriter fichero=null;
BufferedWriter salida=null;
// ahora genera el fichero Users en modo append
try {
fichero=new FileWriter(FICHERO_USERS,true);
salida=new BufferedWriter(fichero);
salida.write(graba2);
} catch (IOException e) {
System.err.println("Error en grabación fichero de usuarios");
e.printStackTrace();
return false;
} finally {
try {
salida.flush();
salida.close();
fichero.close();
} catch (IOException e) {
System.err.println("error cerrando el fichero");
}
}
return true;


/**
* Este metodo sirve para recuperar los datos relativos a un usuario determinado. 
* @param idUser - Id del usuario a recuperar la informacion.
* @return Devuelve como parametros objeto Usuario con los datos del usuario o null si errores. 
*/
public Usuarios getUsuario (long idUser) {

// no comprobamos el idUser!!

String idUsuario=String.valueOf(idUser);
String datosUsuario[]=new String[8];
FileReader fr=null;
BufferedReader bf=null;

try {
fr=new FileReader(FICHERO_USERS);
bf=new BufferedReader(fr);
String leeUser;
while ((leeUser=bf.readLine())!=null) {
// leemos la linea entera
Scanner sc=null;
try {
sc=new Scanner(leeUser);
// asignamos el separador
sc.useDelimiter("\\"+SEPARATOR);
// lee el primer dato
datosUsuario[0]=sc.next();
if (!datosUsuario[0].equals(idUsuario)) {
// siguiente lectura
continue;
} else {
// usuario localizado
// rellena el string[] con los datos del usuario
int n=1;
while (sc.hasNext()) {
datosUsuario[n]=sc.next();
n++;
}
break;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
sc.close();
}
} // fin del while
} catch (IOException e) {
System.err.println("Error leyendo el fichero de usuarios");
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");
}
}

// construimos el objeto Usuarios a retornar
Usuarios usuario=new Usuarios();
try {
usuario.setId(Long.parseLong(datosUsuario[0]));
} catch (NumberFormatException nf) {
usuario.setId(Long.parseLong("0"));
}
usuario.setNombre(datosUsuario[1]);
usuario.setLogin(datosUsuario[2]);
usuario.setPassword(datosUsuario[3]);
usuario.setEmail(datosUsuario[4]);
usuario.setSector(datosUsuario[5]);
usuario.setProfesion(datosUsuario[6]);
try {
usuario.setAntiguedad(Integer.parseInt(datosUsuario[7]));
} catch (NumberFormatException nf) {
usuario.setAntiguedad(Integer.parseInt("1900"));
}
return usuario;


/**
* Este método modifica los datos del usuario grabados en ddbb, sustituyéndolos
* por los datos del objeto suministrado. 
* @param usuario - Objeto Usuarios, correspondiente a los nuevos datos a grabar.
* @return boolean con el resultado de la operación.
*/
public synchronized boolean changeUser(Usuarios usuario) {

// no se comprueba el objeto Usuarios !!

String idSearched=String.valueOf(usuario.getId());
FileReader fr=null;
BufferedReader bf=null;
FileWriter fw=null;
BufferedWriter bw=null;
try {
// asignacion de readers y buffers
fr=new FileReader(FICHERO_USERS);
bf=new BufferedReader(fr);
fw=new FileWriter(FICHERO_TEMP);
bw=new BufferedWriter(fw);
String leeUser;
// dato a comparar
String iden;
// cadena a montar
String modifiedUser=null;
Scanner sc=null;

while ((leeUser=bf.readLine())!=null) {
try {
// leemos la linea entera 
sc=new Scanner(leeUser);
sc.useDelimiter("\\"+SEPARATOR);
// ID del registro grabado
iden=sc.next();
if (!idSearched.equals(iden)) {
// Esta linea no es la que hay que modificar
// por lo tanto, la grabamos sin cambios en el fichero temporal
bw.write(leeUser+System.lineSeparator());
// leemos otra linea
continue;
} else {
// este es el dato del usuario a modificar
// montamos el string que grabaremos como nuevo registro
modifiedUser=iden+SEPARATOR+usuario.getNombre()+SEPARATOR+usuario.getLogin()
+SEPARATOR+usuario.getPassword()+SEPARATOR+usuario.getEmail()+SEPARATOR+usuario.getSector()
+SEPARATOR+usuario.getProfesion()+SEPARATOR+String.valueOf(usuario.getAntiguedad())
+SEPARATOR+System.lineSeparator();
bw.write(modifiedUser);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sc.close();
}
} // fin del while
} catch (IOException e) {
System.err.println("Error leyendo el fichero de usuarios");
return false;
} finally {
try {
// cerramos los ficheros utilizados
bw.flush();
bw.close();
fw.close();
bf.close();
fr.close();
} catch (IOException e) {
// error cerrando el buffer y el fichero
System.err.println("Error de buffer o cierre fichero");
}

}
// una vez finalizado el proceso de lectura, modificacion y escritura
// procedemos a borrar el fichero original y cambiarlo por el duplicado
try {
FICHERO_USERS.delete();
} catch (Exception e) {
e.printStackTrace();
}
// y ahora renombramos el fichero temporal
FICHERO_TEMP.renameTo(FICHERO_USERS);
return true;
}

/**
* Este método elimina del fichero de usuarios al usuario con el id del parámetro suministrado 
* @param usuario - long, con el id del usuario a eliminar
* @return boolean con el resultado de la operación.
*/
public synchronized boolean deleteUser(long idToDelete) {

// no se comprueba el long recibido !!

String idSearched=String.valueOf(idToDelete);
FileReader fr=null;
BufferedReader bf=null;
FileWriter fw=null;
BufferedWriter bw=null;
try {
// asignacion de readers y buffers
fr=new FileReader(FICHERO_USERS);
bf=new BufferedReader(fr);
fw=new FileWriter(FICHERO_TEMP);
bw=new BufferedWriter(fw);
// linea entera de datos
String leeUser;
// dato a comparar
String iden;
Scanner sc=null;

while ((leeUser=bf.readLine())!=null) {
try {
// leemos la linea entera 
sc=new Scanner(leeUser);
sc.useDelimiter("\\"+SEPARATOR);
// ID del registro grabado
iden=sc.next();
if (!idSearched.equals(iden)) {
// Esta linea no es la que hay que borrar
// por lo tanto, la grabamos sin cambios en el fichero temporal
bw.write(leeUser+System.lineSeparator());
} else {
// este es el dato del usuario a borrar
// simplemente no grabamos ese usuario en el fichero temporal
continue;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sc.close();
}
} // fin del while
} catch (IOException e) {
System.err.println("Error leyendo el fichero de usuarios");
return false;
} finally {
try {
// cerramos los ficheros utilizados
bw.flush();
bw.close();
fw.close();
bf.close();
fr.close();
} catch (IOException e) {
// error cerrando el buffer y el fichero
System.err.println("Error de buffer o cierre fichero");
}
}
// una vez finalizado el proceso de lectura, modificacion y escritura
// procedemos a borrar el fichero original y sustituirlo por el duplicado
try {
FICHERO_USERS.delete();
} catch (Exception e) {
e.printStackTrace();
}
// y ahora renombramos el fichero temporal
FICHERO_TEMP.renameTo(FICHERO_USERS);

return true;
}

/**
* Este metodo retorna el último id grabado en la ddbb 
* @return long, con el último id grabado o -1 si hay error.
*/
private long getLastId () {

long idToReturn=0;
FileReader fr=null;
BufferedReader bf=null;

try {
fr=new FileReader(FICHERO_USERS);
bf=new BufferedReader(fr);
String leeUser;

while ((leeUser=bf.readLine())!=null) {
// leemos la linea entera
Scanner sc=null;
try {
sc=new Scanner(leeUser);
// asignamos el separador
sc.useDelimiter("\\"+SEPARATOR);
// lee el primer dato, comprueba si es mayor que el
// anterior y lo sustituye si procede
String idUsuario=sc.next();
try {
long idRead=Long.parseLong(idUsuario);
if (idRead>idToReturn) {
idToReturn=idRead;
}
continue;
} catch (NumberFormatException nf) {
continue;
}
} catch (Exception e) {
e.printStackTrace();
return -1;
} finally {
sc.close();
}
} // fin del while
} catch (IOException e) {
System.err.println("Error leyendo el fichero de usuarios");
return -1;
} 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 idToReturn;
}

} // *****  END OF CLASS


Vale, lo reconozco... muchas líneas. ;-)

Esta clase viene con sus javadocs y además comentarios, por lo que creo que hay poco que comentar. No obstante:
  • En el constructor comprobamos si el fichero existe, y en caso contrario lo crea. Lo cual protege de un FileNotFoundException. Eso si, lo crea en blanco...
  • Como los ficheros son de texto, uso la clase Scanner. Mediante la clase Scanner leo una línea entera (que corresponde con un registro grabado), y luego obtengo los campos mediante useDelimiters (dice cual es el separador de campos) y next() que nos da el contenido entre los Delimiters.
  • Los métodos reciben y devuelven clases. Primero porque Java es OOP (programación orientada a objetos) y luego porque es más MVC implementable.
  • Podríamos haber construido un interface que hubiera sido implementado por UsuariosDAO. De hecho, hubiera sido lo coherente. No obstante, no he querido complicar aún más el ejemplo.
  • No hacemos comprobaciones de los parámetros de entrada, por la misma razón de no querer complicar aún más el ejemplo. Además, quizás eso sea un acicate para implementar los test JUnit en otro tutorial ;-)

Bueno, nuestro proyecto tiene ahora esta estructura.























Seguiremos con las vistas en el siguiente post....

Anterior tema                                                                                         Siguiente tema

No hay comentarios:

Publicar un comentario