Singleton

Un Singleto es un patrón de diseño que asegura que solo exista una instancia de una clase para toda la aplicación, limitando la creación de objetos mediante un constructor privado y proveyendo un acceso global a esa instancia única. Por ejemplo, la implementación más sencilla podría verse así:

public class MiSingleton {
  private final static MiSingleton instancia = new MiSingleton();
  private MiSingleton(){}
  public static MiSingleton getSingleton() {
    return instancia;
  }
}

Este patrón se ha popularizado para referenciar entidades únicas, como contextos de aplicación en frameworks como JavaServer Faces, ahora Jakarta Faces, o Spring, o para emular clases globales en casos como el manejo de logs.

Sin embargo, una limitación importante del patrón Singleton es que la «unicidad» de la instancia se garantiza solo dentro del ámbito de un cargador de clases (classloader). En aplicaciones de escritorio simples, esto no suele representar un problema. No obstante, en el contexto de aplicaciones empresariales desplegadas en servidores de aplicaciones JEE o arquitecturas OSGI, que pueden operar con múltiples classloaders, esta característica puede llevar a situaciones donde existan múltiples instancias del mismo «Singleton», complicando el mantenimiento y la depuración.

Además, es importante diferenciar los Singletons manejados por frameworks como Spring, donde la instancia única se define por el contexto de la aplicación y no por el classloader. Esto suele ser adecuado para la mayoría de las necesidades empresariales, haciendo recomendable delegar la gestión de Singletons a estas tecnologías en lugar de implementarlos manualmente.

En entornos donde las limitaciones del patrón Singleton se vuelven relevantes, se pueden considerar alternativas o patrones de diseño complementarios, como la inyección de dependencias, que ofrecen una mayor flexibilidad y coherencia en la gestión de instancias únicas, especialmente en aplicaciones complejas.

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 solo 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 está mal por algo que el 99.9% de programadores que conozco no se han molestado en aprender, el modificador volatile.

¿Cuál 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 solo pasa si las variables inicializadas en el constructor no son final. Un constructor que inicializa variable 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 de 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

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

Al añadir una sección de libros en mi web, tenía claro que «Effective Java» de Joshua Bloch sería el primero en ser analizado, esperando ofrecer un vistazo detallado y personal sobre él.

Para aquellos menos familiarizados, Joshua Bloch es ampliamente reconocido como uno de los expertos más influyentes en Java, siendo el creador de la API java.util.Collection. Su trabajo no solo es un ejemplo de código bien ejecutado, estructurado y documentado, sino que también marca un hito en su carrera, especialmente notable cuando dejó Sun Microsystems para unirse a Google, donde sigue aportando a la comunidad.

«Effective Java» no es simplemente otro libro sobre Java; es, sin duda, uno de los textos más vendidos y probablemente el más útil en su campo. Está dirigido a desarrolladores con conocimientos básicos de Java y se centra en la creación de código eficiente y práctico. A través de 10 secciones principales que abarcan desde la creación y destrucción de objetos hasta la concurrencia y la serialización, Bloch desgrana los errores comunes y ofrece consejos prácticos para evitarlos.

Destaco, por ejemplo, la sección sobre excepciones, que con sus 9 recomendaciones ilumina un área a menudo nebulosa. Cada ítem, sin importar la sección, es presentado de manera didáctica y clara, sin caer en la trampa de añadir contenido superfluo.

En resumen, cada uno de los 78 ítems del libro es una destilación de sabiduría práctica, presentada de manera concisa, usualmente no extendiéndose más allá de cuatro páginas. Incluso los temas más complejos, como «Favorecer la composición sobre la herencia», se tratan sin que se sienta como relleno.

Podría seguir elogiando «Effective Java» indefinidamente, pero prefiero concluir compartiendo una nota personal: este libro fue la primera recomendación que hice a alguien especial, quien ahora es mi pareja. En cierta manera, las virtudes de este libro son, para mí, infinitas.

Publicado en Libros | Deja un comentario

¡¡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 este:

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

