¿Es un int? ¿Es un String? ¡Es Superman!

Esta es una de esas cosas que veo demasiado a menudo


private int telefono;

Alguien define un teléfono como una variable numérica. Digo el teléfono pero puede ser el código postal, el DNI, el número de puerta en una dirección, etc.

Pregunta ¿cuándo una variable ha de ser un int y cuándo un String?

Mucha gente piensa lo siguiente, el teléfono es todo números, ergo es un int. La verdad es que los tipos de datos numéricos se crearon en los lenguajes de programación con un solo objetivo: vas a hacer operaciones aritméticas con ellos. ¿Alguna vez alguien ha visto algo del tipo telefono++? ¿telefono = (telef1 + telef2)/2 ? Evidentemente no. Pues entonces el número de teléfon, el código postal y el DNI son un String.

La regla es muy simple, si no vas a utilizar operaciones aritméticas con el dato entonces no es un número, es un literal. No puede ser más simple.

Cualquier tipo de supuesta optimización para favorecer el uso de números (el número es más rápido que el literal, ocupa menos en bbdd, etc. ) caen, a mi parecer, dentro de las reglas de optimización cuestionables.

Además, por si todavía no estás convencido:

  • Estos tipos de datos acostumbran a tener un significado (los dos primeros dígitos del código postal son la provincia, por ejemplo) que se puede extraer mucho más fácilmente con un String que con int.
  • Muchos de estos ‘”números” pueden empezar por cero: códigos postales, teléfonos… lo que complica mucho su tratamiento como número.
  • No puedes estar seguro que el dato siempre tendrá un formato numérico. Cuantas veces he visto que alguien ha puesto puerta (en una clase Direccion) como int y luego no podía poner 4B y se tenía que inventar un nuevo dato letra, este sí como un String.
Publicado en Diseño | Deja un comentario

Expresiones regulares útiles

Las expresiones regulares son un gran invento que nos permiten validar de manera fácil una cadena e caracteres  Lamentablemente su sintaxis puede parecer algo esotérica lo que hace que mucha gente se líe al utilizarlas. A continuación van algunos de los ejemplos más comunes que utilizo en mis validaciones.

Las explicaciones de las expresiones regulares son la traducción de las ofrecidas por My RegexTester, lugar ideal donde hacer pruebas.

Código postal

El código postal en España son cinco números. Los dos primeros van del 01 al 52 y los tres restantes pueden ser cualquier valor numérico

0[1-9][0-9]{3}|[1-4][0-9]{4}|5[0-2][0-9]{3}

Explicación

------------------------------------------------------------------
	0		'0'
------------------------------------------------------------------
	[1-9]		cualquier carácter del '1' al '9'
------------------------------------------------------------------
	[0-9]{3}	cualquier carácter del '0' al '9'(3 veces)
------------------------------------------------------------------
|			O
------------------------------------------------------------------
	[1-4]		cualquier carácter del '1' al '4'
------------------------------------------------------------------
	[0-9]{4}	cualquier carácter del '0' al '9'(4 veces)
------------------------------------------------------------------
|			O
------------------------------------------------------------------
	5		'5'
------------------------------------------------------------------
	[0-2]		cualquier carácter del '0' al '2'
------------------------------------------------------------------
	[0-9]{3}	cualquier carácter del '0' al '9'(3 veces)
------------------------------------------------------------------

Fecha

Una fecha en formato día mes año con los siguientes separadores posibles: ‘\’,’/’,’ ‘ o ‘-‘. Se permiten tanto valores del tipo 1/1/2001 como 01/01/2001

Notad un detalle que casi nunca veo en expresiones regulares de otros ejemplos: el formato del primer separador se almacena para asegurar que el segundo separador es del mismo estilo. Es decir, valores como 12/12-2012 son incorrectos ya que, a pesar de que el primer y segundo separador tienen caracteres permitidos, no son iguales.

(?:0?[1-9]|[12][0-9]|3[01])([/ -\\])(?:0?[1-9]|1[012])\\1[12][0-9]{3}

Explicación

------------------------------------------------------------------
(?: 			agrupa pero no captura el valor
------------------------------------------------------------------
	0? 		'0' (opcional)
------------------------------------------------------------------
	[1-9]		cualquier carácter del '1' al '9'
------------------------------------------------------------------
|			O
------------------------------------------------------------------
	[12]		cualquier de los siguientes caracteres:
			'1', '2'
------------------------------------------------------------------
	[0-9]		cualquier carácter del '0' al '9'
------------------------------------------------------------------
|			O
------------------------------------------------------------------
	3		'3'
------------------------------------------------------------------
	[01]		cualquier de los siguientes caracteres:
			'0', '1'
------------------------------------------------------------------
) 			fin del grupo
------------------------------------------------------------------
(			agrupa y guarda el valor en \1:
------------------------------------------------------------------
	[/ -\\]		cualquier de los siguientes caracteres:
			'/', ' ', '-', '\'
------------------------------------------------------------------
)			fin del grupo \1
------------------------------------------------------------------
(?:			agrupa pero no captura el valor
------------------------------------------------------------------
	0?		'0' (opcional)
------------------------------------------------------------------
	[1-9]		cualquier carácter del '1' al '9'
------------------------------------------------------------------
|			O
------------------------------------------------------------------
	1		'1'
------------------------------------------------------------------
	[012]		cualquier de los siguientes caracteres:
			'0', '1', '2'
------------------------------------------------------------------
)			fin del grupo
------------------------------------------------------------------
\\1			el valor que se guardó en el grupo \1
------------------------------------------------------------------
[12] 			cualquier de los siguientes caracteres:
			'1', '2'
------------------------------------------------------------------
[0-9]{3}		cualquier carácter del '0' al '9'(3 veces)
------------------------------------------------------------------

Email

Posiblemente uno de los patrones más utilizados. Fuente: Mykong

