Tag Archives: JPA

Creación de DAOs con Spring JPA

En este post vamos a hacer uso de Spring Data y de JPA estándar para crear dos DAOs diferentes que nos permitirán acceso a una base de datos en memoria para realizar operaciones CRUD tradicionales.

Se usará como parte del suite de Spring:

- Spring Boot, para inicializar y preconfigurar la aplicación con Spring, asi como para hacer uso del Tomcat y de la base de datos HSQLDB embebidos.

- Spring JPA, para implementar uno de los casos de acceso a datos.

1. Dependencias Maven

Partimos del ejemplo de uso de Spring Boot presentado en anteriores post. A partir de él, añadimos las siguiente dependencias al pom.xml, para incluir el uso de Spring Data:

2. Dominio de la aplicación

Implementamos el modelo de datos de la aplicación usando POJOs clásicos. A estos les agregamos las anotaciones JPA tradicionales para establecer que se trata de elementos del dominio de la aplicación. En este caso creamos una sola entidad (Superhero). Esta entidad se verá mapeada automaticamente contra una tabla relacional del mismo nombre dentro de la base de datos en memoria que inicia Spring Boot en su arranque (HSQLDB).

La única anotación JPA, aparte de la antes mencionada @Entity, que se usa en el POJO sirve para indicar cuál es la clave primaria de nuestra entidad (@Id). Como no se usan anotaciones para indicar mapeos a columnas, se mapean los atributos a columnas con el mismo nombre que los atributos.

Las anotaciones @CreatedDate y @CreateBy son anotaciones de la parte de auditoría de Spring Data. Simplemente indican que campos se usarán para generar información de auditoría sobre la entidad, en este caso la fecha de creación y el usuario que lo creo. Más adelante se completará la auditoría de Spring Data con un listener que detectará los cambios o la creación de las entidades y completará los valores de los campos de auditoría, en particular el usuario que crea o modifica el registro. El campo de fechas es compatible con un tipo DateTime estándar de Java 8.

2. Los DAOs

Creamos dos tipos de DAOs diferentes. La forma tradicional y recomendable de crear estos componentes es codificar su interfaz de métodos públicos y posteriormente desarrollar una clase que implemente dicha interfaz. De esta manera desacoplamos la funcionalidad pública del DAO respecto de su implementación. Por comodidad y rapidez, y porque la implementación del DAO usando Spring Data solo necesita de la interfaz, en este caso usaremos una interfaz para el DAO hecho con Spring Data y una clase estándar para el DAO implementado con JPA, pero este último podria tener su propia interfaz o implementar la interfaz de Spring Data con sus propios métodos.

- DAO de Spring Data

Basta con crear una interfaz y extenderla de la interfaz Repositoy o CrudRespository. Ademas se indica en el genérico la clase que implementa la entidad que se procesa con esta interfaz (Superhero). El String es el tipo del ID de la entidad (el nombre en este caso).

- DAO de JPA

El DAO implementado con JPA desde Spring es una implementación clásica de este tipo de desarrollos. Primero se anota la clase con @Repository. Esto indica a Spring que la clase implementa un DAO. Dentro del DAO se añade una referencia al EntityManager de JPA que se usa para acceder a los métodos de acceso a datos. Para vincular este EntityManager exclusivo de este DAO con el EntityManagerFactory y la estructura de acceso a datos de JPA necesaria, se anota con @PersistenceContext. De esta manera Spring se encargará de crear estas conexiones de forma automática (y se añaden los métodos set/get correspondientes).

Una vez hecho esto, se puede usar el entityManager para acceder a los métodos JPA que se pueden usar para manipular los datos, como por ejemplo para acceder a ellos mediante una query en JPQL, usando entidades y lenguaje orientado a objetos, como en el ejemplo. En este caso, la implementación de JPA corre a cargo de Hibernate, ya que se incluye automaticamente con Spring Boot y Spring Data.

3. Auditoría

En el punto 1, en relación al POJO y al modelo de datos, incluimos dentro del mismo unos atributos y unas anotaciones especiales de Spring Data que permiten generar información auditable a la hora de determinar las fechas de creación, modificación y los usuarios que crean o modifican los registros.

La parte que falta en relación a la auditoría sería la clase que detecta estas modificaciones e incorpora la información que falta respecto al usuario que crear o modifica el registro (ya que las fechas de creación y modificación se rellenarían de forma automática). Esta clase sería un listener y se implementaría tal que así:

La clase en cuestion implementa la interfaz AuditorAware y devuelve un String en el método getCurrentAuditor(). Será la implementación de este método la que habra que adaptar a cada caso, en este ejemplo, lo más sencillo es devolver siempre el mismo String pero lo normal es que se devuelva el login o el id del usuario que este logado o que se encuentre activo en ese momento.

Aún falta añadir este listener a la configuración de la aplicación. Esto lo vemos a continucación.

4. Configuración

Tal y como se hace con Spring Boot de forma habitual, en este caso la configuración es via Java, mediante una clase anotada a tal efecto:

