Skip to content

12. [TD]:使用 [Spring Data] 实现 [DAO] 层

关键词:多层架构、Spring、依赖注入、API JPA(Java Persistence API)、Spring Data。

我们将遵循与之前相同的步骤来实现 TD 的 [DAO] 层。

12.1. Support

  • 在 [1] 中,[support / chap-12] 文件夹包含本章的 Eclipse 项目;

12.2. Eclipse 项目

Eclipse 项目如下:

  
  • [elections.dao.config]:包含 Spring 项目的配置类;
  • [elections.dao.entities]:包含实体 JPA 以及项目的异常类;
  • [elections.dao.repositories]:包含用于表 [CONF] 和 [LISTES] 的接口 [CrudRepository];
  • [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;
        }
    }

    // 获取器和设置器
...
}

这是第 11.3.5.2 节中描述的类,但不包含 jSON 过滤器。实际上,在此处,表 [CONF] 和 [LISTES] 之间不存在外键关系。 而正是与模式 [lazy loading] 之间存在此类关系,才导致了需要使用过滤器 jSON。

12.4.3. 类 [ElectionsConfig]

  

类 [ElectionsConfig] 是与表 [CONF] 关联的实体 JPA;


待完成任务:构建类 [ElectionsConfig]。


12.4.4. 类 [ListeElectorale]

  

类 [ListeElectorale] 是与表 [LISTES] 关联的实体 JPA;


待完成任务:构建类 [ListeElectorale]。


12.5. [Spring Data] 层

  

任务:编写 [Spring Data] 的两个接口,用于管理 [CONF] 和 [LISTES] 这两个表;


12.6. [DAO] 层

  

[DAO] 层的 [IElectionsDao] 接口如下:


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);
}

待完成任务:编写接口 [IElectionsDao] 的实现类 [ElectionsDaoJpa]。


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]);
        }
    }
}

待完成任务:在您的 [DAO] 层上通过 [console] 和 [JUnit] 测试。


12.9. 生成项目的 Maven 归档文件

参照第 11.3.12 节的示例,生成项目的 Maven 归档文件。