[_A-Za-z0-9-]+(?:\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(?:\\.[A-Za-z0-9]+)*(?:\\.[A-Za-z]{2,})

Explicación

------------------------------------------------------------------
[_A-Za-z0-9-]+		cualquiera de los siguientes caracteres:
			'_', 'A' a 'Z', 'a' a 'z', '0' a '9', '-'
			(una o más veces)
------------------------------------------------------------------
(?:			agrupa pero no captura el valor
------------------------------------------------------------------
	\.		'.'
------------------------------------------------------------------
	[_A-Za-z0-9-]+	cualquiera de los siguientes caracteres:
			'_', 'A' a 'Z', 'a' a 'z', '0' a '9', '-'
			(una o más veces)
------------------------------------------------------------------
)*			fin del grupo (0 o más veces)
------------------------------------------------------------------
@			'@'
------------------------------------------------------------------
[A-Za-z0-9]+		cualquiera de los siguientes caracteres:
			'A' a 'Z', 'a' a 'z', '0' a '9'
			(una o más veces)
------------------------------------------------------------------
(?:			agrupa pero no captura el valor
------------------------------------------------------------------
	\.		'.'
------------------------------------------------------------------
	[A-Za-z0-9]+	cualquiera de los siguientes caracteres:
			'A' a 'Z', 'a' a 'z', '0' a '9'
			(una o más veces)
------------------------------------------------------------------
)*			fin del grupo (0 o más veces)
------------------------------------------------------------------
(?:			agrupa pero no captura el valor
------------------------------------------------------------------
	\.		'.'
------------------------------------------------------------------
	[A-Za-z]{2,}	cualquiera de los siguientes caracteres:
			'A' a 'Z', 'a' a 'z' (al menos 2 veces)
------------------------------------------------------------------
) fin del grupo 
------------------------------------------------------------------

DNI

Uno fácil. Nueve dígitos más una letra mayúscula o, si eres extranjero, una letra, siete números y otra letra. La letra del DNI se calcula mediante un algoritmo que es de sobras conocido pero que en este tipo de validaciones por patrones no se puede calcular.

Respecto a dicha validación, mucha gente no sabe es que existe alguna partida de DNIs que tienen la letra mal calculada (sin comentarios, pero supongo que a la mayoría no le sorprende). Si hacemos que la validación de la letra cuadre con el algoritmo un día nos encontraremos que una persona con un DNI legal no se puede dar de alta en nuestro sistema. Para solucionarlo es mejor que la comprobación de la letra se marque como un aviso pero que sea posible insertar un DNI con letra incorrecta para hacerle la vida más simple a esos pobres desdichados que van por el mundo con un DNI incorrecto.

[0-9A-Z][0-9]{7}[A-Z]

Explicación

------------------------------------------------------------------
[0-9A-Z]		cualquier carácter del '0' a '9', 
			'A' a la 'Z'
------------------------------------------------------------------
[0-9]{7}		cualquier carácter del '0' a '9' (7 veces)
------------------------------------------------------------------
[A-Z]			cualquier carácter de la 'A' a la 'Z'
------------------------------------------------------------------

Teléfono

Permite prefijo internacional (hasta 5 dígitos) que pueden estar precedidos de un carácter ‘+’ y pueden estar entre paréntesis, más el número en si. El número está en formato de dos números seguidos, espacio opcional, un ciclo de 6 números más espacio opcional y un número al terminar. De esta manera se permiten formatos como XXXXXXXXX, XXX XXX XXX, XX XXX XX XX o XXX XX XX XX .

(?:[+]?(?:[0-9]{1,5}|\\x28[0-9]{1,5}\\x29)[ ]?)?[0-9]{2}(?:[0-9][ ]?){6}[0-9]

------------------------------------------------------------------
(?:				agrupa pero no captura el valor
------------------------------------------------------------------
	[+]?			'+' (opcional)
------------------------------------------------------------------
	(?:			agrupa pero no captura el valor
------------------------------------------------------------------
		[0-9]{1,5}	cualquier carácter del '0' al '9'
				(1 a 5 veces)
------------------------------------------------------------------
	|			O
------------------------------------------------------------------
		\\x28		carácter 40 ( '(' )
------------------------------------------------------------------
		[0-9]{1,5}	cualquier carácter del '0' al '9'
				(1 a 5 veces)
------------------------------------------------------------------
		\\x29		carácter 41 ( ')' )
------------------------------------------------------------------
	)			fin del grupo
------------------------------------------------------------------
	[ ]?			' ' (opcional)
------------------------------------------------------------------
)?				fin del grupo
------------------------------------------------------------------
(?:				agrupa pero no captura el valor
------------------------------------------------------------------
	[0-9]			cualquier carácter del '0' al '9'
------------------------------------------------------------------
	[ ]?			' ' (opcional)
------------------------------------------------------------------
){8}				fin del grupo (repetir 8 veces)
------------------------------------------------------------------
[0-9]				cualquier carácter del '0' al '9'
------------------------------------------------------------------
Publicado en Programación | Etiquetado , , , | 3 comentarios

Transacciones con Spring

El tratar transacciones con Spring e Hibernate es una de las casuísticas más utilizadas por los proyectos de programación y aun así continúa siendo una de las menos conocidas.

Introducción

Una transacción de base de datos es un conjunto de instrucciones que se ejecutan en bloque. Por ejemplo, hago una consulta, modifico un registro A en la base de datos y elimino un registro B. Si en alguna de estas instrucciones se produce un error todo el proceso se echa atrás. De esta manera si luego consulto la base de datos veré que el registro A no ha sido alterado. Este proceso de “tirar atrás” las instrucciones realizadas se le dice hacer un rollback, mientras que el proceso de confirmar todas las instrucciones en bloque una vez hemos visto que no se ha producido ningún error se le llama hacer un commit.

Las transacciones se empiezan y terminan a nivel de servicio, nunca a nivel de DAO. Si lo pensamos tiene lógica, el servicio es el que se encarga de gestionar toda la lógica de negocios: llamará a los DAOs que necesite para consultar, guardar o modificar registros y lo ha de hacer de manera atómica. Por ejemplo, supongamos un proceso batch que trabaja por la noche y que se encarga de recalcular la hipoteca de los clientes. En el primer paso obtiene el registro de la hipoteca, en el segundo recalcula la hipoteca y en el tercero actualiza el registro del cliente con una marca para señalar que el proceso ya ha procesado ese cliente. El DAO se encarga de la consulta, de la actualización de la hipoteca y de la actualización del registro con la marca de manera individual, primero con un método que lance una query, después con un update de la hipoteca y posteriormente con un update de la tabla de avisos del cliente. Pero es el servicio, o su equivalente batch, el que se encarga de que todo este proceso se trate como una unidad. Si empezase la transacción a nivel del DAO no tendría manera de controlar la atomicidad de todo el proceso o, peor aún, debería trasladar la lógica del negocio a la capa del DAO.

Por otra parte, con JPA/Hibernate cada vez que deseemos hacer una modificación sobre la base de datos necesitamos una transacción activa. Esto es así a pesar de que sólo vayamos a insertar un dato y no un bloque de acciones.

Transacciones con Spring

La manera más común de tratar las transacciones en Spring es mediante la anotación @Transactional en la cabecera del método de una clase (nunca un interfaz) gestionada por Spring:


@Transactional
public void hazAlgoTransaccionalmente() {
	// Soy transaccional!
}

y la propiedad


<tx:annotation-driven />

en el fichero de contexto.

Ahora bien, no todo es tan simple como parece. Spring llama a los beans mediante proxies y esto es lo que añade complejidad al asunto. No voy a entrar en detalle de lo que es un proxy, lo dejo para una próxima entrada del glosario, simplemente señalar que debido a esto la anotación sólo funcionará en métodos públicos desde los cuales se acceda a la clase. O sea:


@Transactional
private void hazAlgoTransaccionalmente() {
	// No soy transaccional a pesar de la anotación
}

no funciona ya que el método de acceso es privado. Pero lo peor no es que no funcione, lo peor es que no funcionará y no te avisará de que te estás equivocando. Y es que la anotación @Transactional es tratada como un metadato, si lo puede aprovechar lo hace pero si no lo ignora.

Otro código erróneo que tampoco funcionará:


@Transactional
protected void hazAlgoTransaccionalmente() {
	// No soy transaccional a pesar de la anotación
}

Como antes el método no es público.

Veamos ahora un caso que da muchos quebraderos de cabeza:


public class A {

	@Autowired
	private B b;

	public void hazAlgoEnB() {
		b.hazAlgo();
	}

}

public class B {

	public void hazAlgo() {
		hazAlgoTransaccionalmente();
	}

	@Transactional
	public void hazAlgoTransaccionalmente() {
		// Soy transaccional?
	}
}

Tenemos una clase A que llama al método hazAlgo de la clase B que a su vez llama al método hazAlgoTransaccionalmente. Esta vez la anotación @Transactional está en un método público ¿Tendré una transacción activa? La respuesta es no. Puesto que el método con el que hemos entrado en la clase B es hazAlgo y este no está marcado como transaccional, no existe transacción.

En cambio


public class A {

	@Autowired
	private B b;

	public void hazAlgoEnB() {
		b.hazAlgo();
	}

}

public class B {

	@Transactional
	public void hazAlgo() {
		hazAlgoTransaccionalmente();
	}

	public void hazAlgoTransaccionalmente() {
		// Solo soy transaccional si se me llama
		// desde el método hazAlgo
	}
}

sí que funciona. Ahora la anotación @Transactional sí que está sobre el método de entrada a la clase con lo que se activa una transacción.

Ojo con esto de los métodos de entrada porque he visto a más de uno dándose golpes contra la pared por su culpa. La regla es simple, la anotación sólo tiene efecto en la cabecera de un método por el cual se acceda a la clase. Evidentemente se puede marcar todos los métodos como transaccionales, e incluso la propia clase (con lo que todos sus métodos públicos lo serán) pero como veremos más adelante eso nos puede dar algún problema por lo que hay que ser cautos.

Ámbito de la transacción

Una cosa que se ha de tener clara es cuanto dura una transacción. Esto es, cuando empieza y cuando termina. Si tenemos anotado un método con @Transactional, la transacción empezará justo antes de la primera línea del método y terminará justo después de la última. Si dentro de esto método existen llamadas a otros métodos estos otros métodos son llamados dentro de la transacción sin necesidad de anotarlos con @Transactional.

Ahora bien ¿qué sucede cuando un método anotado con @Transactional llama a otro método anotado también con @Transactional? Por ejemplo:


public class A {

	@Autowired
	private B b;

	@Transactional
	public void hazAlgoA() {
		b.hazAlgoB();
	}

}

public class B {

	@Transactional
	public void hazAlgoB() {
		hazAlgoTransaccionalmente();
	}
}

¿Se mantiene la misma transacción? ¿Se abre una nueva?… Bueno, a esto se le llama propagación de la transacción. Por defecto una transacción tiene una propagación de PROPAGATION_REQUIRED, si la transacción no existe la crea y si la tiene la aprovecha. Según esto, el primer método abre la transacción y el segundo la aprovecha por lo que todo está dentro de la misma transacción.

Ahora supongamos que queremos justo lo contrario. Necesitamos que el método de la clase B utilice una nueva transacción, de esta manera si algo falla en B, la transacción de A no hace rollback. Tal como está actualmente no nos sirve por lo que hemos de modificar la propagación de B de la manera siguiente.


@Transactional( propagation = Propagation.REQUIRES_NEW )
public void hazAlgoB() {
	hazAlgoTransaccionalmente();
}

Ahora, al entrar en B, sí se generará una nueva transacción.

Spring soporta las siguientes propagaciones:

  • PROPAGATION_REQUIRED – Es la que viene por defecto, así que no es necesaria especificarla. Si existe transacción la aprovecha y sino la crea
  • REQUIRES_NEW – Abre una transacción nueva y pone en suspenso la anterior. Una vez el método marcado como REQUIRES_NEW termina se vuelve a la transacción anterior.
  • PROPAGATION_SUPPORTS – Si existe transacción la aprovecha, sino no crea ninguna.
  • PROPAGATION_MANDATORY – Si no existe una transacción abierta se lanza una excepción. Hay gente que anota sus DAO con esta opción.
  • PROPAGATION_NEVER – Si existe una transacción abierta se lanza una excepción. No se me ocurre ningún ejemplo donde esto sea necesario pero seguro que alguno hay.
  • PROPAGATION_NOT_SUPPORTED – Si existe una transacción la pone en suspenso, la transacción se reactiva al salir del método.

Errores comunes con @Transactional

Transacción obligatoria

Una de las confusiones más comunes que existen es que en Spring/Hibernate se requiere una transacción para hacer una consulta a la base de datos lo cual no es del todo cierto. Si en el test de DBUnit ponemos lo siguiente.


@Test
public void getAllTest() {
	List resultados = genericDao.getAll(Persona.class);
	Assert.assertEquals(3, resultados.size());
}

Notad que no hay @Transactional pero el método ejecuta perfectamente y devuelve el resultado correcto. Ergo, no hace falta transacciones para consultas de datos aisladas. Vale, hago trampas, en realidad Hibernate sí que abre una transacción pero lo hace sin decirnos nada y de manera transparente.

Transacciones read-only

Otra confusión es la propiedad readonly. Si ponemos


@Transactional(readonly=true)
public void hazAlgoTransaccionalmente() {
	// Soy transaccional!
}

¿qué se supone que hace? Según la documentación una transacción readonly puede ser utilizada cuando quieres que tu código lea pero no modifique ningún dato y deja intuir que puede ser beneficioso en términos de rendimiento.

Ahora bien, ¿cómo afecta a esto a la hora de hacer inserciones y/o actualizaciones? Supongamos el siguiente código lanzado con JUnit sobre una base de datos HSQLDB:


@Test
@Transactional(readonly=true)
public void insertTest() {
	List resultados = genericDao.getAll(Persona.class);
	Assert.assertEquals(3, resultados.size());

	Persona p = new Persona();
	p.setNombre("Test");
	p.setEdad(33);
	Assert.assertEquals(0, p.getId());

	genericDao.insert(p);
	resultados = genericDao.getAll(Persona.class);
	Assert.assertEquals(4, resultados.size());
}

¿Qué sucede? La verdad es que esto tiene algo de trampa. Uno puede pensar que si he marcado la transacción como readonly la inserción de la persona no debería hacerse y el test fallaría. Pero no, Hibernate pasa del readonly e inserta el registro sin problema. Otros pensarán: No, que no te enteras, lo que sucede es que la entidad persona queda marcada como de solo lectura y lo que no puedo es modificarla y luego hacer un update. Pues tampoco, le cambio la edad y para la base de datos. Los más avispados pueden pensar, lo que sucede es que no haces un flush antes de la query. Vale, aceptamos barco. Eso sí que es cierto. O sea, que parece que la documentación no es correcta y el readonly prácticamente no hace nada.

El problema es cuando sales del test y vas a producción y el comportamiento es diferente y empiezan a saltarte errores. WTF! ¿Qué sucede aquí? ¡Si mis tests funcionan perfectamente! Pues sucede que el parámetro readonly es dependiente de a) la implementación JPA y b) el dirver jdbc que estés utilizando.

