1.对象设计
双向多对一,一对多,表的结构应该是一样的,建立两个关联关系,可以从多方找到一方,亦可以从一方找到多方。
Employee 实体
@Entity
@Setter
@Getter
public class Employee {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Department 实体
@Getter
@Setter
@Entity
public class Department {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany()//建立关联一对多
@JoinColumn(name = "dept_id")//指定关联列的名字
private List employees = new ArrayList<>();
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
2.表结构
![E}PW@OAA$5JBZ}}U~3M6BX.png
从图中可以看出,这并不是双向关系,而是两个单向关系,因为两个外键列的名字不一样,此时在多方有两个外键分别对应单向多对一和单向一对多。
解决办法:
因为多对一可以自动生成外键列,一对多必须加上@JoinColumn注解指定外键列,否则会生成中间表,浪费性能。此时使外键列的名字相同即可。
@Entity
@Setter
@Getter
public class Employee {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "dept_id")
private Department department;
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
@Getter
@Setter
@Entity
public class Department {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany()//建立关联一对多
@JoinColumn(name = "dept_id")//指定关联列的名字
private List employees = new ArrayList<>();
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
生成的表结构:
可以看出这是正常的表结构
3.保存
(1):先保存部门,再保存员工
@Test
public void testSave(){
EntityManager entityManager = JPAUtil.createEntityManager();
//准备一方
Department department = new Department();
department.setName("策划部");
//准备多方
Employee e1 = new Employee();
e1.setName("alice");
Employee e2 = new Employee();
e2.setName("yukki");
//建立关联关系
department.getEmployees().add(e1);
department.getEmployees().add(e2);
e1.setDepartment(department);
e2.setDepartment(department);
//持久化
entityManager.getTransaction().begin();
entityManager.persist(department);
entityManager.persist(e1);
entityManager.persist(e2);
entityManager.getTransaction().commit();
entityManager.close();
}
(2):先保存部门,再保存员工
@Test
public void testSave(){
EntityManager entityManager = JPAUtil.createEntityManager();
//准备一方
Department department = new Department();
department.setName("策划部");
//准备多方
Employee e1 = new Employee();
e1.setName("alice");
Employee e2 = new Employee();
e2.setName("yukki");
//建立关联关系
department.getEmployees().add(e1);
department.getEmployees().add(e2);
e1.setDepartment(department);
e2.setDepartment(department);
//持久化
entityManager.getTransaction().begin();
entityManager.persist(e1);
entityManager.persist(e2);
entityManager.persist(department);
entityManager.getTransaction().commit();
entityManager.close();
}
4.SQL分析
(1):先保存部门,再保存员工
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (dept_id, name) values (?, ?)
Hibernate: insert into Employee (dept_id, name) values (?, ?)
Hibernate: update Employee set dept_id=? where id=?
Hibernate: update Employee set dept_id=? where id=?
执行了5条SQL
(2):先保存员工,再保存部门
Hibernate: insert into Employee (dept_id, name) values (?, ?)
Hibernate: insert into Employee (dept_id, name) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set dept_id=?, name=? where id=?
Hibernate: update Employee set dept_id=?, name=? where id=?
Hibernate: update Employee set dept_id=? where id=?
Hibernate: update Employee set dept_id=? where id=?
执行了7条SQL,添加员工信息,没有部门信息,添加部门,此时更新建立关联关系,多方要维护关系,一方也要维护关系,就多执行了两句SQL。
5.正确配置
一方放弃关系维护,将关系维护权交给多方!不需要添加@JoinColumn(name = "dept_id")外键列信息!在@OneToMany注解中添加参数mappedBy = "多方中的属性"!
@Getter
@Setter
@Entity
public class Department {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "department")
private List employees = new ArrayList<>();
@Override
public String toString() {
return "Department{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
6.SQL分析
(1):先保存员工,再保存部门
Hibernate: insert into Employee (department_id, name) values (?, ?)
Hibernate: insert into Employee (department_id, name) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set department_id=?, name=? where id=?
Hibernate: update Employee set department_id=?, name=? where id=?
(2):先保存部门,再保存员工
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (department_id, name) values (?, ?)
Hibernate: insert into Employee (department_id, name) values (?, ?)
7.查询
@Test
public void testFind(){
EntityManager entityManager = JPAUtil.createEntityManager();
//根据一方查找多方
Department department = entityManager.find(Department.class, 1l);
//System.out.println(department.getEmployees());
entityManager.close();
}
8.执行的SQL
Hibernate : SELECT
department0_.id AS id1_0_0_,
department0_. NAME AS name2_0_0_
FROM
Department department0_
WHERE
department0_.id =?
根据一方查找多方的时候,并没有使用多方的信息,此时并没有发送查询多方的SQL,说明是延迟加载!
9.小结
1.双向多对一,一对多的配置需要一方放弃关系维护,将关系维护权交给多方,在@OneToMany注解中添加参数mappedBy = "多方中的属性"。
2.先添加一方,再添加多方,可以减少SQL发送,提高性能!
3.双向多对一,一对多默认是延迟加载!