7. [TD]: Implementation of the [DAO] layer of TD with API and JDBC
Keywords: relational databases, API JDBC, SQLException.
Let’s take a look at the layered architecture of our application:
![]() |
The data required for the election is stored in a database MySQL [dbelections]
7.1. Support
![]() |
The folder [support / chap-07] [1] contains:
- the Eclipse projects for this chapter [2];
- the script SQL for creating the database MySQL [dbelections] [3];
7.2. The [dbelections] database
Task: Create the database MySQL [dblelections] by following the procedure described in section 6.4.2.
The database [dbelections] is a MySQL database with two tables:
![]() |
The [conf] table contains the election configuration information:
![]() |
- [id]: auto-increment primary key;
- [version]: record ID of version—can be ignored here;
- [sap]: number of seats to be filled;
- [seuilelectoral]: the threshold below which a list is eliminated;
Its contents are as follows:
![]() |
The table [listes] contains the candidate lists for the election:
![]() |
- [id]: auto-increment primary key;
- [version]: record ID of version—can be ignored here;
- [nom]: list name;
- [voix]: list entry – known only after user input in layer [présentation];
- [sieges]: number of seats obtained—known only after calculating layer [métier];
- [elimine]: 1 if the list is eliminated, 0 otherwise – known only after calculation of layer [métier];
The contents of table [listes] are as follows:
![]() |
The script SQL for generating the database [dbelections] is named [dbelections.sql] and is on the server. Its code is as follows:
-- phpMyAdmin SQL Dump
-- version 4.0.4
-- http://www.phpmyadmin.net
--
-- Customer: localhost
-- Generated on: Wed March 11, 2015 at 12:20 pm
-- Version of server: 5.6.12-log
-- Version from PHP: 5.4.12
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- Database: `dbelections`
--
CREATE DATABASE IF NOT EXISTS `dbelections` DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci;
USE `dbelections`;
-- --------------------------------------------------------
--
-- Structure of the `conf` table
--
CREATE TABLE IF NOT EXISTS `conf` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`version` int(11) NOT NULL DEFAULT '1',
`sap` tinyint(4) NOT NULL,
`seuilelectoral` double NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=2 ;
--
-- Contents of the `conf` table
--
INSERT INTO `conf` (`id`, `version`, `sap`, `seuilelectoral`) VALUES
(1, 1, 6, 0.05);
-- --------------------------------------------------------
--
-- Structure of the `lists` table
--
CREATE TABLE IF NOT EXISTS `listes` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`version` int(11) NOT NULL DEFAULT '1',
`nom` varchar(20) COLLATE utf8_swedish_ci NOT NULL,
`voix` int(11) NOT NULL,
`sieges` int(11) NOT NULL,
`elimine` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `nom` (`nom`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci AUTO_INCREMENT=8 ;
--
-- Contents of the `lists` table
--
INSERT INTO `listes` (`id`, `version`, `nom`, `voix`, `sieges`, `elimine`) VALUES
(1, 21, 'A', 10, 1, 0),
(2, 22, 'B', 20, 2, 0),
(3, 21, 'C', 30, 3, 0),
(4, 13, 'D', 40, 3, 0),
(5, 17, 'E', 50, 6, 0),
(6, 18, 'F', 60, 1, 0),
(7, 19, 'G', 70, 2, 0);
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
7.3. The Eclipse Project
![]() |
The Eclipse project for the [DAO] layer will be as follows:
![]() |
- the [elections.dao.entities] package contains the objects manipulated by the [DAO] layer;
- the [elections.dao.service] package contains the implementation of the [DAO] layer;
- the [elections.dao.config] package contains the configuration of the [DAO] layer
- the package [elections.dao.junit] contains a test class JUnit for the project;
- The [elections.dao.console] package contains an executable test class;
7.4. Maven Project Configuration
![]() |
The file [pom.xml] that configures the Maven project is as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-dao-jdbc-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Tomcat Jdbc -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
<!-- library jSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Logging -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
<!-- plugins -->
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>config.AppConfig</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- to install the project artifact in the local Maven repository -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
This file is similar to the one described in Section 6.5.1. The following changes have been made:
- lines 8–12: a parent Maven project has been defined. The [spring-boot-starter-parent] project (line 10) defines a very large number of dependencies with their versions. When using one of them (lines 19–57), there is no need to specify the version, as it is defined in the parent Maven project;
- lines 40–51: dependencies required for the project’s test class. These dependencies have the [<scope>test</scope>] attribute, which means they are only required for the classes in the [src / test / java] folder. These dependencies will not be included in the final project archive;
- lines 53–56: the [spring-boot-starter-logging] library will be used by Spring for on-screen logging;
- lines 14–17: Maven configuration properties:
- line 15: specifies that the source files are encoded in UTF-8;
- line 16: specifies that the compiler to be used must be version 1.8;
7.5. Entities in the [DAO] layer
![]() |
- [ElectionsConfig] is the object model associated with a row in the [CONF] table;
- [ListeElectorale] is the object model associated with a row in the [LISTES] table;
- [AbstactEntity] is the parent class of the two preceding classes. It factors out the [id, version] fields common to both classes;
- [ElectionsException] is an exception class;
7.5.1. The class [ElectionsException]
![]() |
The class [ElectionsException] was described in Section 4.3. Here is its code again:
package ...;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
// exception class for the Elections application
// the exception is uncontrolled
public class ElectionsException extends RuntimeException implements Serializable {
// serial ID
private static final long serialVersionUID = 1L;
// local fields
private int code;
private List<String> erreurs;
// manufacturers
public ElectionsException() {
super();
}
public ElectionsException(int code, Throwable e) {
// parent
super(e);
// local
this.code = code;
this.erreurs = getErreursForException(e);
}
public ElectionsException(int code, String message, Throwable e) {
// parent
super(message,e);
// local
this.code = code;
this.erreurs = getErreursForException(e);
}
public ElectionsException(int code, String message) {
// parent
super(message);
// local
this.code = code;
List<String> erreurs = new ArrayList<>();
erreurs.add(message);
this.erreurs = erreurs;
}
public ElectionsException(int code, List<String> erreurs) {
// parent
super();
// local
this.code = code;
this.erreurs = erreurs;
}
// list of exception error messages
private List<String> getErreursForException(Throwable th) {
// retrieve the list of exception error messages
Throwable cause = th;
List<String> erreurs = new ArrayList<>();
while (cause != null) {
// the message is retrieved only if it is !=null and not blank
String message = cause.getMessage();
if (message != null) {
message = message.trim();
if (message.length() != 0) {
erreurs.add(message);
}
}
// next cause
cause = cause.getCause();
}
return erreurs;
}
// getters and setters
...
}
A [ElectionsException] object is characterized by two pieces of information:
- line 16: an error code;
- line 17: a list of error messages associated with the stack of exceptions that occurred;
- the class has 5 constructors (lines 20, 24, 32, 40, 50);
- lines 59–76: the [getErreursForException] method retrieves the error messages from the exception stack;
7.5.2. The [AbstractEntity] class
![]() |
The [AbstractEntity] class is as follows:
package elections.dao.entities;
import java.io.Serializable;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public abstract class AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
// fields
protected Long id;
protected Long version;
// manufacturers
public AbstractEntity() {
}
public AbstractEntity(Long id, Long version) {
this.id = id;
this.version = version;
}
// signature
public String toString() {
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// getters and setters
...
}
It stores the [ID, NOM] fields from the rows of tables [CONF] and [LISTES] (lines 8–9).
- line 8: the class has the attribute [Abstract] indicating that it cannot be instantiated. It can only be derived;
- Lines 26–33: jSON signature of the object;
- line 28: the string jSON is returned from [this]. If, at runtime, [this] represents an object derived from [AbstactEntity], the string jSON of the derived object is obtained. Derived classes will therefore not need to define a [toString] method. The one from the parent class is sufficient;
7.5.3. The [ElectionsConfig] class
![]() |
The [ElectionsConfig] class is as follows:
package elections.dao.entities;
public class ElectionsConfig extends AbstractEntity {
private static final long serialVersionUID = 1L;
// fields
private int nbSiegesAPourvoir;
private double seuilElectoral;
// manufacturers
public ElectionsConfig() {
}
public ElectionsConfig(Long id, Long version, int nbSiegesAPourvoir, double seuilElectoral) {
// parent
super(id, version);
// local fields
this.nbSiegesAPourvoir = nbSiegesAPourvoir;
this.seuilElectoral = seuilElectoral;
}
// getters and setters
...
}
- line 4: the class extends the [AbstractEntity] class;
- lines 8-9: store the information from the [SAP, SEUILELECTORAL] columns of the [CONF] table;
7.5.4. The [ListeElectorale] class
![]() |
The [ListeElectorale] class is as follows:
package elections.dao.entities;
public class ListeElectorale extends AbstractEntity {
// fields
private String nom;
private int voix;
private int sieges;
private boolean elimine;
// manufacturers
public ListeElectorale() {
}
public ListeElectorale(String nom, int voix, int sieges, boolean elimine) {
// parent
super();
// local fields
initNom(nom);
initVoix(voix);
initSieges(sieges);
this.elimine=elimine;
}
public ListeElectorale(Long id, Long version, String nom, int voix, int sieges, boolean elimine) {
// parent
super(id, version);
// local fields
initNom(nom);
initVoix(voix);
initSieges(sieges);
this.elimine=elimine;
}
// private methods
private void initNom(String nom) {
this.nom = nom.trim();
if ("".equals(nom)) {
throw new ElectionsException(10, "Le nom ne peut être vide");
}
}
private void initVoix(int voix) {
this.voix = voix;
if (voix < 0) {
throw new ElectionsException(11, "Le nombre de voix ne peut être <0");
}
}
private void initSieges(int sieges) {
this.sieges = sieges;
if (sieges < 0) {
throw new ElectionsException(12, "Le nombre de sièges ne peut être <0");
}
}
// getters and setters
public String getNom() {
return nom;
}
public void setNom(String nom) {
initNom(nom);
}
public int getVoix() {
return voix;
}
public void setVoix(int voix) {
initVoix(voix);
}
public int getSieges() {
return sieges;
}
public void setSieges(int sieges) {
initSieges(sieges);
}
public boolean isElimine() {
return elimine;
}
public void setElimine(boolean elimine) {
this.elimine = elimine;
}
}
- line 3: the class extends the [AbstractEntity] class;
- lines 6–9: the class stores the [NOM, VOIX, SIEGES, ELIMINE] columns from the [LISTES] table;
7.6. Spring configuration of the [DAO] layer
![]() |
![]() |
The [AppConfig] class is a Spring configuration class that configures database access as follows:
package elections.dao.config;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan(basePackages = { "elections.dao.service" })
@EnableCaching
public class AppConfig {
// constants
public final static String URL = "jdbc:mysql://localhost:3306/dbelections";
public final static String USER = "root";
public final static String PASSWD = "";
public final static String DRIVER_CLASSNAME = "com.mysql.jdbc.Driver";
public final static String SELECT_LISTES = "SELECT ID, VERSION, NOM, VOIX, SIEGES, ELIMINE FROM LISTES";
public final static String SELECT_CONF = "SELECT ID, VERSION, SAP, SEUILELECTORAL, SAP FROM CONF";
public final static String UPDATE_LISTES = "UPDATE LISTES SET VOIX=?, SIEGES=?, ELIMINE=? WHERE ID=?";
@Bean
public DataSource dataSource() {
// data source TomcatJdbc
DataSource dataSource = new DataSource();
// configuration access JDBC
dataSource.setDriverClassName(DRIVER_CLASSNAME);
dataSource.setUsername(USER);
dataSource.setPassword(PASSWD);
dataSource.setUrl(URL);
// an initially open connection
dataSource.setInitialSize(1);
// result
return dataSource;
}
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("electionsConfig");
}
}
- lines 25–38: Access to BD will be via a [tomcat-jdbc] data source. This type of data source was used and explained in Section 6.5;
- lines 17–23: a set of static constants accessible by all classes in the project;
- line 13: the [@Configuration] annotation makes the [AppConfig] class a Spring configuration class;
- line 11: the [@ComponentScan] annotation indicates the packages where Spring objects can be found. Here, we will define a Spring object in the [dao] package. The [@ComponentScan] annotation makes the class a configuration class, which saves us from having to add the [@Configuration] annotation;
- Line 12 enables cache management. The principle is as follows:
- we apply the [@Cacheable('nom_du_cache')] annotation to a method M. The 'cache_name' is the name used on line 41;
- when method M is called for the first time, its results are returned and also placed in the cache named by the annotation;
- when method M is called subsequently with the same parameters as the first time, it is not executed, and Spring simply returns the cached values;
7.7. Log configuration
![]() |
The logging libraries are defined by the following dependency in [pom.xml]:
<!-- Spring Boot Logging -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
This dependency includes the following libraries:
![]() |
The [logback] library handles logging. It is configured by two files:
- [logback.xml] for the main code branch;
- [logback-test.xml] for the code’s test branch. If this file is missing, the previous file is used instead;
These two files must be located in the project’s [Classpath] directory. For this reason, they are placed in the folder:
- [src / main / ressources] for the main code branch;
- [src / test / ressources] for the code's test branch;
The contents of the files are the same here:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are by default assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- log level control -->
<root level="info"> <!-- info, debug, warn -->
<appender-ref ref="STDOUT" />
</root>
</configuration>
Everything happens on line 12, where the desired information level is set:
- [debug]: the most detailed level;
- [off]: no logs;
- [info]: normal logging level;
- [warn]: same as [info] plus warning messages. These messages indicate a potential error;
Switch to [debug] mode as soon as errors occur during execution.
7.8. Implementation of the [DAO] layer
![]() |
![]() |
The [IDao] interface of the [DAO] layer is as follows:
package istia.st.elections.webapi.client.dao;
import istia.st.elections.webapi.client.entities.ElectionsConfig;
import istia.st.elections.webapi.client.entities.ListeElectorale;
public interface IDao {
// election configuration
public ElectionsConfig getElectionsConfig();
// competing lists
public ListeElectorale[] getListesElectorales();
// save election results
public void setListesElectorales(ListeElectorale[] listesElectorales);
}
The skeleton of the [ElectionsDaoJdbc] class implementing the [dao] layer with a database will be as follows:
package elections.dao.service;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.dao.entities.ListeElectorale;
@Component
@SuppressWarnings("unused")
public class ElectionDaoJdbc implements IElectionsDao {
@Autowired
private DataSource dataSource;
@Cacheable("electionsConfig")
// obtaining conf of the election
public ElectionsConfig getElectionsConfig() {
throw new RuntimeException("[getElectionsConfig] not yet implemented");
}
// obtaining lists
public ListeElectorale[] getListesElectorales() {
throw new RuntimeException("[getListesElectorales] not yet implemented");
}
// list modification [votes, seats, eliminated]
public void setListesElectorales(ListeElectorale[] listesElectorales) {
throw new RuntimeException("[setListesElectorales] not yet implemented");
}
// -------------------- private methods
// end-of-life management
private ElectionsException doFinally(int code, ResultSet rs, PreparedStatement ps, Connection connexion) {
// closure ResultSet
if (rs != null) {
try {
rs.close();
} catch (SQLException e1) {
}
}
// closure [PreparedStatement]
if (ps != null) {
try {
ps.close();
} catch (SQLException e2) {
}
}
// close connection
if (connexion != null) {
try {
connexion.close();
} catch (SQLException e3) {
// returns a [ElectionsException]
return new ElectionsException(code, e3);
}
}
// no exceptions
return null;
}
// gestion du catch
private ElectionsException doCatchException(int code1, int code2, Connection connexion, Throwable th) {
// we generate a [ElectionsException]
ElectionsException ex1 = new ElectionsException(code1, th);
// cancel transaction
try {
if (connexion != null) {
connexion.rollback();
}
} catch (SQLException e2) {
}
// we return the exception
return ex1;
}
}
- line 24: the annotation [@Cacheable] is a Spring annotation that instructs the system to cache the results of the method [getElectionsConfig]. This is possible here because the contents of the table [CONF] never change. It would not be possible to place this annotation on the [getListesElectorales] method because the contents of the [LISTES] table change over time;
- the methods [doCatchException] and [doFinally] return a type [ElectionsException]. The method [doFinally] returns a null pointer if the resources were released without errors;
Task: Write the class [ElectionDaoJdbc] based on the class [IntroJdbc02] discussed in Section 6.5.2.
7.9. The test class [Main]
![]() |
![]() |
The [Main] class is as follows:
package elections.dao.console;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import elections.dao.config.AppConfig;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ElectionsException;
import elections.dao.entities.ListeElectorale;
import elections.dao.service.IElectionsDao;
public class Main {
// data source
private static IElectionsDao dao;
public static void main(String[] args) {
// retrieve the [DAO] layer reference after instantiating the Spring context
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class)) {
// data source recovery
dao = ctx.getBean(IElectionsDao.class);
} catch (Exception e) {
System.out.println("Les erreurs suivantes se sont produites lors de l'initialisation du contexte Spring -------");
for (String erreur : getErreursForThrowable(e)) {
System.out.println(erreur);
}
// end
return;
}
// reading the BD
ElectionsConfig electionsConfig = null;
ListeElectorale[] listes;
try {
// contents of both tables
electionsConfig = dao.getElectionsConfig();
listes = dao.getListesElectorales();
} catch (ElectionsException e) {
System.out.println("Les erreurs suivantes se sont produites lors de la lecture des tables ----------");
for (String erreur : e.getErreurs()) {
System.out.println(erreur);
}
// end
return;
}
// all went well - 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);
}
}
// private methods ------------------
private static List<String> getErreursForThrowable(Throwable th) {
// retrieve the list of exception error messages
Throwable cause = th;
List<String> erreurs = new ArrayList<>();
while (cause != null) {
// the message is retrieved only if it is !=null and not blank
String message = cause.getMessage();
if (message != null) {
message = message.trim();
if (message.length() != 0) {
erreurs.add(message);
}
}
// next cause
cause = cause.getCause();
}
return erreurs;
}
}
- lines 21–31: retrieving a reference to the [DAO] layer;
- line 21: we use a syntax called try_with_resources. Its syntax is as follows:
try (T ressource=expression) {
// resource exploitation
...
}
- (continued)
- The type T in line 1 must implement the interface [java.lang.AutoCloseable];
- at the end of the block in lines 1–4, the resource of type [java.lang.AutoCloseable] is automatically released regardless of whether an exception occurred or not. Those familiar with the C# language will recognize this as a syntactic and functional counterpart to the using clause (T resource = expression);
- lines 40–49: use of the [DAO] layer to retrieve the contents of the [CONF] and [LISTES] tables;
- lines 41–47: the [electionsconfig] cache is tested. This cache has been defined in two places:
- in the [ElectionsDaoJdbc] class:
@Cacheable("electionsConfig")
public ElectionsConfig getElectionsConfig() {
- (continued)
- in the configuration class [AppConfig]:
@EnableCaching
public class AppConfig {
...
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("electionsConfig");
}
}
- line 59: closing the Spring context;
- lines 62–67: display of the retrieved information;
The results obtained with an implemented [DAO] layer are as follows:
- Lines 2–4: show the impact of the cache:
- Query 1 takes 80 milliseconds;
- Query 2 takes 1 millisecond;
When the database is down, the following results are obtained:
7.10. Tests JUnit of class [ElectionsDaoJdbc]
![]() |
![]() |
The class [Test01] is the following test class [JUnit]:
package elections.dao.junit;
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 elections.dao.config.AppConfig;
import elections.dao.entities.ElectionsConfig;
import elections.dao.entities.ListeElectorale;
import elections.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]
// competitive listings
ListeElectorale[] listes = electionsDao.getListesElectorales();
// set votes and seats to 0 and eliminate to 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);
}
// make this data persistent using the [dao] layer
electionsDao.setListesElectorales(listes);
}
@Test
public void testElections01() {
...
}
}
- line 17: the annotation [RunWith], which is an annotation of [JUnit] (line 6), ensures integration with Spring via the class [SpringJUnit4ClassRunner];
- line 16: the [SpringApplicationConfiguration] annotation is a [Spring] annotation (line 8) that allows you to specify the configuration classes for the JUnit test. Here, we specify the [AppConfig] class used to configure the project. We then have access to all the Spring objects defined by this configuration class. This is how we can inject, on lines 21–22, a reference to the [DAO] layer that will be tested;
- line 25: the [Before] annotation designates a method that must be executed before each test;
- lines 26–41: The method [init] sets the votes and seats in the lists of table [LISTES] to zero and the boolean [elimine] to [false];
The only test method is as follows:
@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);
// competitive listings
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;
}
// 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 : listesElectorales2) {
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]);
}
}
- lines 5-9: we verify that we can retrieve the contents of table [CONF];
- lines 11-19: we display the contents of table [LISTES]. There are no tests here, only a visual check;
- lines 21–35: we modify the [LISTES] table in the database by assigning values to the lists for their [voix, sieges, elimine] fields;
- lines 37–51: we read the [LISTES] table again and verify that the result matches what was entered;
- lines 52–55: visual verification;
The console results obtained with an implemented [DAO] layer are as follows:
- lines 1-23: Spring Test logs;
- lines 25-26: the contents of table [CONF];
- lines 27-34: the initial contents of table [LISTES];
- lines 35-42: the contents of table [LISTES] after assigning values to the fields in [voix, sieges, elimine];
In addition, the JUnit test succeeds:
![]() |
7.11. Creation of the [with-dependencies] archive from the [dao] layer
The final project has the following architecture:
![]() |
Here is the project’s Maven configuration:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-dao-jdbc-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.7.RELEASE</version>
</parent>
<dependencies>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Tomcat Jdbc -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
<!-- library jSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring Boot Logging -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
<!-- plugins -->
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>config.AppConfig</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- to install the project artifact in the local Maven repository -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</plugin>
</plugins>
</build>
</project>
![]() |
We will generate a single jar file that will contain the classes from all of the above JARs plus those from the [DAO] layer project. This is done by making a modification to the [pom.xml] file:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.elections</groupId>
<artifactId>elections-jdbc-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
...
</dependencies>
<!-- plugins -->
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>console.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
- lines 25–37: configure a Maven plugin to generate the jar;
- line 15: specifies the Java version to use for compilation;
Once this change is made, the jar can be generated as follows: [1-10]:
![]() |
![]() |
- In [5], select the project folder using the [6] button;
- In [7], name the build configuration;
- In [8], enter the list of Maven goals to execute:
- [clean]: clears the [target] folder of the project where the generated jar will be placed;
- [compile]: compiles the project;
- [assembly:single]: generates a single jar file containing all the classes of the project and its dependencies;
![]() |
- In [9-10], verify that you have a JDK (Java Development Kit) and not a JRE (Java Runtime Environment). The difference is that JDK includes a compiler, whereas JRE does not. If you do not have JDK, you must add one in [10] with [11]. To do this, follow the procedure described in section 3.1;
![]() |
- In [17], run the execution configuration;
- In [13], the generated archive;
You can open this archive with an unzipper:
![]() |
7.12. Testing the [DAO] layer archive
Let’s create a standard Eclipse project (not Maven) [1]:
![]() |
Let’s copy and paste the [dao.console] package from the [elections-dao-jdbc-01] project into the [elections-dao-jdbc-02] [2] project. Numerous errors appear because the [Main] class references classes that are not in its [Classpath]. We will modify this one.
First, we create a folder named [2-8] in the new project:
![]() |
![]() |
In [9], we place the archive created in the previous step into the [lib] folder, then we modify the project’s Build Path:
![]() |
![]() |
![]() |
- In [18], we have imported the archive of the layer [DAO] created previously;
![]() |
- In [19], the project no longer has any errors;
We can then run the [Main] class. We obtain the same results as before.







