Por ejemplo, el test anterior lo lancé en una base de datos HSQLDB pero el comportamiento en Oracle puede ser diferente. Con Oracle 11 sí que puede generar una transacción de solo lectura, con HSQLDB no existe esa posibilidad. Ojo, esto no pasa en todas las versiones de Oracle ya que depende del driver que utilices. Si el driver sobreescribe correctamente el método setReadOnly del interfaz Connection tendremos una transacción readonly. Si no lo tenemos, pues sólo tendremos que no nos hace un flush antes de una query.

Y lo malo de todo esto es que no está documentado en ninguna parte. Para que uno se fíe de la documentación. Así que ojo.

Modificar la transacción

Hay gente que intenta modificar los parámetros de la transacción una vez abierta. Por ejemplo, algo que ya he visto alguna vez:


public class A {

	@Autowired
	private B b;

	public void hazAlgoA() {
		b.hazAlgoB();
	}

}

class B {

	@Autowired
	private C c;

	@Transactional
	public void hazAlgoB() {
		// inserto un dato en bbdd
		try {
 			c.hazAlgoC();
		catch( NullPointerExecption e) {
		}
		// inserto otro dato en bbdd

	}
}

class C {

	@Transactional(readonly=true)
	public void hazAlgoC() {
		// leo un dato en bbdd
	}
}

Se abre una transacción para B se llama a C y cómo se quiere hacer una lectura se marca la transacción como readonly. Bueno, esto no funciona. Una vez abierta una transacción no se puede modificar su estado.

Commit y Rollback

Ok, ya lo tenemos (casi) todo claro. Ahora lo que queremos es controlar el commit o el rollback de nuestra transacción. La cosa es simple, si un método anotado como @Transaccional lanza una excepción que herede de RuntimeException se producirá un rollback. En caso contrario, un commit.

Ok, vamos a dar vueltas a esto. Primero lo más evidente:


public class A {

	@Autowired
	private B b;

	public void hazAlgoA() {
		try {
			b.hazAlgoB();
		}
		catch( NullPointerExecption e) {
		}
	}

}

public class B {

	@Transactional
	public void hazAlgo(Entidad entidad) {
		// inserto un dato en bbdd

 		throw new NullPointerException();

		// inserto otro dato en bbdd
	}
}

lanza una NullPointerException, que hereda de RuntimeException, luego se produce un rollback y la primera inserción no se realiza (ni la segunda, claro).

Veamos esto


public class A {

	@Autowired
	private B b;

	public void hazAlgoA() {
		try {
			b.hazAlgoB();
		}
		catch( FileNotFoundException e) {
		}
	}

}

public class B {

