12. [TD]: Implementatie van de laag [DAO] van TD met [Spring Data]
Trefwoorden: meerlaagse architectuur, Spring, afhankelijkheidsinjectie, API JPA (Java Persistence API), Spring Data.
We volgen dezelfde aanpak als eerder om de laag [DAO] van TD te implementeren.
12.1. Support
![]() |
- in [1]; de map [support / chap-12] bevat het Eclipse-project van dit hoofdstuk;
12.2. Het Eclipse-project
Het Eclipse-project ziet er als volgt uit:
![]() |
- [elections.dao.config]: bevat de configuratieklasse van het Spring-project;
- [elections.dao.entities]: bevat de entiteiten JPA en de uitzonderingsklasse van het project;
- [elections.dao.repositories]: bevat de interfaces [CrudRepository] voor de tabellen [CONF] en [LISTES];
- [elections.dao.service]: bevat de implementatie van de laag [DAO]. Deze moeten we schrijven;
- [elections.dao.console]: bevat een testklasse van het type [console];
- [pom.xml]: het configuratiebestand van het Maven-project;
Dit project implementeert de volgende architectuur:
![]() |
De laag [DAO] ziet alleen de laag die door [Spring Data] wordt geïmplementeerd.
12.3. Maven-configuratie
Te doen: bouw het bestand [pom.xml] van het project.
12.4. De entiteiten van de laag [JPA]
![]() |
![]() |
- [ElectionsConfig] is het objectmodel dat gekoppeld is aan een rij in de tabel [CONF];
- [ListeElectorale] is het objectmodel dat gekoppeld is aan een rij in de tabel [LISTES];
- [AbstactEntity] is de bovenliggende klasse van de twee voorgaande klassen. Deze klasse bundelt de velden van [id, version] die beide klassen gemeen hebben;
- [ElectionsException] is de uitzonderingsklasse van het project;
12.4.1. De klasse [ElectionsException]
![]() |
De klasse [ElectionsException] is de ongecontroleerde uitzondering die in paragraaf 4.3 wordt beschreven.
12.4.2. De klasse [AbstractEntity]
![]() |
De klasse [AbstractEntity] is als volgt:
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;
// eigenschappen
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
protected Long id;
@Version
@Column(name = "VERSION")
protected Long version;
// constructors
public AbstractEntity() {
}
public AbstractEntity(Long id, Long version) {
this.id = id;
this.version = version;
}
// herdefinitie van [equals] en [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;
}
// handtekening jSON
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// getters en setters
...
}
Dit is de klasse die wordt beschreven in paragraaf 11.3.5.2, zonder de filters jSON. Hier zijn de tabellen [CONF] en [LISTES] namelijk niet gekoppeld via een vreemde-sleutelrelatie. Maar juist het bestaan van een dergelijke relatie met de modus [lazy loading] maakt de filters jSON noodzakelijk.
12.4.3. De klasse [ElectionsConfig]
![]() |
De klasse [ElectionsConfig] is de entiteit JPA die is gekoppeld aan de tabel [CONF];
Te doen: maak de klasse [ElectionsConfig] aan.
12.4.4. De klasse [ListeElectorale]
![]() |
De klasse [ListeElectorale] is de entiteit JPA die gekoppeld is aan de tabel [LISTES];
Te doen: maak de klasse [ListeElectorale] aan.
12.5. De laag [Spring Data]
![]() |
Opdracht: schrijf de twee interfaces van [Spring Data] om de twee tabellen [CONF] en [LISTES] te beheren;
12.6. De laag [DAO]
![]() |
De interface [IElectionsDao] van de laag [DAO] is als volgt:
package dao.service;
import dao.entities.ElectionsConfig;
import dao.entities.ListeElectorale;
public interface IElectionsDao {
// verkiezingsconfiguratie
public ElectionsConfig getElectionsConfig();
// kandidatenlijsten
public ListeElectorale[] getListesElectorales();
// bijwerken van kandidatenlijsten
public void setListesElectorales(ListeElectorale[] listesElectorales);
}
Opdracht: schrijf de implementatie [ElectionsDaoJpa] van de interface [IElectionsDao].
12.7. Configuratie van het Spring-project
De klasse [DaoConfig] configureert het Spring-project.
Opdracht: schrijf de klasse [DaoConfig]
12.8. De laag [console]
![]() |
![]() |
De klasse [Main] is de volgende uitvoerbare klasse:
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 {
// gegevensbron
private static IElectionsDao dao;
public static void main(String[] args) {
// ophalen van de Spring-context
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// ophalen van de gegevensbron
dao = ctx.getBean(IElectionsDao.class);
// inhoud van beide tabellen
ElectionsConfig electionsConfig = dao.getElectionsConfig();
ListeElectorale[] listes = dao.getListesElectorales();
// weergave
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);
}
// Spring-context sluiten
ctx.close();
}
}
De klasse [Test01] is een test van 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 {
// laag [DAO]
@Autowired
private IElectionsDao electionsDao;
@Before
public void init() {
// de tabel wordt opgeschoond [LISTES]
// concurrerende lijsten
ListeElectorale[] listes = electionsDao.getListesElectorales();
// we zetten het aantal stemmen en zetels op 0 en verwijderen de waarde '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);
}
// we maken deze gegevens persistent via de laag [dao]
electionsDao.setListesElectorales(listes);
}
@Test
public void testElections01() {
System.out.println("testElections01-------------------------------------");
// ophalen van de verkiezingsconfiguratie
ElectionsConfig electionsConfig = electionsDao.getElectionsConfig();
int nbSiegesAPourvoir = electionsConfig.getNbSiegesAPourvoir();
double seuilElectoral = electionsConfig.getSeuilElectoral();
Assert.assertEquals(6, nbSiegesAPourvoir);
Assert.assertEquals(0.05, seuilElectoral, 1E-6);
// deelnemende lijsten
ListeElectorale[] listes = electionsDao.getListesElectorales();
// weergave van uitgelezen waarden
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]);
}
// stemmen en zetels worden toegewezen aan de lijsten
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;
}
// deze gegevens worden opgeslagen via de laag [dao]
electionsDao.setListesElectorales(listes);
// de gegevens worden opnieuw uitgelezen
ListeElectorale[] listesElectorales2 = electionsDao.getListesElectorales();
// de gelezen gegevens worden gecontroleerd
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]);
}
}
}
Te doen: voer de tests [console] en [JUnit] uit op uw laag [DAO].
12.9. Het Maven-archief van het project genereren
Genereer het Maven-archief van het project volgens het voorbeeld in paragraaf 11.3.12.












