De persistencia, enums y varargs en Java

A pesar del tiempo que ha pasado desde la salida de la versión 1.5 de Java, muchas personas siguen sin comprender ni utilizar los enum y los varargs, quizás por falta de interés al no encontrarles ninguna utilidad. Espero haber demostrado la utilidad de los primeros con mis entradas anteriores al respecto y, más aun con la siguiente entrada, donde utilizare enum en su forma más básica y además explicare la utilidad de los varargs.

A este respecto, vamos a centrarnos en entities o entidades EJB. Vamos a crear una entidad con persistencia, la cual hereda una serie de métodos para validar, antes de persistir estas entidades, que están definidas correctamente. Es obvio, que este tipo de verificaciones debería hacerlas una capa superior como un DAO, pero cuando queremos definir un EJB específico para tareas de persistencia, este tipo de verificaciones no está de más ya que no sabemos cómo va a ser utilizado el EJB una vez lo hayamos desplegado. Por otra parte, veremos cómo hacer que el EJB se comporte de distintas maneras, según lo deseemos.

Vamos a definir una entidad llamada "MonoUser" la cual contiene los datos de un usuario de la aplicación:



package es.monocaffe.persistence

...

@Entity
public class MonoUser extends ValidEntity implements Serializable{
@Id
private int id;

private String name;
private String lastName;
private String address;
private String phone;
private String email;
private String idDoc;

@Transient
protected Validation validation;

//Implementamos dos contructores
public MonoUser(){
super();
//Definimos un nivel de validación por defecto
this.validation = Validation.ESTRICT;
}

//Este constructor, lo utilizaremos para especificar el nivel de validación
public MonoUser(Validation validation){
super();
this.validation = validation;

//Implementación de getters/setters
...

@Override
public boolean isValid() throws IncorrectFormatException{
validateMonoUser(this);
return true;
}

}


Como veis, es una entidad sencilla, que simplemente, hereda de ValidEntity una clase abstracta con el método abstracto isValid el cual debemos implementar en nuestras entidades. Además, hemos utilizado el enum Validation para controlar el nivel de validación, con esto controlamos el comportamiento de nuestras entities. Será esto lo que utilizaremos para cambiar cuánto y qué deseamos validar de los datos de MonoUser.La clase ValidEntity es algo así:


package es.monocaffe.persistence

...

public abstract class ValidEntity{
public abstract boolean isValid() throws IncorrectFormatException;

//Ahora definimos los métodos que realizarán la validación

public static void validateMonoUser(MonoUser user, Validation validation) throws IncorrectFormatException{
switch(validation){
// Algo a lo que nunca me acostumbrare es que lo siguiente, no compila:
// case Validation.ESTRICT:
// case Validation.NONE:
// case Validation.LAZY:
// Esto nos tirará una excepción de como ésta:
// The enum constant Validation.ESTRICT reference cannot be qualified in a case label
// Vamos a hacerlo bien :)

case ESTRICT:
validateIdDoc(user.getIdDoc);
validateStrings(new String[] {user.getName(), user.getLastName(), user.getAddress()});
validatePhone(user.getPhone());
validateEmail(user.getEmail());

case LAZY:
validateEmail(user.getEmail());
case NONE:
validateUserId(user.getId());
break;
default:
throw new java.lang.IllegalArgumentException();
}
}

public static enum Validation{
ESTRICT, LAZY, NONE;
}

}


Es bastante gráfico el ejemplo de utilización de los enum para definir estados y opciones más concretas. Podríamos haber definido la validación utilizando boolean o int pero preguntaros ¿cuál de los siguientes ejemplos es más fácil de leer y entender?:


MonoUser myuser = new MonoUser(1);

MonoUser myuser = new MonoUser(true);

MonoUser myuser = new MonoUser(Validation.ESTRICT);


Creo que queda demostrada cual es la intención aquí, y la utilidad de los enum. Continuemos con validateStrings.

Esto



public boolean validateString(String a){ ...

public boolean validateString(String a, String b){ ...

public boolean validateString(String a, String b, String c){ ...

public boolean validateString(String a, String b, String c, String d){ ...

public boolean validateString(String a, String b, String c, String d, String e){ ...



Es cosa del pasado. Antiguo, viejo, y como alguien haga esto, deberían ponerle una etiqueta en la frente con @Deprecated :)

Entran los varargs en acción, para poder definir un método que reciba argumentos de manera dinámica, sin tener que repetirnos 30 veces para 30 parámetros distintos. Ojo, que esto también lo podemos utilizar para factories aunque puede ser un poco peligroso. Este es el ejemplo de validateStrings con varargs:


public static void validateStrings(String... strings) throws IncorrectFormatException{
for(String string : strings){
// Haz lo que debas con cada String
}
}


Como veis, sólo hace falta un parámetro para el método, así que podemos meter allí, cuantos String queramos de la siguiente manera:


// La forma limpia
String[] toverify = {user.getName(), user.getLastName(), user.getAddress()};
validateStrings(toverify);

// O como hemos visto más arriba
validateStrings(new String[] {user.getName(), user.getLastName(), user.getAddress()});


Para este caso concreto, quiero verificar que los datos que voy persistir, no sean inválidos y que la BB.DD. se pegue una hostia por esto. Así que supongamos que hemos definido los campos name, lastname y address como varchar(50). Tenemos que evitar, que si alguno de los datos contenidos en una nueva entidad, contienen más de 50 caracteres, podamos atraparlos y tomar medidas al respecto.

Nota: Este problema, lo tendremos al implementar la persistencia utilizando un modelo bottom-up o lo que es lo mismo, el esquema de la BB.DD. ya está definido y no podemos cambiarlo a complacencia. Por lo que al intentar enviar un String demasiado grande para un campo, tendremos problemas.

Por último, si deseamos comentar cada elemento de un enum habrá que añadirle los comentarios típicos de javadoc a cada uno.


/**
* ENUM para determinar distintos tipos de
* validaciones
*/
public static enum Validation{
/**
* Validación Estricta, se validarán todos los campos
*/
ESTRICT,

/**
* Validación amigable, solo validará los campos que no pueden ser 'null'
*/
LAZY,

/**
* Sin validación, sólo valida que el ID este definido en caso de ser necesario.
*/
NONE;
}


Como siempre, espero que os sea de utilidad para mejorar vuestras aplicaciones.

Nota2: El tema de los factories con varargs es peligros, ya que no podemos controlar el orden con el que recibimos los datos, así que podemos terminar creando un MonoUser con la dirección en el campo name.