	@Transactional
	public void hazAlgo(Entidad entidad) {
		// inserto un dato en bbdd

 		throw new FileNotFoundException();

		// inserto otro dato en bbdd
	}
}

¿Qué hará? ¿Inserta un dato? ¿Dos? ¿Ninguno? Veamos, FileNotFoundException no hereda de RuntimeException por lo que no debería hacer rollback, pero lanzamos la excepción antes de insertar el segundo dato así que el flujo del programa no llegaría al código de insertarlo, pero si hace el primero, ergo solo insertamos un dato. Podemos imaginar que hay algo así:


public void hazAlgo(Entidad entidad) {
	try{
		tx.begin

		// inserto un dato en bbdd
		throw new FileNotFoundException();

		// inserto otro dato en bbdd
	}
	catch(Exception e ) {
		if( e instanceof RuntimeException ) {
			tx.rollback();
			tx.close();
		}
		else {
			tx.commit();
			tx.close();
		}
		throw e;
	}
}

¡Esto no es el código que hay realmente! Tan sólo es poner en pseudocódigo lo explicado anteriormente.

Compliquemoslo un poco más:


public class A {

	@Autowired
	private B b;

	public void hazAlgoA() {
		b.hazAlgoB();
	}

}

class B {

	@Autowired
	private C c;

	@Transactional
	public void hazAlgoB() {
		// inserto un dato en bbdd
		try {
 			c.hazAlgoC();
		catch( NullPointerExecption e) {
		}
		// inserto otro dato en bbdd

	}
}

class C {

	@Transactional
	public void hazAlgoC() {
		// inserto un dato en bbdd

 		throw new NullPointerException();

		// inserto otro dato en bbdd
	}
}

Esta es más interesante. La clase C lanza una NullPointerException que hereda una RuntimeException ergo debería hacer un rollback. Pero, pero, pero, la clase B, donde se inicia la transacción ya tenía previsto este error, así que captura la excepción y no la vuelve a lanzar. ¿Hará un rollback? ¿Hará un commit? Hagan sus apuestas.

Bueno, hace un rollback. El punto es que si una RuntimeExcepcition sale de un método anotado como @Transactional, toda la transacción queda marcada como rollback. No importa que no fuese el método desde el que se inició la transacción, si su propagación es normal (esto es, no es una REQUIRES_NEW), en cuanto lanzamos la excepción estamos vendidos.

Esto es importante por un motivo, hay gente que, ante la duda, anota todos los métodos o las clases con @Transactional pero esto supone un problema de concepto de construcción. El encargado de saber si la transacción ha de marcarse como rollback o commit es siempre el método desde donde se inicia la transacción, nunca uno de los métodos a los que llama. Si yo llamo a un método externo que está marcado como transaccional y este por algún motivo me lanza una RuntimeExcepción, automáticamente me obliga a hacer un rollback. No me deja la opción de que sea yo el que valore si puedo continuar o no, quizás ya tenía previsto que podía saltar esa excepción y la capturaba para tratarla. Lamentablemente, al utilizar una librería que anotó su método con @Transactional me limita de cualquier intento de recuperarme de la excepción.

Moraleja, sólo hay que poner @Transactional en métodos donde se abre, cierra y controla la transacción. En el resto de métodos su colocación puede ser contraproducente.

Controlar las excepciones que hagan commit o rollback

El hecho anterior, que una transacción haga rollback si se lanza una RuntimeExcecption y commit si no, es algo demasiado rígido para muchos casos. Hay momentos que no queremos que se haga un rollback para alguna excepción en concreto o lo contrario, no queremos que haga un commit cuando se lanza una excepción que no herede de RuntimeException. Para esto tenemos las propiedades noRollbackFor y rollbackFor, con estas opciones podemos configurar el commit y el rollback según nuestras necesidades.

Por ejemplo:


@Transactional(noRollbackFor={NumberFormatException.class,ArithmeticException.class})
public void hazAlgoTransaccionalmente() {
	// Soy transaccional!
}

Hará un commit incluso si lanza las excepciones NumberFormatException o ArithmeticException o excepciones que hereden de estas. Lo de las excepciones que hereden de esta es muy importante para tener en cuenta su alcance. Hay gente que lo aprovecha para hacer cosas del tipo:


@Transactional(noRollbackFor={RuntimeException.class})
public void hazAlgoTransaccionalmente() {
	// Soy transaccional!
}

lo cual es una muy pésima idea porque implica que el método nunca hará un rollback.

Tal como he dicho existe la propiedad inversa, rollbackFor. Con esta podremos conseguir que la transacción haga un rollback para las excepciones que no hereden de RuntimeException.

Por ejemplo


@Transactional(rollbackFor={FileNotFoundException.class})
public void hazAlgoTransaccionalmente() {
	// Soy transaccional!
}

hará un rollback si lanza una FileNotFoundException a pesar de que esta no hereda de RuntimeException.

Transacciones programáticas

No siempre se pueden abrir transacciones con @Transactional. A veces necesitamos crear una transacción en algún lugar que la anotación no nos permite (un método privado, por ejemplo) así que hemos de buscarnos un método alternativo. Para esto utilizaremos las transacciones programáticas  Con estas podremos abrir y cerrar transacciones sin necesidad de anotaciones

Veamos el siguiente código:


Obejct o = new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
	public Object doInTransaction(TransactionStatus status) {
		// Soy Transaccional!
	}
});

Con él podemos abrir la transacción sin necesidad de anotaciones.

