3. JPA對象關(guān)系映射 -- 級聯(lián)操作

對象映射關(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主對象時,子對象同樣會被刪除
  • 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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容