Skip to content

12. [TD]: Implementazione del livello [DAO] del TD utilizzando [Spring Data]

Parole chiave: architettura multistrato, Spring, iniezione di dipendenze, JPA (Java Persistence API), Spring Data.

Seguiremo lo stesso approccio di prima per implementare il livello [DAO] del TD.

12.1. Supporto

  • In [1], la cartella [support / chap-12] contiene il progetto Eclipse relativo a questo capitolo;

12.2. Il progetto Eclipse

Il progetto Eclipse sarà il seguente:

  
  • [elections.dao.config]: contiene la classe di configurazione del progetto Spring;
  • [elections.dao.entities]: contiene le entità JPA e la classe delle eccezioni del progetto;
  • [elections.dao.repositories]: contiene le interfacce [CrudRepository] per le tabelle [CONF] e [LISTES];
  • [elections.dao.service]: contiene l'implementazione del livello [DAO]. Questo è ciò che dobbiamo scrivere;
  • [elections.dao.console]: contiene una classe di test [console];
  • [pom.xml]: il file di configurazione del progetto Maven;

Questo progetto implementa la seguente architettura:

Il livello [DAO] vede solo il livello implementato da [Spring Data].

12.3. Configurazione Maven


Attività: Compilare il file [pom.xml] del progetto.


12.4. Le entità del livello [JPA]

  
  • [ElectionsConfig] è il modello a oggetti associato a una riga della tabella [CONF];
  • [VoterList] è il modello a oggetti associato a una riga della tabella [LISTS];
  • [AbstractEntity] è la classe padre delle due classi precedenti. Estrae i campi [id, version] comuni a entrambe le classi;
  • [ElectionsException] è la classe di eccezione del progetto;

12.4.1. La classe [ElectionsException]

  

La classe [ElectionsException] è l'eccezione non gestita descritta nella sezione 4.3.

12.4.2. La classe [AbstractEntity]

  

La classe [AbstractEntity] è la seguente:


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;
 
    // properties
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    protected Long id;
    @Version
    @Column(name = "VERSION")
    protected Long version;
 
    // manufacturers
    public AbstractEntity() {
 
    }
 
    public AbstractEntity(Long id, Long version) {
        this.id = id;
        this.version = version;
    }
 
    // redefine [equals] and [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;
    }
 
    // signature jSON
    public String toString() {
        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }
 
    // getters and setters
...
}

Questa è la classe descritta nella sezione 11.3.5.2, senza i filtri JSON. Qui, le tabelle [CONF] e [LISTES] non sono collegate da una relazione di chiave esterna. Tuttavia, è proprio l'esistenza di tale relazione con la modalità [lazy loading] a rendere necessari i filtri JSON.

12.4.3. La classe [ElectionsConfig]

  

La classe [ElectionsConfig] è l'entità JPA associata alla tabella [CONF];


Compito: implementare la classe [ElectionsConfig].


12.4.4. La classe [VoterList]

  

La classe [VoterList] è l'entità JPA associata alla tabella [LISTS];


Compito: implementare la classe [VoterList].


12.5. Il livello [Spring Data]

  

Compito: scrivere le due interfacce [Spring Data] per gestire le due tabelle [CONF] e [LISTES];


12.6. Il livello [DAO]

  

L'interfaccia [IElectionsDao] del livello [DAO] è la seguente:


package dao.service;
 
import dao.entities.ElectionsConfig;
import dao.entities.ListeElectorale;
 
public interface IElectionsDao {
 
    // election configuration
    public ElectionsConfig getElectionsConfig();
 
    // candidate lists
    public ListeElectorale[] getListesElectorales();
 
    // updating candidate lists
    public void setListesElectorales(ListeElectorale[] listesElectorales);
}

Compito: Scrivere l'implementazione [ElectionsDaoJpa] dell'interfaccia [IElectionsDao].


12.7. Configurazione del progetto Spring

  

La classe [DaoConfig] configura il progetto Spring.


Compito: scrivere la classe [DaoConfig]


12.8. Il livello [console]

  

La classe [Main] è la seguente classe eseguibile:


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 {
 
    // data source
    private static IElectionsDao dao;
 
    public static void main(String[] args) {
        // spring context retrieval
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        // data source recovery
        dao = ctx.getBean(IElectionsDao.class);
        // contents of both tables
        ElectionsConfig electionsConfig = dao.getElectionsConfig();
        ListeElectorale[] listes = dao.getListesElectorales();
        // display
        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);
        }
        // closing Spring context
        ctx.close();
    }
 
}

La classe [Test01] è un test 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 {
 
    // layer [DAO]
    @Autowired
    private IElectionsDao electionsDao;
 
    @Before
    public void init() {
        // the table is cleaned [LISTES]
        // competing lists
        ListeElectorale[] listes = electionsDao.getListesElectorales();
        // set votes and seats to 0 and eliminate 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 make this data persistent using the [dao] layer
        electionsDao.setListesElectorales(listes);
    }
 
    @Test
    public void testElections01() {
        System.out.println("testElections01-------------------------------------");
        // election configuration recovery
        ElectionsConfig electionsConfig = electionsDao.getElectionsConfig();
        int nbSiegesAPourvoir = electionsConfig.getNbSiegesAPourvoir();
        double seuilElectoral = electionsConfig.getSeuilElectoral();
        Assert.assertEquals(6, nbSiegesAPourvoir);
        Assert.assertEquals(0.05, seuilElectoral, 1E-6);
 
        // competing lists
        ListeElectorale[] listes = electionsDao.getListesElectorales();
        // display read values
        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]);
        }
 
        // votes and seats are allocated to lists
        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;
        }
 
        // we make this data persistent using the [dao] layer
        electionsDao.setListesElectorales(listes);
 
        // data re-reading
        ListeElectorale[] listesElectorales2 = electionsDao.getListesElectorales();
        // check data read
        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]);
        }
    }
}

Compito: Esegui i test [console] e [JUnit] sul tuo livello [DAO].


12.9. Generazione dell'archivio Maven del progetto

Seguendo l'esempio della Sezione 11.3.12, generare l'archivio Maven del progetto.