La gente crea 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 y no encuentra nada. Entonces prueba el merge y, como funciona, así se queda. La realidad es que 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 registran 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 su estado ha sido alterado. Es más, si tenemos varias instancias de la misma entidad, veréis que todas son el mismo objeto asociado al EntityManager. Eso es lo que se llama el primer nivel de caché. 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.find(Persona.class, 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 evalúa todo el proceso en su conjunto. Su objetivo principal es verificar todos los métodos y procesos empleados por el programa o servicio, sin recurrir a la «simulación» de datos mediante mocks u otros métodos similares.

El enfoque de una prueba de integración es directo. Consideremos un proceso hipotético: un procedimiento que consulta de una base de datos una serie de registros para, posteriormente, si se determina que el registro fue modificado antes de una fecha específica, almacenarlo en un archivo de texto. En este contexto, la prueba de integración implicaría ejecutar el proceso completo utilizando una base de datos que contenga únicamente tres registros: uno con la fecha adecuada para ser almacenado en el archivo, otro con una fecha que no satisface la condición y un último con una fecha nula. El resultado esperado sería que el archivo generado por el proceso contenga únicamente una línea con la información del primer registro, y que la fecha nula no provoque un error no gestionado. No es necesario sobrecargar la base de datos con miles de registros, puesto que estos no contribuirían significativamente al objetivo de la prueba.

Realizar pruebas de integración no excluye la necesidad de llevar a cabo pruebas unitarias, y viceversa. Ambos tipos de pruebas son fundamentales dentro del ciclo de desarrollo de cualquier aplicación.

Publicado en Glosario | Deja un comentario

TDD

Test-driven development o Desarrollo guiado por pruebas, en lengua común. Es la manera de programar que le da una importancia capital a las pruebas. Los puristas escriben primero una prueba unitaria, posteriormente escriben el código y validan que supera la prueba escrita anteriormente. Una vez hecho esto se puede entrar en el ciclo de depurar y simplificar el código comprobando siempre que la prueba unitaria se pasa correctamente tras cualquier modificación. Yo, que debo ser un bicho raro, escribo primero el código y luego la prueba unitaria, ya que hacer una prueba unitaria de un código que no existe me parece algo raro.

Tiene la ventaja de crear porciones de código muy pequeño, manejable, fiable y fácilmente mantenible.

En caso de que se encuentre un error en el código ya creado, primero (importante) se escribe la prueba unitaria que reproduzca el error. Una vez reproducido, se corrige y se verifica que la prueba da, ahora sí, un resultado correcto.

Publicado en Glosario | Deja un comentario

DAO

Data Access Object, u Objeto de Acceso a Datos explicado de manera simple, es la clase encargada de acceder a la base de datos, aislando al resto de la aplicación de esta responsabilidad. Así, se logra separar la base de datos, una parte crítica de la aplicación, haciéndola transparente para el resto de las clases. Esto no solo facilita el mantenimiento y la optimización, sino que también, en caso necesario, permite crear implementaciones distintas para adaptarse a diferentes tipos de bases de datos, mejorando así la flexibilidad y la capacidad de prueba del software.

Publicado en Glosario | Deja un comentario

Spring y JPA (2 – Proyecto)

Primera parte

Una vez tenemos creado el proyecto y las dependencias vamos a empezar a añadir código.

Lo primero es añadir las clases que nos permitan acceder a la base de datos. Para ello basaremos el proyecto en un diseño basado en un solo DAO. Desde este centralizaremos todo el acceso a la base de datos para los casos más comunes (insertar, borrar, buscar…). Si en algún momento necesitamos algo más complejo podemos crear una clase que extienda de nuestro DAO. Otra cosa que vamos a crear es una clase sobre la cual extiendan todas las entidades que vayamos creando. Esta clase tan sólo tendrá algo común a todas las entidades: una variable version para el optimist lock y una variable id para la clave primaria.

Vamos a crear las clases según el diseño de proyecto de la imagen de la izquierda. Como soy un fanático del TDD no puedo evitar diseñar el proyecto de manera algo extraña para los no iniciados (¿dónde está el main?)

Vamos por el DAO. En la carpeta src/java/main creamos el paquete cafebabe.test.jpa y en su interior la clase sobre la que heredarán todas nuestras entidades:

package cafebabe.test.jpa;

import java.io.Serializable;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;

@MappedSuperclass
public abstract class AbstractEntity implements Serializable{

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue
	private long id;

	@Version
	private long version;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public long getVersion() {
		return version;
	}

	public void setVersion(long version) {
		this.version = version;
	}
}

A continuación, en el mismo paquete, creamos nuestra clase de acceso a la base de datos, GenericDao, con el siguiente código:


package cafebabe.test.jpa;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaQuery;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

@Repository
public class GenericDao {

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

	@PersistenceContext(unitName = "cafebabePersistenceUnit")
	private EntityManager em;

	public <T extends AbstractEntity> void delete(T entity) {
		log.debug("Eliminando la entidad::Clase={}::Id={}", entity
				.getClass().getCanonicalName(), entity.getId());
		em.remove(entity);
	}

	public <T extends AbstractEntity> void insert(T entity) {
		log.debug("Insertando una nueva entidad::Clase={}", entity
				.getClass().getCanonicalName());
		em.persist(entity);
		log.debug("Se le ha asignado la Id={}", entity.getId());
	}

	public <T extends AbstractEntity> T getByPrimaryKey(Class<T> entityClass, Object id) {
		log.debug("Buscando por clave primaria::Clase={}::Id={}", entityClass, id);
		return em.find(entityClass, id);
	}

	public <T extends AbstractEntity> List<T> getAll(Class<T> entityClass) {
		log.debug("Buscando todos los registros de la clase::Clase={}", entityClass);
		CriteriaQuery<T> cq =  em.getCriteriaBuilder().createQuery(entityClass);
		cq.from(entityClass);
		return em.createQuery(cq).getResultList();
	}
}

Y ya está, no tenemos que hacer nada más. La pregunta, claro, es ¿pero esto funciona? Evidentemente no, aquí no hay nada que ejecute código así que vamos a crear un test que haga funcionar nuestro DAO.

Vamos a pensar un ejemplo simple que luego podemos ir complicando. Tenemos una tabla Persona donde se guardan datos de usuarios o clientes ( nombre, edad… ) y una tabla Direccion donde se guardan direcciones (calle, código postal). Una persona puede tener muchas direcciones (relación 1 a n) ya que tenemos a algún cliente ricachón con una casa en la playa y otra en la montaña para las vacaciones. En base a este modelo vamos a construir las entidades.

Vamos al directorio /src/test/java, creamos el paquete cafebabe.test.jpa y la persona:

package cafebabe.test.jpa;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.OneToMany;

import cafebabe.test.jpa.AbstractEntity;

@Entity
public class Persona extends AbstractEntity {

	private static final long serialVersionUID = 1L;

	@OneToMany(mappedBy = "id")
	private Set<Direccion> direcciones;

	private int edad;

	@Column(nullable = false)
	private String nombre;

	public Set<Direccion> getDirecciones() {
		return direcciones;
	}

	public int getEdad() {
		return edad;
	}

	public String getNombre() {
		return nombre;
	}

	public void setDirecciones(Set<Direccion> direcciones) {
		this.direcciones = direcciones;
	}

	public void setEdad(int edad) {
		this.edad = edad;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}
}

y ahora la dirección:

package cafebabe.test.jpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;

import cafebabe.test.jpa.AbstractEntity;

@Entity
public class Direccion extends AbstractEntity {

	private static final long serialVersionUID = 1L;

	@Column(nullable = false)
	private String calle;

	@Column(length = 5, nullable = false)
	private String codigoPostal;

	@ManyToOne
	private Persona persona;

	public String getCalle() {
		return calle;
	}

	public String getCodigoPostal() {
		return codigoPostal;
	}

	public Persona getPersona() {
		return persona;
	}

	public void setCalle(String calle) {
		this.calle = calle;
	}

	public void setCodigoPostal(String codigoPostal) {
		this.codigoPostal = codigoPostal;
	}

	public void setPersona(Persona persona) {
		this.persona = persona;
	}
}

Y ahora, al fin, vamos a crear un pequeño test que utilice todo esto. En el mismo paquete que la clase Persona y Direccion creamos la clase GenericDaoTest que no es más que una prueba unitaria que valida que estemos insertando una persona en la base de datos:

package cafebabe.test.jpa;

import java.util.List;

import org.junit.Assert;
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.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import cafebabe.test.jpa.GenericDao;

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

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

	@Autowired
	private GenericDao genericDao;

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

		List 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());
		log.info("Fin del test de inserción");
	}
}

