美文网首页
Spring Boot JPA @OneToOne

Spring Boot JPA @OneToOne

作者: 又语 | 来源:发表于2020-06-25 19:08 被阅读0次

    本文介绍 Spring Boot JPA @OneToOne 的使用方法。


    目录

    • 开发环境
    • 基础示例

    开发环境

    • JDK 8
    • MySQL 8

    基础示例

    1. 创建数据表。
    CREATE SCHEMA `test` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
    
    USE `test`;
    
    CREATE TABLE `student` (
      `id`        BIGINT     UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
      `number`    CHAR(10)            NOT NULL                COMMENT '学号',
      `name`      VARCHAR(30)         NOT NULL                COMMENT '姓名',
      `family_id` TINYINT    UNSIGNED NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE,
      UNIQUE INDEX `number_UNIQUE` (`number` ASC) VISIBLE)
    COMMENT = '学生表';
    
    CREATE TABLE `family` (
      `id`     BIGINT      UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
      `father` VARCHAR(30)          NOT NULL                COMMENT '父亲姓名',
      `mother` VARCHAR(30)          NOT NULL                COMMENT '母亲姓名',
      PRIMARY KEY (`id`),
      UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE);
    
    1. 如何创建 Spring Boot JPA 工程请参考:https://www.jianshu.com/p/e2b64d5c6107

    2. 创建 PO(Persistence Object) 对象。

    package tutorial.spring.boot.domain;
    
    import javax.persistence.*;
    
    @Entity
    @Table(name = "family")
    public class FamilyPO {
    
        /**
         * 自增主键
         */
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        private String father;
    
        private String mother;
    
        // Getter、Setter、toString 方法略
    }
    
    package tutorial.spring.boot.domain;
    
    import javax.persistence.*;
    
    @Entity
    @Table(name = "student")
    public class StudentPO {
    
        /**
         * 自增主键
         */
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        /**
         * 学号
         */
        private String number;
    
        /**
         * 姓名
         */
        private String name;
    
        @OneToOne(cascade = CascadeType.ALL)
        @JoinColumn(name = "family_id")
        private FamilyPO family;
    
        // Getter、Setter、toString 方法略
    }
    

    注意:@JoinColumn(name = "family_id") 可以省略。

    1. 创建继承 JpaRepository 的 Repository 接口类。
    package tutorial.spring.boot.dao;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    import tutorial.spring.boot.domain.FamilyPO;
    
    @Repository
    public interface FamilyRepository extends JpaRepository<FamilyPO, Long> {
    }
    
    package tutorial.spring.boot.dao;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    import tutorial.spring.boot.domain.StudentPO;
    
    @Repository
    public interface StudentRepository extends JpaRepository<StudentPO, Long> {
    }
    
    1. 编写单元测试。
    package tutorial.spring.boot.dao;
    
    import org.apache.commons.lang3.RandomStringUtils;
    import org.assertj.core.api.Assertions;
    import org.junit.jupiter.api.MethodOrderer;
    import org.junit.jupiter.api.Order;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.TestMethodOrder;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import tutorial.spring.boot.domain.FamilyPO;
    import tutorial.spring.boot.domain.StudentPO;
    
    import java.util.List;
    import java.util.Objects;
    
    @SpringBootTest
    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    class StudentRepositoryTest {
    
        @Autowired
        private FamilyRepository familyRepository;
    
        @Autowired
        private StudentRepository studentRepository;
    
        /**
         * 测试 [CascadeType.ALL] 条件下的级联新增
         */
        @Test
        @Order(1)
        void testInsert() {
            // family 和 student 表中都没有记录
            Assertions.assertThat(familyRepository.count()).isEqualTo(0);
            Assertions.assertThat(studentRepository.count()).isEqualTo(0);
            // 创建 family 和 student 实体对象并将其绑定起来
            StudentPO student = new StudentPO();
            student.setNumber(RandomStringUtils.randomAlphanumeric(10));
            student.setName(RandomStringUtils.randomAlphanumeric(2, 20));
            FamilyPO family = new FamilyPO();
            family.setFather(RandomStringUtils.randomAlphanumeric(2, 20));
            family.setMother(RandomStringUtils.randomAlphanumeric(2, 20));
            student.setFamily(family);
            // 执行保存 student 实体对象的操作
            studentRepository.save(student);
            // family 和 student 表中各新增一条记录,且新增记录信息与保存的 student 实体对象信息一致
            List<FamilyPO> families = familyRepository.findAll();
            Assertions.assertThat(families.size()).isEqualTo(1);
            List<StudentPO> students = studentRepository.findAll();
            Assertions.assertThat(students.size()).isEqualTo(1);
            Assertions.assertThat(students.get(0).getNumber()).isEqualTo(student.getNumber());
            Assertions.assertThat(students.get(0).getName()).isEqualTo(student.getName());
            Assertions.assertThat(students.get(0).getFamily().getFather()).isEqualTo(families.get(0).getFather());
            Assertions.assertThat(students.get(0).getFamily().getMother()).isEqualTo(families.get(0).getMother());
            Assertions.assertThat(students.get(0).getFamily().getId()).isEqualTo(families.get(0).getId());
        }
    
        /**
         * 测试 [CascadeType.ALL] 条件下的级联更新
         * 只更新级联关系被持有方属性
         */
        @Test
        @Order(2)
        void testUpdate1() {
            // 依赖于之前的单元测试,此时 student 和 family 表中应各有一条记录
            List<FamilyPO> families = familyRepository.findAll();
            Assertions.assertThat(families.size()).isEqualTo(1);
            List<StudentPO> students = studentRepository.findAll();
            Assertions.assertThat(students.size()).isEqualTo(1);
            StudentPO student = students.get(0);
            FamilyPO family = families.get(0);
            // 修改 student 信息及关联的 family 信息后保存
            String originalStudentNumber = student.getNumber();
            student.setNumber(originalStudentNumber.substring(1));
            String originalStudentName = student.getName();
            student.setName(originalStudentName + RandomStringUtils.randomAlphabetic(1));
            student.getFamily().setFather(student.getFamily().getFather() + RandomStringUtils.randomAlphabetic(1));
            student.getFamily().setMother(student.getFamily().getMother() + RandomStringUtils.randomAlphabetic(2));
            studentRepository.save(student);
            // 保存后 student 和 family 表中还应只有一条记录
            families = familyRepository.findAll();
            Assertions.assertThat(families.size()).isEqualTo(1);
            students = studentRepository.findAll();
            Assertions.assertThat(students.size()).isEqualTo(1);
            // 校验 student 及关联的 family 信息已更新且与原来不同
            FamilyPO findFamilyResult = families.get(0);
            StudentPO findStudentResult = students.get(0);
            Assertions.assertThat(findStudentResult.getNumber())
                    .isEqualTo(student.getNumber())
                    .isNotEqualTo(originalStudentNumber);
            Assertions.assertThat(findStudentResult.getName())
                    .isEqualTo(student.getName())
                    .isNotEqualTo(originalStudentName);
            Assertions.assertThat(findStudentResult.getFamily().getFather())
                    .isEqualTo(student.getFamily().getFather())
                    .isEqualTo(findFamilyResult.getFather())
                    .isNotEqualTo(family.getFather());
            Assertions.assertThat(findStudentResult.getFamily().getMother())
                    .isEqualTo(student.getFamily().getMother())
                    .isEqualTo(findFamilyResult.getMother())
                    .isNotEqualTo(family.getMother());
        }
    
        /**
         * 测试 [CascadeType.ALL] 条件下的级联更新
         * 重新绑定级联关系被持有方
         */
        @Test
        @Order(3)
        void testUpdate2() {
            // 依赖于之前的单元测试,此时 student 和 family 表中应各有一条记录
            List<FamilyPO> families = familyRepository.findAll();
            Assertions.assertThat(families.size()).isEqualTo(1);
            List<StudentPO> students = studentRepository.findAll();
            Assertions.assertThat(students.size()).isEqualTo(1);
            StudentPO student = students.get(0);
            FamilyPO family = families.get(0);
            // 为 student 重新绑定 family 并保存
            FamilyPO newFamily = new FamilyPO();
            newFamily.setFather(family.getFather() + RandomStringUtils.randomAlphabetic(2));
            newFamily.setMother(family.getMother() + RandomStringUtils.randomAlphabetic(1));
            student.setFamily(newFamily);
            studentRepository.save(student);
            // family 表应该保留之前绑定的 family 信息并新增新绑定的 family 信息
            families = familyRepository.findAll();
            Assertions.assertThat(families.size()).isEqualTo(2);
            students = studentRepository.findAll();
            Assertions.assertThat(students.size()).isEqualTo(1);
            StudentPO findStudentResult = students.get(0);
            FamilyPO findOldFamilyResult;
            FamilyPO findNewFamilyResult;
            if (Objects.equals(families.get(0).getId(), findStudentResult.getFamily().getId())) {
                findNewFamilyResult = families.get(0);
                findOldFamilyResult = families.get(1);
            } else {
                findNewFamilyResult = families.get(1);
                findOldFamilyResult = families.get(0);
            }
            Assertions.assertThat(findOldFamilyResult.getId()).isEqualTo(family.getId());
            Assertions.assertThat(findOldFamilyResult.getFather()).isEqualTo(family.getFather());
            Assertions.assertThat(findOldFamilyResult.getMother()).isEqualTo(family.getMother());
            Assertions.assertThat(findNewFamilyResult.getId()).isEqualTo(findStudentResult.getFamily().getId());
            Assertions.assertThat(findNewFamilyResult.getFather()).isEqualTo(findStudentResult.getFamily().getFather());
            Assertions.assertThat(findNewFamilyResult.getMother()).isEqualTo(findStudentResult.getFamily().getMother());
        }
    
        @Test
        @Order(4)
        void testDelete() {
            // 依赖于之前的单元测试,此时 student 表中应有一条记录,family 表中应有两条记录
            List<FamilyPO> families = familyRepository.findAll();
            Assertions.assertThat(families.size()).isEqualTo(2);
            List<StudentPO> students = studentRepository.findAll();
            Assertions.assertThat(students.size()).isEqualTo(1);
            FamilyPO familyNoUsed;
            if (Objects.equals(families.get(0).getId(), students.get(0).getFamily().getId())) {
                familyNoUsed = families.get(1);
            } else {
                familyNoUsed = families.get(0);
            }
            // 执行删除 student 操作
            studentRepository.delete(students.get(0));
            Assertions.assertThat(studentRepository.count()).isEqualTo(0);
            families = familyRepository.findAll();
            Assertions.assertThat(families.size()).isEqualTo(1);
            Assertions.assertThat(families.get(0).getId()).isEqualTo(familyNoUsed.getId());
            Assertions.assertThat(families.get(0).getFather()).isEqualTo(familyNoUsed.getFather());
            Assertions.assertThat(families.get(0).getMother()).isEqualTo(familyNoUsed.getMother());
        }
    }
    

    说明:
    因为使用了 CascadeType.ALL,级联关系持有方拥有被持有方的所有权限,本例中 StudentPO 是级联关系持有方,FamilyPO 是级联关系的被持有方,因此 StudentPO 拥有 FamilyPO 的所有权限,测试用例中创建的两个 PO 对象本来都不存在于数据库中,因为 StudentPO 拥有 FamilyPO 的所有权限,所以保存 StudentPO 实例时也同时保存了 FamilyPO 实例,有关级联更新和删除详情请参考测试用例。

    相关文章

      网友评论

          本文标题:Spring Boot JPA @OneToOne

          本文链接:https://www.haomeiwen.com/subject/fbzsfktx.html