El código se puede modificar para añadir diferentes tipos de transacciones (propagación o aislamiento), para que nos devuelva un resultado ((TransactionCallback) o no (TransactionCallbackWithoutResult), etc. Para más información leer el manual

Lo que me dejo en el tintero

Lo más evidente es el aislamiento de la transacción (Isolation). Por ejemplo, por defecto la transacción no  nos permite hacer una consulta y que en ella aparezcan datos que se han insertado pero que todavía no se han commiteado pero modificando el nivel de aislamiento de la transacción sí que es posible. Las opciones de aislamiento no son muchas (cuatro) pero las consecuencias de estas opciones dan para mucho, así que lo dejo para otro momento.

Otra cosa es la duración de la transacción. Hay gente que hace transacciones enormes de centenares o miles de inserciones y/o modificaciones. Esto puede ser problemático debido a la memoria que consume y a la necesidad real de generar transacciones muy largas. En general es mejor tener transacciones pequeñas y bien definidas.

Hay gente que utiliza transacciones mediante aspectos. Con librerías tipo AspectJ se puede saltar las limitaciones de anotaciones en métodos privados o la (engorrosa) obligatoriedad de poner la anotación en el método de entrada de la clase. Lamentablemente primero tendría que explicar la programación orientada a aspectos y esto ocuparía más que su ejemplo en transacciones.

También existe la posibilidad de definir las transacciones en los ficheros de configuración de Spring. Esto te permite hacer cosas  más potentes como, por ejemplo, que todos los métodos de los DAO cuyo nombre empiezan por get abran transacciones readonly y los que empiezan por insert abran transacciones normales. Todo, por supuesto, sin tener que añadir nada al código. A la hora de la verdad algo tan genérico no sirve de mucha ayuda y, yo al menos, ni lo utilizo ni lo he visto en ningún proyecto en el que haya estado. En cualquier caso es algo que es útil conocer por sus posibilidades.

Publicado en Programación, Programación-JPA, Programación-Spring | Etiquetado , , , | 13 comentarios

Singleton

Un singleton es un patrón de diseño que consiste en que sólo exista un único objeto de una clase para toda la aplicación. Para conseguir esto se prohíbe el uso del constructor haciéndolo privado (de otra forma podríamos crear tantos objetos como quisiéramos) y se obtiene la instancia mediante un método que nos proporciona siempre el mismo objeto. O sea:



public class MiSingleton {

  private final static MiSingleton instancia = new MiSigleton();

  private MiSingleton(){}

  public static MiSingleton getSingleton() {
    return instancia;
  }
}


Hay otras maneras de hacerlo, pero esta es una de las más simples.

El uso de este patrón de diseño está cada vez más extendido. Antes se utilizaba para clases que referenciaban algo único, como el contexto de la aplicación (FacesContext en JavaServer Faces o SpringContext en Spring) o cuando se quería hacer algo parecido a una clase global (para los Logs, por ejemplo). Actualmente se generan singletons como churros. Por ejemplo, las clases que genera Spring por defecto son un singleton.

Mucha gente desconoce una pequeña gran limitación del patrón. Un singleton como el creado más arriba es único, sí, pero solo en su cargador de clases (el classloader) Una aplicación normal de escritorio o de móvil normalmente sólo tiene un classloader (vaaaale, en realidad tiene tres pero los otros dos son para la clases de java.* y sus extensiones por lo que sólo utilizamos el tercero) pero una aplicación empresarial que utilice un servidor de aplicaciones JEE (tipo JBoss AS o Weblogic) o que utilice una arquitectura tipo OSGI puede estar en más de uno. Según esta regla, podemos encontrarnos con más de un singleton del mismo tipo en la aplicación. Os podéis imaginar el lío y la dificultad en encontrar este tipo de errores.

Por otra parte, los singletons que crea Spring o una aplicación JEE difieren en que son único para su contexto en lugar de para su classloader. Esto acostumbra a ser suficiente para las aplicaciones empresariales por lo que es mejor dejar que sea Spring o JEE el que genere el singleton en lugar de crearlo programaticamente nosotros.

Publicado en Glosario | Etiquetado , , | Deja un comentario

El infame lazy init

Uno de los errores más comunes que se ven en los proyectos donde he estado es el infame lazy init de variables, especialmente collections. O sea:


private List lista = null;

public List getLista() {
  if (lista == null) {
    lista = new ArrayList();
  }
  return lista;
}

No me diréis que no lo habéis visto nunca. La excusa de esto es la “carga perezosa”: no se carga la lista hasta que no se necesita con lo que se ahorra tiempo de cálculo al inicio, memoria y bla, bla, bla.

Esto en un proceso monohilo no tendría mayor problema. El problema es cuando este código está en una clase que ha de ser accesible por muchos hilos simultáneamente, y esto incluye cuando la gestionamos en un bean de Spring o JEE o singletons similares. Puesto que la variable es compartida por las instancias de la clase, todos los hilos acceden a ella sin preocuparse unos de otros. En otras palabras: la clase no es thread safe con lo que hay que sincronizar o tendremos lío asegurado.

El detalle

Mucha gente no ve el porqué. Vamos a verlo con algo más de detalle:

Lo que todo el mundo espera que pase es: el hilo A entra en el método, ve que es null y crea el objeto; el hilo B entra, ve que no es null y lo devuelve ya creado. Si siempre pasase esto no habría ningún problema.

Lo que puede pasar es: el hilo A entra en el método, ve que es null; el hilo B entra simultáneamente y también ve que es null; el hilo A crea el objeto y el hilo B lo vuelve a crear. Aquí tenemos un problema, hemos creado dos objetos diferentes donde sólo esperábamos uno.

Solución 1 (Correcta pero poco óptima)

La primera solución, y la más fácil, es sincronizar el método completo:


private List lista = null;

public synchronized List getLista() {
  if (lista == null) {
    lista = new ArrayList();
  }
  return lista;
}

Lo cual, a pesar de ser correcto, es muuuuy poco óptimo ya que lo que obligamos es a poner a todos los hilos en fila y a entrar al método uno a uno siempre, incluso cuando ya hemos creado la lista y no necesitamos sincronizar nada.

Solución 2 (Incorrecta)

Podemos mejorarlo un poco más:


private List lista = null;

public List getLista() {
  if (lista == null) {
    synchronized(this) {
      lista = new ArrayList();
    }
  }
  return lista;
}

Ahora parece mejor pero ya no es correcto. Por ejemplo, el hilo A y el B entran simultáneamente, como hay un synchronized se ponen en fila (ya no pueden entrar a la vez en el bloque); entra el hilo A y crea la lista, al salir el hilo A del bloque sincronizado el hilo B ya puede entrar y vuelve a crear la lista. Hemos creado dos objetos, el mismo problema de antes.

Solución 3 (Incorrecta)

Y aquí llegamos a la solución que casi todo el mundo considera correcta, la doble comprobación:


private List lista = null;

public List getLista() {
  if (lista == null) {
    synchronized(this) {
      if (lista == null) {
        lista = new ArrayList();
      }
    }
  }
  return lista;
}

Ahora parece todo correcto, el hilo A crea el objeto, el hilo B entra en el bloque sincronizado, comprueba que el objeto ya ha sido creado y devuelve el objeto creado por el primer hilo. ¡Prueba superada!

Pues no, esto está mal y esta mal por algo que el 99.9% de programadores que conozco no se han molestado en aprender, el modificador volatile.

¿Cual es el problema? El hilo A entra en el bloque sincronizado y empieza a crear la lista, nótese el empieza porque aquí está el quid: puede darse el caso de que lista == null sea false pero lista todavía no esté correctamente instanciada. Sí, hijos míos, esto es perfectamente posible y así está documentado en las especificaciones de la JVM. Por ejemplo, tenemos una clase con 5 variables int que inicializamos en un constructor. Primero se crea la instancia con las variables en su valor por defecto (0) y luego se les adjudican los valores del constructor. Es posible que un hilo pille la construcción a medias y unas variables estén a 0 y otras no. Ojo, esto sólo pasa si las variables inicializadas en el constructor no son final. Un constructor que inicializa variables final es thread safe.

O sea, la lista está compartida por todos los hilos y su lectura no está sincronizada como en la Solución 1 (fijaos que el primer bloque if está fuera del synchronized) por lo que se puede dar el caso de que el hilo A empieza a crear la lista, el hilo B vea lista==null como false y nos la devuelva antes que el hilo A termine de crearla y error al canto.

Solución 4 (Correcta)

Solución, hacer lista volatile. Con volatile nos aseguramos, entre otras cosas, que una instancia que lee un hilo cualquiera esté completamente inicializada y no parcialmente. Así pues el código correcto sería:


private volatile List lista = null;

public List getLista() {
  if (lista == null) {
    synchronized(this) {
      if (lista == null) {
        lista = new ArrayList();
      }
    }
  }
  return lista;
}

Solución 5 (Correcta)

Existe una solución todavía mejor ¿Realmente necesitamos una inicialización perezosa? Si la respuesta es no (valga decir que en el 99% de los casos en los que he visto este código la respuesta es no) podemos escribir:


private final List lista = new ArrayList();

public List getLista() {
  return lista;
}

Con final también nos aseguramos que no suceda la instanciación parcial de la que hablaba antes y además es más óptimo que volatile. Pensad que cualquier tipo de sincronización supone un coste en proceso. El de volatile es realmente muy poco pero si se puede evitar, bienvenido sea.

Ojo, olvidarse el final en la lista hace que esto vuelva a no ser thread safe. A pesar de que parezca anti-intuitivo se puede volver a dar el caso anterior, lista está parcialmente instanciada cuando algún hilo la lee, llamando al método getLista, y error otra vez.

Conclusiones

Os oigo los pensamientos: “Pues yo he hecho esto muchas veces y siempre ha ido bien”. El hecho de que hasta ahora no te haya fallado no quiere decir que esté bien, quiere decir que nunca te ha fallado.

También escucho los pensamientos del guru: “Pero esto solo pasa en las JVM tal y pascual y yo no las utilizo”. Tu código no es portable y lo peor, no puedes estar seguro que en alguna revisión esto no cambie y que sí que te afecte posteriormente.

En resumen, evitad las cargas perezosas a menos que de verdad se necesiten, utilizad el modificador final siempre que podáis y leeros la documentación porque esto es mucho más complicado de lo que parece. Por ejemplo:


private int i = 100;

es thread safe, pero


private long l = 100;

no lo es. (!?)

En la documentación encontraréis el porqué y en volatile la respuesta. Amén.

Publicado en Diseño | Etiquetado , , | Deja un comentario

Effective Java. 2nd edition

Autor: Joshua Bloch
Editorial: Addison-Wesley
Idioma: Inglés
Número de páginas: 369
Número de páginas útiles: 369!!

Cuando puse una sección de libros en mi web tenía muy claro que este sería el primero que analizaría (por llamarlo de alguna manera).

Para quien no lo sepa, Joshua Bloch es uno de los gurus de Java (por no llamarlo el guru) Es, por ejemplo, el creador de java.util.collection, una api que recomiendo encarecidamente observar para admirar lo que es un código bien realizado, estructurado y documentado. Hace unos años dió la campanada al dejar Sun y fichar por Google donde todavía continúa. Effective Java no es su único libro pero sí el más famoso. Es probablemente uno de los libros Java más vendido y me atrevería a decir que el más útil.

Lo primero que hay que remarcar es que Effective Java no es un libro para aprender a programar, se da por supuesto que el lector ya tiene unos conocimientos básicos, sino que está orientado a, tal como dice su nombre, crear código eficiente y práctico. Estructurado en 10 secciones principales (Creación y destrucción de objetos, Métodos comunes a todos los objetos, Clases e Interfaces, Genericos, Enums y anotaciones, Métodos, Programación en general, Excepciones, Concurrencia y Serialización) destripa los problemas o errores más comunes, no siempre fáciles de detectar, dando ejemplos y instrucciones de cómo evitarlos así como (excelentes) consejos de cómo programar correctamente.

Por ejemplo, la sección de excepciones, una de mis favoritas por la poca documentación existente sobre el tema, contiene 9 recomendaciones (o items como él los llama) de las que, simplemente leyendo el título, podemos aprender algo: “Utiliza excepciones solo para condiciones excepcionales”, “Utiliza excepciones que hereden de Exception para errores recuperables y excepciones que hereden de RuntimeException par errores de programación”, “Evita el uso innecesario de excepciones que hereden de Exception”, “Privilegia el uso de excepciones estandar”, “Documenta las excepciones lanzadas por un método”, etc… Otros items de otras secciones son: “Favorece interfaces sobre reflexión”, “Evita una sincronización excesiva”, “Favorece interfaces sobre clases abstractas”, “Diseña y documenta la herencia o sino prohibela”, “Sobreescribe siempre el método toString” y no sigo que es un vicio. En total 78 secciones de sabios consejos y errores a evitar.

Todos están explicado de manera muy didactica y clara. ¿Nunca has leído un libro que por cada párrafo interesante añade tres de relleno? Pues aquí no sucede. Cada item es corto y conciso, en general no más de cuatro páginas, expone un problema o recomendación, pone un ejemplo y explica una o varias soluciones. Por supuesto existen algunos items más extensos: “Favorece composición sobre herencia” de 7 páginas o el dedicado a cómo hacer un buen método equals que tiene 12 pero en ningún momento da la sensación de relleno. Todo es información útil.

Podría continuar alabando el libro ad infinitum pero no quiero excederme. Tan sólo terminaré diciendo que este fue el primer libro de informática que recomendé a una chica que me gustaba y de la que ahora soy su afortunado novio. Vamos, que sus virtudes son infinitas.

Publicado en Libros | Deja un comentario

DBUnit

DBUnit es una librería que permite hacer simplificar ciertas pruebas en las que se utilice una base de datos.

Por ejemplo, supongamos que tenemos que testear un método de consulta a la base de datos para ver si nos da algún resultado determinado. Puesto que estamos utilizando una base de datos en memoria con HSQLDB que siempre se inicia al principio nos encontramos que al inicio de cada test, la base de datos no tiene ningún registro. Esto es genial para algunas cosas ya que nos permite conocer el estado de la base de datos justo antes de lanzar el test. El problema es que para validar que el ejemplo anterior funciona primero hemos de crear algún dato e insertarlo en la base de datos ya que de otra manera no tendríamos nada sobre lo que probar ¿No sería genial que hubiese algo que ya hiciese esto por nosotros? Pues eso es DBUnit. Mediante DBUnit podemos alterar el estado de la base de datos para dejarlo tal como deseemos justo antes de un test.

Su uso más común es tener un fichero xml llamado dataset donde especificamos todo el contenido de la base de datos. Una vez ejecutamos un test, DBUnit vacía la base de datos sobre la que estamos probando (ojo, que si estamos en una base de datos física te borrará todos los datos) e introduce los datos del xml.

Voy a continuar el ejemplo anterior de Spring y JPA para añadirle soporte a DBUnit.

Lo primero es añadirle la dependencia, vamos al pom.xml y escribimos.


<dbunit.version>2.4.8</dbunit.version>

en properties y


<dependency>
	<groupId>org.dbunit</groupId>
	<artifactId>dbunit</artifactId>
	<version>${dbunit.version}</version>
	<scope>test</scope>
</dependency>

en dependencies.

Una vez hemos añadido la dependencia vamos a crear un dataset para la prueba. Creamos la carpeta cafebabe/test/jpa/datasets dentro de /src/test/resources y en su interior creamos un fichero llamado dataset.xml con el siguiente contenido:


<dataset>
	<PERSONA ID="1" VERSION="0" NOMBRE="TestPerson1" EDAD="31" />
	<PERSONA ID="2" VERSION="0" NOMBRE="TestPerson2" EDAD="32" />
	<PERSONA ID="3" VERSION="0" NOMBRE="TestPerson3" EDAD="31" />

	<DIRECCION ID="4" VERSION="0" PERSONA_ID="1" CALLE="Jacinto Barrera" CODIGOPOSTAL="08329" />
	<DIRECCION ID="5" VERSION="0" PERSONA_ID="2" CALLE="La Rambla" CODIGOPOSTAL="08001" />
	<DIRECCION ID="7" VERSION="0" PERSONA_ID="2" CALLE="Passeig de Gràcia" CODIGOPOSTAL="08001" />
	<DIRECCION ID="6" VERSION="0" PERSONA_ID="3" CALLE="Barcelona" CODIGOPOSTAL="08002" />
</dataset>

Como podemos ver el contenido se explica pos si solo. Crearemos tres registros para la tabla Persona y cuatro más para la tabla Direccion.

Una vez lo tenemos vamos a modificar el GenericDaoTest para que utilice DBUnit. Tan solo hemos de susituir el código anterior por el siguiente.


package cafebabe.test.jpa;

import java.io.IOException;
import java.net.MalformedURLException;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class GenericDaoTest {

	private static Logger log = LoggerFactory.getLogger(GenericDaoTest.class);

	@Autowired
	private GenericDao genericDao;

	@Autowired
	private DataSource dataSource;

	@Test
	@Transactional
	public void insertTest() {
		log.info("Inicio del test de inserción");

		List<Persona> resultados = genericDao.getAll(Persona.class);

		Assert.assertEquals(3, resultados.size());

		Persona p = new Persona();
		p.setNombre("Test");
		p.setEdad(33);

		Assert.assertEquals(0, p.getId());

		genericDao.insert(p);
		resultados = genericDao.getAll(Persona.class);

		Assert.assertEquals(4, resultados.size());
		log.info("Fin del test de inserción");
	}

	private IDatabaseConnection createDBUnitConnection() throws SQLException {
		IDatabaseConnection dbConn = new DatabaseDataSourceConnection(
				dataSource);
		DatabaseConfig config = dbConn.getConfig();
		config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
				new HsqldbDataTypeFactory());
		return dbConn;
	}

	private IDataSet createDataSet(String classPathResource)
			throws MalformedURLException, DataSetException, IOException {
		Resource resource = new ClassPathResource(classPathResource);
		IDataSet dataSet = new FlatXmlDataSetBuilder()
				.build(resource.getFile());
		return dataSet;
	}

	@Before
	public void setUp() throws Exception {
		IDataSet dataSet = createDataSet("/cafebabe/test/jpa/datasets/dataset.xml");
		IDatabaseConnection dbConn = createDBUnitConnection();
		DatabaseOperation.CLEAN_INSERT.execute(dbConn, dataSet);
	}
}