Esto es todo el código que necesitamos. Ahora sólo hemos de añadir los ficheros de configuración. Empezamos con la configuración de Spring:


<?xml version="1.0" encoding="UTF-8">
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" >

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

	<tx:annotation-driven >

	<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.generatDdl}" >

	<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" >

</beans>

Como podemos ver he externalizado las propiedades de acceso a la base de datos para que se pueda modificar sin necesidad de modificar los xml, así que también hemos de poner el susodicho fichero de propiedades. Lo añado con dos configuraciones, una con HSQLDB que ya funciona sin tener que instalar ninguna base de datos y otra, comentada, para MySQL en caso de que queráis entrar más a trapo e ir viendo lo que se va creando en una base de datos física.

#jdbc.test.driverClassName=com.mysql.jdbc.Driver
#jdbc.test.url=jdbc:mysql://localhost:3306/test
#jdbc.test.user=root
#jdbc.test.password=admin
#jpa.test.database=MYSQL
#jpa.test.showSql=true
#jpa.test.generateDdl=true

## HSQLDB
jdbc.test.driverClassName=org.hsqldb.jdbcDriver
jdbc.test.url=jdbc:hsqldb:mem:mypersistence
jdbc.test.user=sa
jdbc.test.password=
jpa.test.database=HSQL
jpa.test.showSql=false
jpa.test.generateDdl=true

