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

百科知识2025-04-261

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

业务案例图

###业务分析

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

我们知道在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 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 getDepartments() { return departments; } public void setDepartments(Set 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 (?, ?)

首先,我们知道单向数据关联,生成了三张表分别是departments,institutes还有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 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=?

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

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