Vamos a verlo en detalle.

Primero notad que inyectamos el dataSource a la clase de test mediante la instrucción


@Autowired
private DataSource dataSource;

Esto es así ya que DBunit ha de parchear la conexión con la base de datos por lo que requiere los datos de conexión. Puesto que definimos el dataSource en el fichero de configuración de Spring no tenemos mayor problema que añadir la inyección de la instancia para obtenerla dentro del test.

Lo siguiente es crear la conexión que utilizará DBUnit. Esto lo hacemos mediante el método createDBUnitConnection:


private IDatabaseConnection createDBUnitConnection() throws SQLException {
	IDatabaseConnection dbConn = new DatabaseDataSourceConnection(dataSource);
	DatabaseConfig config = dbConn.getConfig();
	config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,new HsqldbDataTypeFactory());
	return dbConn;
}

Si os fijáis no tiene mucho de especial. Le pasamos el dataSource y le notificamos mediante una propiedad de configuración que estamos utilizando una base de datos del tipo HSQLDB. Podemos añadirle tantas propiedades como deseemos a través del objeto config. Tenéis una lista completa de las propiedades de configuración aceptadas aquí.

Una vez tenemos la conexión cargamos el dataset mediante el método createDataSet


private IDataSet createDataSet(String classPathResource)
			throws MalformedURLException, DataSetException, IOException {
	Resource resource = new ClassPathResource(classPathResource);
	IDataSet dataSet = new FlatXmlDataSetBuilder().build(resource.getFile());
	return dataSet;
}

