12. [TD]: Implementation of the [DAO] layer of the TD using [Spring Data]
Keywords: multi-layer architecture, Spring, dependency injection, JPA (Java Persistence API), Spring Data.
We will follow the same approach as before to implement the [DAO] layer of the TD.
12.1. Support
![]() |
- In [1], the [support / chap-12] folder contains the Eclipse project for this chapter;
12.2. The Eclipse project
The Eclipse project will be as follows:
![]() |
- [elections.dao.config]: contains the Spring project configuration class;
- [elections.dao.entities]: contains the JPA entities as well as the project’s exception class;
- [elections.dao.repositories]: contains the [CrudRepository] interfaces for the [CONF] and [LISTES] tables;
- [elections.dao.service]: contains the implementation of the [DAO] layer. This is what we need to write;
- [elections.dao.console]: contains a [console] test class;
- [pom.xml]: the Maven project configuration file;
This project implements the following architecture:
![]() |
The [DAO] layer only sees the layer implemented by [Spring Data].
12.3. Maven Configuration
Task: Build the project's [pom.xml] file.
12.4. The entities of the [JPA] layer
![]() |
![]() |
- [ElectionsConfig] is the object model associated with a row in the [CONF] table;
- [VoterList] is the object model associated with a row in the [LISTS] table;
- [AbstractEntity] is the parent class of the two preceding classes. It factors out the [id, version] fields common to both classes;
- [ElectionsException] is the project’s exception class;
12.4.1. The [ElectionsException] class
![]() |
The [ElectionsException] class is the unhandled exception described in section 4.3.
12.4.2. The [AbstractEntity] class
![]() |
The [AbstractEntity] class is as follows:
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;
// constructors
public AbstractEntity() {
}
public AbstractEntity(Long id, Long version) {
this.id = id;
this.version = version;
}
// override [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;
}
// JSON signature
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// getters and setters
...
}
This is the class described in section 11.3.5.2, without the JSON filters. Here, the [CONF] and [LISTES] tables are not linked by a foreign key relationship. However, it is the existence of such a relationship with the [lazy loading] mode that necessitates JSON filters.
12.4.3. The [ElectionsConfig] class
![]() |
The [ElectionsConfig] class is the JPA entity associated with the [CONF] table;
Task: Implement the [ElectionsConfig] class.
12.4.4. The [VoterList] class
![]() |
The [VoterList] class is the JPA entity associated with the [LISTS] table;
Task: Implement the [VoterList] class.
12.5. The [Spring Data] layer
![]() |
![]() |
Task: Write the two [Spring Data] interfaces to manage the two tables [CONF] and [LISTES];
12.6. The [DAO] layer
![]() |
![]() |
The [IElectionsDao] interface of the [DAO] layer is as follows:
package dao.service;
import dao.entities.ElectionsConfig;
import dao.entities.VoterList;
public interface IElectionsDao {
// election configuration
public ElectionsConfig getElectionsConfig();
// candidate lists
public VoterList[] getVoterLists();
// update candidate lists
public void setCandidateLists(CandidateList[] candidateLists);
}
Task: Write the [ElectionsDaoJpa] implementation of the [IElectionsDao] interface.
12.7. Spring Project Configuration
![]() |
The [DaoConfig] class configures the Spring project.
Task: Write the [DaoConfig] class
12.8. The [console] layer
![]() |
![]() |
The [Main] class is the following executable class:
package dao.console;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import dao.config.AppConfig;
import dao.entities.ElectionsConfig;
import dao.entities.VoterList;
import dao.service.IElectionsDao;
public class Main {
// data source
private static IElectionsDao dao;
public static void main(String[] args) {
// retrieve the Spring context
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// Retrieve the data source
dao = ctx.getBean(IElectionsDao.class);
// Contents of the two tables
ElectionsConfig electionsConfig = dao.getElectionsConfig();
VoterList[] lists = dao.getVoterLists();
// display
System.out.println(String.format("Number of seats to be filled: %d", electionsConfig.getNbSiegesAPourvoir()));
System.out.println(String.format("Electoral threshold: %5.2f", electionsConfig.getSeuilElectoral()));
System.out.println("Candidate lists----------------");
for (ElectionList list : lists) {
System.out.println(list);
}
// Close Spring context
ctx.close();
}
}
The [Test01] class is a JUnit test:
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.VoterList;
import dao.service.IElectionsDao;
@SpringApplicationConfiguration(classes = AppConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
// [DAO] layer
@Autowired
private IElectionsDao electionsDao;
@Before
public void init() {
// clear the [LISTS] table
// competing lists
ElectoralLists[] lists = electionsDao.getElectoralLists();
// Set votes and seats to 0 and set "eliminated" to false
int votes = 0;
int seats = 0;
boolean eliminated = false;
for (VoterList list : lists) {
list.setVotes(votes);
list.setSeats(seats);
list.setEliminated(eliminated);
}
// We make this data persistent using the [DAO] layer
electionsDao.setVoterLists(lists);
}
@Test
public void testElections01() {
System.out.println("testElections01-------------------------------------");
// retrieve the election configuration
ElectionsConfig electionsConfig = electionsDao.getElectionsConfig();
int numSeatsToBeFilled = electionsConfig.getNumSeatsToBeFilled();
double electoralThreshold = electionsConfig.getSeuilElectoral();
Assert.assertEquals(6, nbSiegesAPourvoir);
Assert.assertEquals(0.05, electoralThreshold, 1E-6);
// competing lists
ElectionLists[] lists = electionsDao.getElectionLists();
// display read values
System.out.println("Number of seats to be filled: " + nbSiegesAPourvoir);
System.out.println("Electoral threshold: " + electoralThreshold);
System.out.println("Lists in the race ---------------------");
for (int i = 0; i < lists.length; i++) {
System.out.println(lists[i]);
}
// assign votes and seats to the lists
int votes = 0;
int seats = 0;
boolean eliminated = false;
for (VoterList list : lists) {
list.setVotes(votes);
list.setSeats(seats);
list.setEliminated(eliminated);
votes += 10;
seats += 1;
eliminated = !eliminated;
}
// We make this data persistent using the [DAO] layer
electionsDao.setVoterLists(lists);
// we read the data again
VoterList[] voterLists2 = electionsDao.getVoterLists();
// we verify the retrieved data
Assert.assertEquals(7, electoralLists2.length);
votes = 0;
seats = 0;
eliminated = false;
for (VoterList list : lists) {
Assert.assertEquals(votes, list.getVotes());
Assert.assertEquals(votes, list.getVotes());
Assert.assertEquals(eliminated, list.isEliminated());
votes += 10;
seats += 1;
eliminated = !eliminated;
}
System.out.println("Competing lists ---------------------");
for (int i = 0; i < lists.length; i++) {
System.out.println(lists[i]);
}
}
}
Task: Run the [console] and [JUnit] tests on your [DAO] layer.
12.9. Generating the project's Maven archive
Following the example in Section 11.3.12, generate the project's Maven archive.















