依照上一节的案例,我们来拆解一对多的关系怎么使用,首先还是把业务关系图弄出来。
业务案例图
业务分析
从图中我们知道,研究所和部门是一对多关系,今天我们来展示,一对多设计的最佳实践。
我们知道在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
(?, ?)
首先,我们知道单向数据关联,生成了三张表分别是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<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=?
从日志信息可以看出,这次只生成了两个表institutes
和departments
,并且在departments上多了一个department_id,这个字段实际上就是我们在研究所实体上配置的外键。从SQL执行可以看出,插入的语句没有啥变化,只不过多了三个更新外键字段的语句。总的说来,有所改善,但是效果不是最好。
今天我们暂时学习到这里,当然这里还出有一些有意思的问题需要探讨。我们下一节继续。欢迎讨论。