Recibimos un String con la ruta del dataset y lo generamos mediante un builder. En este caso, como estamos creando el contenido mediante un xml, utilizaremos la clase FlatXmlDataSetBuilder pero se puede utilizar streams, csv o incluso consultas a una base de datos física.
Como detalle notad que cargo el fichero mediante un ClassPathResource de Spring. De esta manera me ahorro tratar con File y tener que poner una ruta completa de la localización del fichero (/src/test/resources…)

Por último creamos un método setUp marcado con @Beforeq ue se ejecutará justo antes de empezar los métodos marcados con @Test.


@Before
public void setUp() throws Exception {
	IDataSet dataSet = createDataset("/cafebabe/test/jpa/datasets/dataset.xml");
	IDatabaseConnection dbConn = createDBUnitConnection();
	DatabaseOperation.CLEAN_INSERT.execute(dbConn, dataSet);
}

Lo único que tenemos que hacer es decirle cómo ha de tratar los datos de la base de datos. En este caso queremos un clean insert: cada vez que empiece el test borrará la base de datos y la llenará con el contenido del xml pero podríamos haber elegido mantener los datos anteriores, actualizarlos, no hacer nada, etc. Podemos ver todas las operaciones disponibles aquí

Ejecutamos el test y voila: Al empezar el test ya podemos ver que la base de datos tiene tres registros en la tabla Persona a pesar de que no hemos realizado todavía ninguna inserción.


List<Persona> resultados = genericDao.getAll(Persona.class);
Assert.assertEquals(3, resultados.size());

Una cosa en la que me gustaría hacer incapié es que el dataset no ha de tener un registro completo de toda la base de datos sino del mínimo necesario para cumplir el test. Siguiendo esa norma mi dataset es incorrecto ya que tiene más datos de los necesarios. Si os fijáis he añadido registros de la tabla Dirección a pesar de que en el test no los utilizo. He preferido hacerlo así para enseñar el ejemplo de dos tablas relacionadas. En la práctica intentad manejar datasets lo más reducido posible,

Ventajas

Las ventajas de DBUnit son bastante evidentes. Nos permite controlar el estado de la base de datos justo antes de cada test. En ciertas pruebas esto es muy útil y permite ahorrar una buena cantidad de código. Además, a parte de crear los registros de la base de datos, DBUnit es capaz de comparar el resultado final registro a registro y columna a columna de las tablas que nosotros le indiquemos con lo que el test puede ser tan completo como nosotros deseemos. Podéis encontrar un ejemplo de esto en el howto de su pagina web: http://www.dbunit.org/howto.html.

Desventajas

Para simplificar, en este ejemplo todo es muy fácil. En la realidad las tablas tienen muchas claves foráneas y se requieren datos más complejos en el dataset que pueden empezar a crecer sin control. Por fortuna existen herramientas que son capaces de migrar los datos de la base de datos a un fichero xml que tenga la estructura requerida por DBUnit pero esto no soluciona el problema de ficheros xml muuuy grandes cargados de datos para tests.

Otro problema de DBUnit es que no es suficiente listo como para saber el orden en la que ha de hacer las inserciones, así que hemos de colocar los datos con mucho cuidado en el dataset para no encontrarnos con problemas en las claves foráneas de datos que todavía no se han insertado. Por ejemplo, cambiad el orden en el dataset del ejemplo y poned antes las direcciones que las personas. Como  la dirección hace referencia a una persona que todavía no ha sido insertada obtendremos un error de clave foránea.

Otro punto  sucede cuando intentamos rellenar algunas columnas selectivamente. DBUnit determina que columnas rellenar en base al primer registro de la tabla. Esto es, si ponemos algo así como esto:


<dataset>
	<PERSONA ID="1" VERSION="0" NOMBRE="TestPerson1" />
	<PERSONA ID="2" VERSION="0" NOMBRE="TestPerson2" EDAD="32" />
</dataset>

