12. [TD]: Реалізація шару [DAO] з TD за допомогою [Spring Data]
Ключові слова: багатошарова архітектура, Spring, ін’єкція залежностей, API JPA (Java Persistence API), Spring Data.
Ми будемо дотримуватися того самого підходу, що й раніше, для реалізації шару [DAO] у TD.
12.1. Support
![]() |
- у [1], папка [support / chap-12] містить проект Eclipse для цього розділу;
12.2. Проєкт Eclipse
Проєкт Eclipse матиме такий вигляд:
![]() |
- [elections.dao.config]: містить клас конфігурації проекту Spring;
- [elections.dao.entities]: містить сутності JPA, а також клас винятків проекту;
- [elections.dao.repositories]: містить інтерфейси [CrudRepository] для таблиць [CONF] та [LISTES];
- [elections.dao.service]: містить реалізацію шару [DAO]. Саме її нам і потрібно написати;
- [elections.dao.console]: містить тестовий клас типу [console];
- [pom.xml]: файл конфігурації проекту Maven;
Цей проєкт реалізує таку архітектуру:
![]() |
Рівень [DAO] бачить лише рівень, реалізований [Spring Data].
12.3. Конфігурація Maven
Завдання: створіть файл [pom.xml] для цього проєкту.
12.4. Елементи шару [JPA]
![]() |
![]() |
- [ElectionsConfig] — це об’єктна модель, пов’язана з рядком таблиці [CONF];
- [ListeElectorale] — це об’єктна модель, пов’язана з рядком таблиці [LISTES];
- [AbstactEntity] — це батьківський клас для двох попередніх класів. Він об’єднує поля [id, version], спільні для обох класів;
- [ElectionsException] — це клас винятків проекту;
12.4.1. Клас [ElectionsException]
![]() |
Клас [ElectionsException] є неконтрольованим винятком, описаним у розділі 4.3.
12.4.2. Клас [AbstractEntity]
![]() |
Клас [AbstractEntity] є таким:
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;
// властивості
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
protected Long id;
@Version
@Column(name = "VERSION")
protected Long version;
// конструктори
public AbstractEntity() {
}
public AbstractEntity(Long id, Long version) {
this.id = id;
this.version = version;
}
// перевизначення [equals] та [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
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
// методи getter та setter
...
}
Це клас, описаний у параграфі 11.3.5.2, без фільтрів jSON. Адже тут таблиці [CONF] та [LISTES] не пов’язані відношенням зовнішнього ключа. Однак саме наявність такого зв’язку з режимом [lazy loading] зумовлює необхідність фільтрів jSON.
12.4.3. Клас [ElectionsConfig]
![]() |
Клас [ElectionsConfig] — це суть JPA, пов’язана з таблицею [CONF];
Завдання: створіть клас [ElectionsConfig].
12.4.4. Клас [ListeElectorale]
![]() |
Клас [ListeElectorale] — це суть JPA, пов’язана з таблицею [LISTES];
Завдання: створіть клас [ListeElectorale].
12.5. Рівень [Spring Data]
![]() |
Завдання: напишіть два інтерфейси для [Spring Data] для управління двома таблицями [CONF] та [LISTES];
12.6. Рівень [DAO]
![]() |
Інтерфейс [IElectionsDao] шару [DAO] має такий вигляд:
package dao.service;
import dao.entities.ElectionsConfig;
import dao.entities.ListeElectorale;
public interface IElectionsDao {
// конфігурація виборів
public ElectionsConfig getElectionsConfig();
// списки кандидатів
public ListeElectorale[] getListesElectorales();
// оновлення списків кандидатів
public void setListesElectorales(ListeElectorale[] listesElectorales);
}
Завдання: напишіть реалізацію [ElectionsDaoJpa] інтерфейсу [IElectionsDao].
12.7. Налаштування проекту Spring
Клас [DaoConfig] налаштовує проект Spring.
Завдання: напишіть клас [DaoConfig]
12.8. Шар [console]
![]() |
![]() |
Клас [Main] є наступним виконуваним класом:
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 {
// джерело даних
private static IElectionsDao dao;
public static void main(String[] args) {
// отримання контексту Spring
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// отримання джерела даних
dao = ctx.getBean(IElectionsDao.class);
// вміст обох таблиць
ElectionsConfig electionsConfig = dao.getElectionsConfig();
ListeElectorale[] listes = dao.getListesElectorales();
// відображення
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
ctx.close();
}
}
Клас [Test01] є тестом 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 {
// шар [DAO]
@Autowired
private IElectionsDao electionsDao;
@Before
public void init() {
// очищення таблиці [LISTES]
// списки кандидатів
ListeElectorale[] listes = electionsDao.getListesElectorales();
// обнуляємо кількість голосів і місць та встановлюємо значення «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);
}
// збереження цих даних за допомогою шару [dao]
electionsDao.setListesElectorales(listes);
}
@Test
public void testElections01() {
System.out.println("testElections01-------------------------------------");
// отримання конфігурації виборів
ElectionsConfig electionsConfig = electionsDao.getElectionsConfig();
int nbSiegesAPourvoir = electionsConfig.getNbSiegesAPourvoir();
double seuilElectoral = electionsConfig.getSeuilElectoral();
Assert.assertEquals(6, nbSiegesAPourvoir);
Assert.assertEquals(0.05, seuilElectoral, 1E-6);
// списки кандидатів
ListeElectorale[] listes = electionsDao.getListesElectorales();
// відображення зчитаних значень
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]);
}
// присвоєння голосів і місць спискам
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;
}
// ці дані зберігаються за допомогою шару [dao]
electionsDao.setListesElectorales(listes);
// повторне зчитування даних
ListeElectorale[] listesElectorales2 = electionsDao.getListesElectorales();
// перевіряються зчитані дані
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]);
}
}
}
Завдання: виконайте тести [console] та [JUnit] на вашому шарі [DAO].
12.9. Створення архіву Maven для проєкту
Дотримуючись прикладу з розділу 11.3.12, створіть архів Maven для проекту.