Se indica que la clase se usará para configurar (@Configuration). Se activa la configuración automática que habilitará la conexión a la base de datos embebida y el entityManagerFactory asociado a la misma (@EnableAutoConfiguration). Por último, se habilira la auditoría de Spring Data con @EnableJpaAuditing. Además declaramos un bean (@Bean) con nombre auditorProvider que devuelve una instancia del listener de auditoría que se expone en el punto anterior.

Además usamos el bean “demo” para crear 3 registros nada más arrancar el aplicativo. Hacemos uso del DAO creado con Spring Data que contiene métodos para insertar nuevas entidades.

4. Servicios web

Para probar el uso de estos DAOs utilizamos un controlador web como el que se ha visto en post anteriores. Se usa una URL para invocar al método que devuelve todos los registros de Superhero del DAO implementado con Spring Data, y otra URL para invocar al mismo método del DAO implementado con JPA. El resultado debería ser el mismo y ambos deberían retornar registros.

5. Conclusión

Lanzando la aplicación y accediendo a ambas URLs se ve como se retornar los mismos registros y además en los logs se aprecia como en cada caso se entra por un DAO diferente.

DAO con Spring Data

DAO con Spring Data

DAO con JPA

DAO con JPA

Log cuando se accede al DAO JPA

Log cuando se accede al DAO JPA

Se puede preparar una aplicación con acceso a datos con Spring Boot y Spring Data y/o JPA rápida y facilmente y respetando los principios de diseño y patrones estándar más habituales. Este es el enlace para descargar el proyecto entero (Spring Data + Spring Batch). Y también en Github.

Selección de datos en JPA II

El siguiente ejemplo de JPA crea una selección de datos estándar, que retorna una serie de columnas para una query determinada.

1. Se crea la query y el FROM sobre el PO principal de la misma. La claúsula es sobre el PO, pero la query retornará una lista de DTOs. El PO esta mapeado directamente al modelo en BBDD, mientras que el DTO es una representación con tipos primitivos y clases ‘wrapper’, del mismo.

/* Criteria builder */
CriteriaBuilder criteria = getEntityManager().getCriteriaBuilder();
CriteriaQuery<DonutDTO> query = criteria.createQuery(DonutDTO.class);
Root<DonutPO> root = query.from(DonutPO.class);

2. Se añaden los predicados de la claúsula WHERE. Aquí, ‘cream’ es un atributo de DonutPO, mientras que ‘toppingPO’ es asimismo un atributo de esta clase que representa una relación 1 a n, siendo ‘id’ el atributo PK de su clase PO.

Predicate conditions = criteria.or(
criteria.equal(root.get("toppingPO").get("id"), topping),
criteria.equal(root.get("cream"), cream));
query.where(conditions);

3. Se establecen los campos que se devolverán en la query. Se pueden solicitar campos del PO principal (DonutPO) o de aquellos que estén relacionados mediante su PO correspondiente (ToppingPO). Para ello, el DTO de destino ha de tener un constructor con los campo en el mismo orden en el que se establecen aquí. En este caso, debe existir un constructor con 2 campos, uno para “name” y otro para “flavour”.

List<Selection> listFields = new ArrayList<Selection>();
Path<?> id = root.get("name");
listFields.add(id);
Join<?, ?> joinTopping = root.join("toppingPO", JoinType.LEFT);
Path<?> toppingName = joinTopping.get("flavour");
criteria.multiselect(listFields.toArray(new Selection[0]));

4. Se ejecuta la query.

/* Execute query */
List<DonutPO> donutList = getEntityManager().createQuery(query).getResultList();

Selección de datos en JPA I

A continuación se presenta un sencillo ejemplo de como implementar una selección en JPA usando la clase CriteriaBuilder. Esto tiene la ventaja de que podemos implementar una selección de datos en Java sin teclear ni una sola query, ni en SQL ni en ningún otro lenguaje de consulta, haciedola totalmente independiente de la capa de datos y omitiendo la necesidad de almacenar y mantener dichas queries.

1. Se crea el objeto ‘query’ y la representación de la clausula FROM sobre el elemento del modelo de datos principal de nuestra claúsula.

CriteriaBuilder cb = this.getEntityManager().getCriteriaBuilder();
CriteriaQuery<Long> countTransaction = cb.createQuery(Long.class);
Root<BosTransactionsPO> root = countTransaction.from(BosTransactionsPO.class);

2. Se establecen los predicados de la clausula WHERE.

/* Predicates */
List<Predicate> predicates = new ArrayList<Predicate>();
Long plazaId = itineraryArg.getTollPlazaId();
if (plazaId != null) {
Predicate p = cb.equal(root.get("tollPlazaPO").get("id"), plazaId);
predicates.add(p);
}
countTransaction.where(predicates.toArray(new Predicate[0]));

3. Se establecen lo que se retornará en la selección. En este caso, es un SELECT COUNT(*).

countTransaction.select(cb.count(root));

4. Se ejecuta la query.

/* Execute query */
Long count = getEntityManager().createQuery(countTransaction).getSingleResult();