nos encontraremos que el primer registro insertado en la base de datos no tiene edad pero que el segundo tampoco. Esto es por lo comentado antes, si el primer registro no tiene especificado un valor de una columna no importa que el resto los tenga, no lo utiliza. Podemos solucionar esto creando un dtd de las tablas que deseemos (http://www.dbunit.org/faq.html#generatedtd) cosa que os recomiendo para ejemplos no triviales pero esto tampoco resuelve todos nuestros problemas.

En definitiva, no todo es tan fácil como parece. Tratadlo con precaución y tened muy en cuenta lo que pueda o no pueda hacer antes de introducirlo en vuestro proyecto. Bien utilizado os puede ahorrar mucho tiempo de testeo y detectar errores no triviales en tiempo de desarrollo.

Publicado en Programación | Etiquetado , , , , | 2 comentarios

¡¡No existe update en JPA!!

Perdonad las exclamaciones pero es algo que he visto miles de veces. Vas a un proyecto que utiliza JPA y te encuentras que tienen un dao con un código parecido a esto:


public Serializable update(Serializable entity) {
	return entityManager.merge(entity);
}

La gente hace un DAO y ve claramente que el método persist es para insertar y que remove es para borrar pero cuando llega al update busca y busca no encuentra nada. Entonces prueba el merge y como funciona así se queda. La realidad es que el merge no sirve para actualizar datos sino para asociar (en inglés merge) al EntityManager una entidad desasociada. Hablaré de esto en otro post pero quedaros con la idea general: Cuando insertas una entidad o la recuperas mediante una query esa entidad queda sincronizada y se miran todos sus cambios por lo que para hacer un update no hay que llamar a ningún método ya que JPA ya sabe que ha su estado ha sido alterado. Es más, si tenemos varias instancias de la misma entidad veréis que todas son el mismo objeto, que se asocial EntityManager. Eso es lo que se llama el primer nivel de cache. Así pues, el update de la base de datos se hará automáticamente al hacer el commit de la transacción.

Es decir, este método.


@Transactional
public void actualizarEdad(Long id) {
	Persona p = entityManager.getPersona(id);
	p.setEdad(45);
}

actualizará la edad de la persona a 45 en la base de datos sin necesidad de hacer un merge.

¿Para qué sirve el método merge? Puede darse el caso de que necesitemos desasociar esa entidad del EntityManager, un motivo muy común es al serializarla para pasarla a la capa vista de una aplicación. Si posteriormente queremos volver a sincronizarla para actualizar los datos entonces sí que hemos de utilizar el método merge.

Publicado en Programación, Programación-JPA | Etiquetado , | 2 comentarios

Prueba de integración

Una prueba de integración es aquella que testea todo el proceso en conjunto. En este caso el objetivo es testear todos los métodos y procesos que utiliza el programa o servicio y no “falsear” ningún dato mediante mocks o similares.

El planteamiento de un test de integración es simple. Supongamos un proceso ficticio: un proceso que lee de una base de datos una serie de registros para luego, si el registro cumple que ha sido modificado antes de una fecha en concreto, se guarda en un fichero de texto. En este caso el test de integración consistiría en lanzar el proceso completo con una base de datos que sólo tenga 3 registros: Uno con la fecha correcta para almacenarse en el fichero, otro con una fecha que no cumpla la condición y otro con una fecha nula. El resultado final a comparar sería que en el fichero que escribe el proceso sólo existe una línea con los datos del primer registro y que la fecha nula no genera un error no controlado. No es necesario llenar la base de datos de miles de registros ya que
estos no aportarán nada a la prueba.

Un test de integración no implica que no se hayan de utilizar test unitarios y viceversa. Los dos tipo de test son necesarios dentro del ciclo de desarrollo de una aplicación.

Publicado en Glosario | Deja un comentario

Spring y JPA (3 – Configuración)

Primera parte
Segunda parte

Primero vamos por la configuración. Dentro de src/test/resources tenemos los ficheros de configuración para que funcione Spring e Hibernate.

La configuración de Hibernate es tan simple como poner un fichero persistence.xml dentro de un directorio META-INF. Puesto que esa es la localización y nombre por defecto no hay que indicarle dónde buscar.

El contenido del fichero se reduce, quitando cabeceras, a


<persistence-unit name="cafebabePersistenceUnit" transaction-type="RESOURCE_LOCAL">
</persistence-unit>

donde le indicamos el nombre de nuestra unidad de persistencia “cafebabePersistenceUnit” y el tipo de transacción que vamos a utilizar.

El nombre simplemente es un simbolismo a la base de datos. Si tuviesemos dos bases de datos diferentes en nuestra aplicación habrían de tener un nombre diferente. También podríamos tener una base de datos única pero separada con diferentes unidades de persistencia. Como estos casos no son comunes la mayor parte de las veces no vamos hacer nada con esto.

Existen dos tipos de transacciones RESOURCE_LOCAL y JTA (Java Transaction API). Según la especificación JSR-317, RESOURCE_LOCAL es el tipo de transacción que se ha de utilizar en una aplicación JAVA SE y JTA en una JAVA EE. Esto siempre que estemos utilizando un patrón EJB, claro, cosa que en esto ejemplo no sucede (utilizamos Spring). En una aplicación con Spring, RESOURCE_LOCAL se utiliza siempre a menos que tengamos aplicaciones distribuidas, en cuyo caso utilizaremos JTA.

Continuamos con la configuración de Spring.

El fichero GenericDaoTest-context.xml se llama igual, más el -context.xml, que la clase que tiene el test con la que lo leeremos (GenericDaoTest), de esta manera no hay que especificarlo manualmente dentro del @ContextConfiguration de la clase.

Podemos dividir el fichero en dos partes, la primera de configuración de Spring


<context:component-scan base-package="cafebabe.test" />
<context:annotation-config />
<context:property-placeholder location="classpath:/cafebabe/test/jpa/properties/jdbc.properties" />

<tx:annotation-driven />

Donde, por orden, decimos a Spring que al empezar busque todas las clases que cuelguen del paquete cafebabe.test (subpaquetes incluídos) en busca de clases que haya de gestionar. Spring gestionará todas las clases que tengan las anotaciones @Component, @Repository, @Service o @Controller.

La segunda línea le comunicamos que trabajaremos la inyección de dependencias con anotaciones (sino las anotaciones del tipo @Autowired o @Resource no funcionarían).

En la tercera decimos que tenemos un fichero de propiedades (el jdbc.properties ) que se supone vamos a leer para despues proporcionarle la información mediante una expression language (la típica ${nombrepropiedad})

Y por último, la cuarta línea indica que vamos a manejar las transacciones mediante anotaciones. De no ponerlo el @Transactional no haría nada (ni lanzar una excepción para avisar de que no va).

El segundo bloque es donde configuramos el acceso al JPA.


<bean id="dataSource"
	class="org.springframework.jdbc.datasource.DriverManagerDataSource"
	p:driverClassName="${jdbc.test.driverClassName}" p:url="${jdbc.test.url}"
	p:username="${jdbc.test.user}" p:password="${jdbc.test.password}" />

<bean id="jpaAdapter"
	class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
	p:database="${jpa.test.database}" p:showSql="${jpa.test.showSql}"
	p:generateDdl="${jpa.test.generateDdl}" />

<bean id="entityManagerFactory"
	class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
	p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter"
	p:persistenceUnitName="cafebabePersistenceUnit" />

<bean id="transactionManager"
	class="org.springframework.orm.jpa.JpaTransactionManager"
	p:entityManagerFactory-ref="entityManagerFactory" />

En la primera línea, dataSource, especificamos la conexión a la base de datos (driver, dirección, usuario y password)

En la segunda, jpaAdapter, especificamos qué implementación de JPA vamos a utilizar (Hibernate, en nuestro caso). Configurar el adaptador de JPA no siempre es necesario pero nos servirá para añadir cierta configuración al funcionamiento de JPA. En nuesto le decimos la base de datos que vamos a atacar para que Hibernate utilice el dialecto adecuado, si queremos que se cree la base de datos al vuelo en caso de que se detecte que falten tablas (generateDdl) y si queremos que se muestren las queries en la traza de log (showSql). ¿Por qué puede no ser realmente necesaria esta línea? Porque esta información la podemos añadir también en el fichero persistence.xml de la configuración de JPA. Como no lo hemos hecho allí, lo hacemos aquí.

La tercera línea, entityManagerFactory, configura la factoria que nos irá proporcionando los objetos de la clase EntityManager a medida que los vayamos necesitando. Como dejamos que sea Spring la encargada de ir haciendo esto le tenemos que configurar el bean.

Por último configuramos la transacción, transactionManager, y le asociamos el entityManagerFactory.

Finalmente queda el fichero del log (logback.xml) que necesariamente ha de estar en la raiz de src/test/resources y con la configuracíon de la traza que queremos, WARN para todos excepto para nuestras clases que será en DEBUG.

Vamos ahora con la clase GenericDaoTest.

La anotacion @RunWith(SpringJUnit4ClassRunner.class) indica que la prueba se va a gestionar con la clase SpringJUnit4ClassRunner de Spring que nos proprciona soporte al contexto de Spring dentro del test.

@ContextConfiguration tan sólo es una anotación donde especificarle la ruta y nombre del fichero de configuración. Si está vacío, como ahora, irá a buscarlo al mismo directorio de la clase test (aunque una esté en resources y la otra en java) y con el nombre de la clase. De querer apuntar a otro lugar habríamos de utilizar algo así:


@ContextConfiguration(locations={"ruta/fichero1.xml", "ruta/fichero2.xml"})

La anotación @Autowired inyectará la dependencia al Dao. Esto es, de manera automática, el objeto genericDao estará informado con una instancia de la clase GenericDao (sin necesidad de métodos sets o gets). Esto es así porque previamente hemos puesto la anotación @Repository en la cabecera de dicha clase y está dentro de la ruta a buscar por el component-scan que expliqué más arriba.

Por último la anotación @Test indican que dicho método es un test independiente y la anotación @Transactional hará que exista una transacción y que una vez terminado el test se haga un rollback automático. Esto último es importante porque hace que no se modifique el estado de la base de datos. Esto es, una vez terminado el test, si comprobásemos la base de datos, veríamos que no hay nada, las tablas están vacías o tal como las dejamos al iniciar el test. Ojo, las tablas que se creen no podrán ser borradas ya que no se puede hacer rollback de instrucciones del tipo CREATE TABLE o ALTER TABLE. En nuestro caso, este ejemplo está configurado para trabajar con una base de datos en memoria por lo que esto no es realmente necesario (la base de datos se esfuma cuando se termina el test) pero es muy útil para ciertas pruebas que se realizan en bases de datos físicas que queremos mantener sin cambios.

Por último el test en sí:


List<Persona> resultados = genericDao.getAll(Persona.class);

Assert.assertEquals(0, resultados.size());

Persona p = new Persona();
p.setNombre("Test");
p.setEdad(33);

Assert.assertEquals(0, p.getId());

genericDao.insert(p);
resultados = genericDao.getAll(Persona.class);

Assert.assertEquals(1, resultados.size());

Creo que se entiende fácilmente. Se comprueba que no hay ningún registro en la tabla Persona, se añade un registro y se comprueba que, ahora sí, existe un registro. Esto no es una prueba muy bien realizada ya que deberíamos validar que el registro es el mismo que hemos insertado pero como ejemplo ya vale.

Publicado en Tutorial, Tutorial-Hibernate, Tutorial-Spring | Etiquetado , , , | Deja un comentario