JPA对象关系-双向多对一
双向多对一(很少用)
- 双向多对一就是 通过A对象可以找到B对象,同理通过B对象可以找到A对象,他们之间互相依赖。
- 既然他们互相依赖,在JSON序列化或ToString时就会报出栈内存溢出,并且业务上也很少这样用,基本都是通过主表-找到子表即可,不会通过子表再去查主表。JSON序列化异常,可以通过
@JsonBackReference
注解解决。 - 双向多对一和双向一对多其实是一样的,这里只用双向多对一说明使用方式。
- 我们知道单向多对一会在多方添加一列,单向一对多会生成1个中间关系表,那么JPA对双向多对一是如何维护的?
@JoinColumn
手动维护关系,告诉JPA不需要创建关系。
废话不多数,看下面代码
1、实体类定义
@Data
@Entity
public class Employee {
@Id
@GeneratedValue
private long id;
private String name;
@ManyToOne
private Department department;
}
@Data
@Entity
public class Department {
@Id
@GeneratedValue
private long id;
private String name;
@OneToMany
private List<Employee> employees;
}
2、persistence.xml 配置
<class>com.hongying.entity.many2many.Department</class>
<class>com.hongying.entity.many2many.Employee</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
3、单元测试类
@Test
public void save(){
Department d1=new Department();
d1.setName("人事部");
Employee gg=new Employee();
gg.setName("gg");
Employee jl=new Employee();
jl.setName("jl");
//设置关系
List<Employee> employees=new ArrayList<>();
employees.add(gg);
employees.add(jl);
d1.setEmployees(employees);
gg.setDepartment(d1);
jl.setDepartment(d1);
//保存
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
entityManager.persist(d1);
entityManager.persist(gg);
entityManager.persist(jl);
entityManager.getTransaction().commit();
entityManager.close();
}
4、控制台输出
分析:先保存部门,部门ID就有了,发现在保存员工时,department_id插入到员工表了;员工保存后,提交事务时,会发现员工ID有了,部门中员工属性发生变化,从而会中间表插入2条关系;结果就是下面了
从这些SQL语句可知,JPA在@ManyToOne关系维护了1列,在@OneToMany关系又维护了个中间表;
Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (department_id, name) values (?, ?)
Hibernate: insert into Employee (department_id, name) values (?, ?)
Hibernate: insert into Department_Employee (Department_id, employees_id) values (?, ?)
Hibernate: insert into Department_Employee (Department_id, employees_id) values (?, ?)
5、JPA维护了2个关系
现在对于双向多对一,JPA维护了2个关系,但是我们业务上只需要1个关系就可以了,多出来1个关系也没有意义,并且一旦2个关系不一致了,还不知道应该以哪一方为准。此时如何解决
6、用mappedBy
解决双向多对一出现2个关系问题
解决方法很简单,就是让1方放弃关系的维护,通常我们都是让N方放弃关系的维护,这样不用生成一个中间表了。在`@OneToMany 上添加mappedBy属性
@Data
@Entity
public class Department {
@Id
@GeneratedValue
private long id;
private String name;
//mappedBy 作用:
//1、然N方,放弃关系的维护,不创建中间关系表
//2、N方查找关系时,只需要在Employee的department属性上
@OneToMany(mappedBy = "department")
private List<Employee> employees;
}
JPA 双向时,也不会级联保存
JPA双向多对一,只是查询时,如果使用到依赖数据时,会自动懒加载加载。但是我们只保存部门或员工时,JPA不会依赖把另一方自动保存,必须调用2方的persist
方法,都持久化。
如下,当只保存员工时,部门信息不会自动保存,部门ID是NULL
@Test
public void save(){
Department d1=new Department();
d1.setName("人事部");
Employee gg=new Employee();
gg.setName("gg");
Employee jl=new Employee();
jl.setName("jl");
//设置关系
List<Employee> employees=new ArrayList<>();
employees.add(gg);
employees.add(jl);
d1.setEmployees(employees);
gg.setDepartment(d1);
jl.setDepartment(d1);
//保存
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
// entityManager.persist(d1);
entityManager.persist(gg);
entityManager.persist(jl);
entityManager.getTransaction().commit();
entityManager.close();
}
双向多对一,如何手动维护关系?@JoinColumn
上面关系维护都是由JPA自动在多方添加1列(员工表自动添加部门ID),但是现在员工表和部门表已经自己创建好了,不需要JPA维护关系,如何告诉JPA使用哪个列作为关系键?
@Data
@Entity
public class Employee {
@Id
@GeneratedValue
private long id;
private String name;
@ManyToOne
//name指定的外键列名称。referencedColumnName 外键表的主键名称,默认是主键名称可以不写
@JoinColumn(name = "department_id",referencedColumnName = "id")
private Department department;
}
@Data
@Entity
public class Department {
@Id
@GeneratedValue
private long id;
private String name;
//mappedBy 作用:
//1、然N方,放弃关系的维护,不创建中间关系表
//2、N方查找关系时,只需要在Employee的department属性上
@OneToMany(mappedBy = "department")
private List<Employee> employees;
}