12. [TD]: Implementación de la capa [DAO] de TD con [Spring Data]
Palabras clave: arquitectura multicapa, Spring, inyección de dependencias, API JPA (Java Persistence API), Spring Data.
Seguiremos el mismo procedimiento que antes para implementar la capa [DAO] del TD.
12.1. Support
![]() |
- en [1]; la carpeta [support / chap-12] contiene el proyecto Eclipse de este capítulo;
12.2. El proyecto de Eclipse
El proyecto de Eclipse será el siguiente:
![]() |
- [elections.dao.config]: contiene la clase de configuración del proyecto Spring;
- [elections.dao.entities]: contiene las entidades JPA, así como la clase de excepciones del proyecto;
- [elections.dao.repositories]: contiene las interfaces [CrudRepository] para las tablas [CONF] y [LISTES];
- [elections.dao.service]: contiene la implementación de la capa [DAO]. Es esta la que tenemos que escribir;
- [elections.dao.console]: contiene una clase de prueba del tipo [console];
- [pom.xml]: el archivo de configuración del proyecto Maven;
Este proyecto implementa la siguiente arquitectura:
![]() |
La capa [DAO] solo ve la capa implementada por [Spring Data].
12.3. Configuración de Maven
Tarea a realizar: compila el archivo [pom.xml] del proyecto.
12.4. Las entidades de la capa [JPA]
![]() |
![]() |
- [ElectionsConfig] es el modelo de objetos asociado a una fila de la tabla [CONF];
- [ListeElectorale] es el modelo de objeto asociado a una fila de la tabla [LISTES];
- [AbstactEntity] es la clase padre de las dos clases anteriores. Agrupa los campos [id, version] comunes a ambas clases;
- [ElectionsException] es la clase de excepción del proyecto;
12.4.1. La clase [ElectionsException]
![]() |
La clase [ElectionsException] es la excepción no controlada descrita en el apartado 4.3.
12.4.2. La clase [AbstractEntity]
![]() |
La clase [AbstractEntity] es la siguiente:
package elections.dao.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@MappedSuperclass
public abstract class AbstractEntity implements Serializable{
private static final long serialVersionUID = 1L;
// propiedades
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
protected Long id;
@Version
@Column(name = "VERSION")
protected Long version;
// constructores
public AbstractEntity() {
}
public AbstractEntity(Long id, Long version) {
this.id = id;
this.version = version;
}
// redefinición de [equals] y [hashcode]
@Override
public int hashCode() {
return (id != null ? id.hashCode() : 0);
}
@Override
public boolean equals(Object entity) {
if (!(entity instanceof AbstractEntity)) {
return false;
}
String class1 = this.getClass().getName();
String class2 = entity.getClass().getName();
if (!class2.equals(class1)) {
return false;
}
AbstractEntity other = (AbstractEntity) entity;
return id != null && this.id == other.id;
}
// firma jSON
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// getters y setters
...
}
Se trata de la clase descrita en el apartado 11.3.5.2, sin los filtros jSON. En este caso, las tablas [CONF] y [LISTES] no están vinculadas por una relación de clave externa. Sin embargo, es precisamente la existencia de dicha relación con el modo [lazy loading] lo que hace necesarios los filtros jSON.
12.4.3. La clase [ElectionsConfig]
![]() |
La clase [ElectionsConfig] es la entidad JPA asociada a la tabla [CONF];
Tarea: crea la clase [ElectionsConfig].
12.4.4. La clase [ListeElectorale]
![]() |
La clase [ListeElectorale] es la entidad JPA asociada a la tabla [LISTES];
Tarea: crea la clase [ListeElectorale].
12.5. La capa [Spring Data]
![]() |
Tarea: escribe las dos interfaces de [Spring Data] para gestionar las dos tablas [CONF] y [LISTES];
12.6. La capa [DAO]
![]() |
La interfaz [IElectionsDao] de la capa [DAO] es la siguiente:
package dao.service;
import dao.entities.ElectionsConfig;
import dao.entities.ListeElectorale;
public interface IElectionsDao {
// configuración de la elección
public ElectionsConfig getElectionsConfig();
// listas de candidatos
public ListeElectorale[] getListesElectorales();
// actualización de las listas de candidatos
public void setListesElectorales(ListeElectorale[] listesElectorales);
}
Tarea: escribe la implementación [ElectionsDaoJpa] de la interfaz [IElectionsDao].
12.7. Configuración del proyecto Spring
La clase [DaoConfig] configura el proyecto Spring.
Tarea: escribe la clase [DaoConfig]
12.8. La capa [console]
![]() |
![]() |
La clase [Main] es la siguiente clase ejecutable:
package dao.console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import dao.config.AppConfig;
import dao.entities.ElectionsConfig;
import dao.entities.ListeElectorale;
import dao.service.IElectionsDao;
public class Main {
// fuente de datos
private static IElectionsDao dao;
public static void main(String[] args) {
// recuperación del contexto de Spring
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// Recuperación de la fuente de datos
dao = ctx.getBean(IElectionsDao.class);
// contenido de las dos tablas
ElectionsConfig electionsConfig = dao.getElectionsConfig();
ListeElectorale[] listes = dao.getListesElectorales();
// visualización
System.out.println(String.format("Nombre de sièges à pourvoir : %d", electionsConfig.getNbSiegesAPourvoir()));
System.out.println(String.format("Seuil électoral : %5.2f", electionsConfig.getSeuilElectoral()));
System.out.println("Listes candidates----------------");
for (ListeElectorale liste : listes) {
System.out.println(liste);
}
// Cierre del contexto de Spring
ctx.close();
}
}
La clase [Test01] es una prueba de JUnit:
package dao.tests;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import dao.config.AppConfig;
import dao.entities.ElectionsConfig;
import dao.entities.ListeElectorale;
import dao.service.IElectionsDao;
@SpringApplicationConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
// capa [DAO]
@Autowired
private IElectionsDao electionsDao;
@Before
public void init() {
// se limpia la tabla [LISTES]
// listas en liza
ListeElectorale[] listes = electionsDao.getListesElectorales();
// se ponen a 0 los votos y los escaños y se elimina «false»
int voix = 0;
int sièges = 0;
boolean elimine = false;
for (ListeElectorale liste : listes) {
liste.setVoix(voix);
liste.setSieges(sièges);
liste.setElimine(elimine);
}
// se hacen persistentes estos datos mediante la capa [dao]
electionsDao.setListesElectorales(listes);
}
@Test
public void testElections01() {
System.out.println("testElections01-------------------------------------");
// recuperación de la configuración de las elecciones
ElectionsConfig electionsConfig = electionsDao.getElectionsConfig();
int nbSiegesAPourvoir = electionsConfig.getNbSiegesAPourvoir();
double seuilElectoral = electionsConfig.getSeuilElectoral();
Assert.assertEquals(6, nbSiegesAPourvoir);
Assert.assertEquals(0.05, seuilElectoral, 1E-6);
// listas en liza
ListeElectorale[] listes = electionsDao.getListesElectorales();
// Visualización de los valores leídos
System.out.println("Nombre de sièges à pourvoir : " + nbSiegesAPourvoir);
System.out.println("Seuil électoral : " + seuilElectoral);
System.out.println("Listes en compétition ---------------------");
for (int i = 0; i < listes.length; i++) {
System.out.println(listes[i]);
}
// se asignan votos y escaños a las listas
int voix = 0;
int sièges = 0;
boolean elimine = false;
for (ListeElectorale liste : listes) {
liste.setVoix(voix);
liste.setSieges(sièges);
liste.setElimine(elimine);
voix += 10;
sièges += 1;
elimine = !elimine;
}
// se guardan estos datos de forma permanente mediante la capa [dao]
electionsDao.setListesElectorales(listes);
// se vuelven a leer los datos
ListeElectorale[] listesElectorales2 = electionsDao.getListesElectorales();
// se comprueban los datos leídos
Assert.assertEquals(7, listesElectorales2.length);
voix = 0;
sièges = 0;
elimine = false;
for (ListeElectorale liste : listes) {
Assert.assertEquals(voix, liste.getVoix());
Assert.assertEquals(sièges, liste.getSieges());
Assert.assertEquals(elimine, liste.isElimine());
voix += 10;
sièges += 1;
elimine = !elimine;
}
System.out.println("Listes en compétition ---------------------");
for (int i = 0; i < listes.length; i++) {
System.out.println(listes[i]);
}
}
}
Tarea pendiente: ejecuta las pruebas [console] y [JUnit] en tu capa [DAO].
12.9. Generación del archivo Maven del proyecto
Siguiendo el ejemplo del apartado 11.3.12, genera el archivo Maven del proyecto.