Seguimos con la configuración de JPA:


<?xml version="1.0" encoding="UTF-8">
<persistence 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"
	version="2.0">

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

</persistence>

Y terminamos con la configuración del log:


<?xml version="1.0" encoding="UTF-8">

<configuration>
	<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>%d{yyyy-MM-dd HH:mm:ss} - %c{1} [%p] %m%n</pattern>
		</encoder>
	</appender>

	<logger name="cafebabe.test" level="DEBUG" >
	<root level="WARN">
		<appender-ref ref="Console" />
	</root>
</configuration>

Y ahora sí que esto ya está. Para ejecutarlo podemos ir a la clase GenericDaoTest, apretar el botón derecho y seleccionar Run As -> JUnit Test. Al ejecutarse HSQLDB creará una base de datos en memoria (por lo que no hace falta una física tipo Oracle o MySql) y se crearán las tablas Persona y Dirección. Si lo deseáis, podéis ver las trazas de todo esto cambiando el nivel de log de WARN a DEBUG.

Una vez lo tenemos funcionando, vamos a analizarlo con detalle.

Detalle para los puristas: Lo que he hecho no es un test unitario sino un test de integración. Lo sé.

Tercera parte

Publicado en Programación-JPA, Programación-Spring, Tutorial | Etiquetado , | Deja un comentario

Cafebabe

Si abrimos cualquier archivo .class compilado de Java con un editor hexadecimal, observaremos que los primeros valores forman la palabra CAFEBABE. Este número hexadecimal mágico sirve para identificar todas las clases Java, actuando como una firma que verifica su autenticidad y compatibilidad.

Publicado en Glosario | Deja un comentario

Prueba unitaria

Un test unitario se enfoca en verificar una única funcionalidad del código, lo que justifica su denominación. Un ejemplo ilustrativo sería calcular el pago mensual de una hipoteca.

Para diseñar adecuadamente un test unitario, es crucial conocer de antemano el resultado esperado de la funcionalidad a verificar. Por ejemplo, para cubrir el pago de un piso de 200.000 € a 240 meses con un interés del 2.5%, deberíamos pagar 1095.10 € al mes. Conociendo esto, invocamos al método con los parámetros correspondientes (200.000, 240, 2.5) y, si el método devuelve el resultado esperado, consideramos que la prueba ha sido superada con éxito.

De esta forma, si en algún momento se modifica la funcionalidad para añadir o cambiar alguna parte del código, podemos estar razonablemente seguros de que nuestra prueba detectará cualquier posible error.

La librería más utilizada para realizar estas pruebas unitarias en Java es JUnit.

Publicado en Glosario | Deja un comentario