對象映射關(guān)系分為單向關(guān)系和雙向關(guān)系,單向關(guān)系只在一方對象上存在對方對象,雙向關(guān)系是在雙方對象上存在彼此對象。
一、單向關(guān)系
public class Department implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer departmentId;
private String departmentCode;
@OneToMany(cascade = {CascadeType.ALL})
@JoinColumn
private List<Employee> employeeList;
}
在沒有@JoinColumn時,將多增加一個中間關(guān)系表,由此表來維護兩個對象關(guān)系,增加之后只有兩個表,由Employee表維護關(guān)系。默認情況下jpa會使用主鍵來做關(guān)聯(lián),并在子表中增加外鍵約束。
二、單向關(guān)系使用code關(guān)聯(lián)
在設(shè)計表結(jié)構(gòu)時,主鍵一般會使用自增ID,但在做子表關(guān)聯(lián)時由于分布式結(jié)構(gòu)原因不想使用自增ID來做關(guān)系維護,則可自定義字段code來維護關(guān)系,如下:
public class Department implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer departmentId;
private String departmentCode;
@OneToMany(cascade = {CascadeType.ALL})
@JoinColumn(name = "departmentCode", referencedColumnName = "departmentCode")
private List<Employee> employeeList;
}
在employee表中會增加一個department_code字段來維護關(guān)聯(lián)關(guān)系。
注意:從add操作中的SQL可以看出,employee是先insert之后,再去update關(guān)系字段的,多一步update。
Hibernate: insert into department (department_code, department_name) values (?, ?)
Hibernate: insert into employee (employee_code, employee_name) values (?, ?)
Hibernate: insert into employee (employee_code, employee_name) values (?, ?)
Hibernate: update employee set department_code=? where employee_id=?
Hibernate: update employee set department_code=? where employee_id=?
三、雙向關(guān)系
public class Department implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer departmentId;
private String departmentCode;
@OneToMany(cascade = {CascadeType.ALL}, mappedBy = "department")
private List<Employee> employeeList;
}
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer employeeId;
private String employeeCode;
@ManyToOne
private Department department;
}
使用mappedBy將去掉中間關(guān)系表,由employee維護department的關(guān)系。
注意事項:
1.在add操作中子對象employee必須設(shè)置主對象,否則數(shù)據(jù)庫層面是沒有維護外鍵關(guān)系的,如:
@Test
public void add(){
Department department = Department.builder()
.departmentCode("D001")
.departmentName("部門1")
.build();
Employee employee = Employee.builder()
.employeeCode("E001")
.employeeName("員工1")
.build();
// 如無此操作,將無關(guān)聯(lián)關(guān)系
employee.setDepartment(department);
List<Employee> employeeList = new ArrayList<>();
employeeList.add(employee);
department.setEmployeeList(employeeList);
departmentRepository.save(department);
}
2.此種設(shè)置是不需要額外update關(guān)系的
Hibernate: insert into department (department_code, department_name) values (?, ?)
Hibernate: insert into employee (department_department_id, employee_code, employee_name) values (?, ?, ?)
Hibernate: insert into employee (department_department_id, employee_code, employee_name) values (?, ?, ?)
3.關(guān)閉子對象中的父對象toString,避免無限循環(huán)調(diào)用
四、雙向關(guān)系使用code關(guān)聯(lián)
同單向關(guān)系類似,且有額外的update操作
code關(guān)聯(lián),使用JoinColumn時不可同時使用mappedBy
五、級聯(lián)操作設(shè)置
- CascadeType.PERSIST:級聯(lián)保存,在保存department的同時保存employee對象
- CascadeType.MERGE:級聯(lián)更新,將department和employee視為一個整體,任何一個對象有變化,都會更新
- CascadeType.REMOVE:級聯(lián)刪除
- 當沒有設(shè)置時,delete主對象時,子對象只是去掉關(guān)系;remove子對象時也只是去掉關(guān)系,如果增加
orphanRemoval = true則會刪除remove的子對象 - 當有設(shè)置時,delete主對象時,子對象同樣會被刪除
- 當沒有設(shè)置時,delete主對象時,子對象只是去掉關(guān)系;remove子對象時也只是去掉關(guān)系,如果增加
- CascadeType.REFRESH:級聯(lián)刷新(較少使用),在并發(fā)的場景下避免臟數(shù)據(jù)
- CascadeType.DETACH:級聯(lián)脫管(較少使用)
- CascadeType.ALL:以上全部,需要根據(jù)實際情況謹慎設(shè)置,以免產(chǎn)生混亂
六、源代碼
https://gitee.com/hypier/barry-jpa/tree/master/jpa-section-2