JPA实体映射——一对多关系映射(上)

时间:2022-06-01 12:59:25

依照上一节的案例,我们来拆解一对多的关系怎么使用,首先还是把业务关系图弄出来。

业务案例图

JPA实体映射——一对多关系映射(上)

业务分析

从图中我们知道,研究所和部门是一对多关系,今天我们来展示,一对多设计的最佳实践。

我们知道在JPA的实体设计中,一对多的关系可以设计成单向关联,也可以设计成双向关联。今天我们一步一步来实践各种设计,从而总结出最佳实践。

Unidirectional @OneToMany

 

研究所实体

port javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "institutes")
public class Institute implements Serializable {

    @Id
    @GeneratedValue
    private Long id;
    @OneToMany(
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    private Set<Department> departments = new HashSet<>(0);

    private String name;

    public Institute(){
    }

    public Institute(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Set<Department> getDepartments() {
        return departments;
    }

    public void setDepartments(Set<Department> departments) {
        this.departments = departments;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

部门实体

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;


@Entity
@Table(name = "departments")
public class Department implements Serializable {

    @Id
    @GeneratedValue
    private Long id = 0L;

    private String name;

    public Department(){}

    public Department(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

研究所DAO

import com.jpa.demo.model.undirectional.Institute;
import com.jpa.demo.utils.JPAUtil;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;

public class InstituteDao {

    private EntityManagerFactory entityManagerFactory = JPAUtil.getEntityManagerFactory();
    public Long save(Institute institute) {
        EntityManager entityManager = null;
        Long id = null;
        try {
            entityManager = this.entityManagerFactory.createEntityManager();
            EntityTransaction tx = entityManager.getTransaction();
            tx.begin();
            entityManager.persist(institute);
            id = institute.getId();
            tx.commit();
        } finally {
            entityManager.close();
        }
        return id;
    }
}

工具类就不展示啦,可以查看https://devnote.pro/noteweb/docs/10000020363496/contents/10000063133496

测试类

import com.jpa.demo.model.undirectional.Department;
import com.jpa.demo.model.undirectional.Institute;
import org.junit.Test;

public class InstituteDaoTest {

    @Test
    public void testSaveInstitute(){
        Institute institute = new Institute("深圳研究所");
        institute.getDepartments().add(new Department("深圳研究所1部"));
        institute.getDepartments().add(new Department("深圳研究所2部"));
        institute.getDepartments().add(new Department("深圳研究所3部"));

        InstituteDao dao = new InstituteDao();
        dao.save(institute);
    }
}

查看日志信息如下:

Hibernate: 
    
    create table departments (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    
    create table institutes (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    
    create table institutes_departments (
       Institute_id bigint not null,
        departments_id bigint not null,
        primary key (Institute_id, departments_id)
    )

Hibernate: 
    
    alter table institutes_departments 
       drop constraint if exists UK_67unewuyoqel2nu2ef8me1hyv
Hibernate: 
    
    alter table institutes_departments 
       add constraint UK_67unewuyoqel2nu2ef8me1hyv unique (departments_id)
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 
    
    alter table institutes_departments 
       add constraint FK3lq3buf7j6k7is3q1lurp2j91 
       foreign key (departments_id) 
       references departments
Hibernate: 
    
    alter table institutes_departments 
       add constraint FKlqld2ri9e0qmvhwy6nnany0o9 
       foreign key (Institute_id) 
       references institutes

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        institutes
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        institutes_departments
        (Institute_id, departments_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        institutes_departments
        (Institute_id, departments_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        institutes_departments
        (Institute_id, departments_id) 
    values
        (?, ?)

首先,我们知道单向数据关联,生成了三张表分别是departmentsinstitutes还有institutes_departments

然后在中间表的department_id上建立唯一约束,最后在中间表的字段上建立外键,分别指向各自的表主键。

我在保存数据库的时候,首先在institutes上插入一条数据,然后在departments上插入三条数据,最后插入institutes_departments表对应的数据,这样这个一对多的关系就建立起来。

这种设计显然存在很多问题:

1、生成了一个冗余的中间表

2、发出了多条SQL执行语句

3、建立了两个外键,我们需要更多的缓存

 

下面我们看看单向关系的另一种@OneToMany

Unidirectional @OneToMany with @JoinColumn

这次我们只需要增加一个注解

研究所实体修改

@OneToMany(
            cascade = CascadeType.ALL,
            orphanRemoval = true
    )
    @JoinColumn(name = "department_id")
    private Set<Department> departments = new HashSet<>(0);

同理,我们执行测试方法后,日志信息如下:

Hibernate: 
    
    create table departments (
       id bigint not null,
        name varchar(255),
        department_id bigint,
        primary key (id)
    )
Hibernate: 
    
    create table institutes (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: 
    
    alter table departments 
       add constraint FK3ttxjsckxb3vwld1idf7a3r29 
       foreign key (department_id) 
       references institutes

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        institutes
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        departments
        (name, id) 
    values
        (?, ?)
Hibernate: 
    update
        departments 
    set
        department_id=? 
    where
        id=?
Hibernate: 
    update
        departments 
    set
        department_id=? 
    where
        id=?
Hibernate: 
    update
        departments 
    set
        department_id=? 
    where
        id=?

从日志信息可以看出,这次只生成了两个表institutesdepartments,并且在departments上多了一个department_id,这个字段实际上就是我们在研究所实体上配置的外键。从SQL执行可以看出,插入的语句没有啥变化,只不过多了三个更新外键字段的语句。总的说来,有所改善,但是效果不是最好。

今天我们暂时学习到这里,当然这里还出有一些有意思的问题需要探讨。我们下一节继续。欢迎讨论。