原文: https://howtodoinjava.com/hibernate/hibernate-jpa-cascade-types/

我们已经在之前的教程中了解了 Hiberate 中的映射关联实体,例如一对一映射一对多映射。 每当要保存关系所有者实体时,我们都想保存映射的实体。 为此,我们使用了“CascadeType”属性。 在此 JPA 级联类型教程中,我们将通过CascadeType了解各种可用的级联选项。

JPA 级联类型如何工作?

在继续之前,让我们看一下如何在代码中定义此级联类型属性。 让我们举个例子来更清楚地了解。 假设一个雇员可以有多个帐户; 但一个帐户只能与一名员工关联。 为了清楚起见,让我们以最少的信息创建实体。

EmployeeEntity.java

  1. @Entity
  2. @Table(name = "Employee")
  3. public class EmployeeEntity implements Serializable
  4. {
  5. private static final long serialVersionUID = -1798070786993154676L;
  6. @Id
  7. @Column(name = "ID", unique = true, nullable = false)
  8. private Integer employeeId;
  9. @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
  10. private String firstName;
  11. @Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
  12. private String lastName;
  13. @OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
  14. @JoinColumn(name="EMPLOYEE_ID")
  15. private Set<AccountEntity> accounts;
  16. //Getters and Setters Ommited
  17. }

AccountEntity.java

  1. @Entity
  2. @Table(name = "Account")
  3. public class AccountEntity implements Serializable
  4. {
  5. private static final long serialVersionUID = 1L;
  6. @Id
  7. @Column(name = "ID", unique = true, nullable = false)
  8. @GeneratedValue(strategy = GenerationType.SEQUENCE)
  9. private Integer accountId;
  10. @Column(name = "ACC_NO", unique = false, nullable = false, length = 100)
  11. private String accountNumber;
  12. @OneToOne (mappedBy="accounts", fetch = FetchType.LAZY)
  13. private EmployeeEntity employee;
  14. }

查看上面EmployeeEntity.java源代码中的粗体行。 它定义了“cascade=CascadeType.ALL”,从本质上讲意味着EmployeeEntity上发生的任何更改也必须级联到AccountEntity。 如果您保存员工,则所有关联的帐户也将保存到数据库中。 如果删除雇员,则与该雇员关联的所有帐户也将被删除。 很简单。

但是,如果我们只希望级联仅保存操作而不删除级联,该怎么办。 然后,我们需要使用以下代码明确指定它。

  1. @OneToMany(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
  2. @JoinColumn(name="EMPLOYEE_ID")
  3. private Set<AccountEntity> accounts;

现在,仅当使用员工实例调用save()persist()方法时,才会保留帐户。 如果在会话中调用任何其他方法,则该方法的效果不会影响/级联到帐户。

JPA 级联类型

Java 持久化架构支持的级联类型如下:

  1. CascadeType.PERSIST :级联类型presist表示save()persist()操作级联到相关实体。
  2. CascadeType.MERGE :级联类型merge表示在合并所有者实体时会合并相关实体。
  3. CascadeType.REFRESH :级联类型refreshrefresh()操作执行相同的操作。
  4. CascadeType.REMOVE :级联类型remove在删除所有者实体时会删除与此设置关联的所有相关实体。
  5. CascadeType.DETACH :如果发生“手动分离”,则级联类型detach分离所有相关实体。
  6. CascadeType.ALL :级联类型all是上述所有级联操作的简写。

JPA 中没有默认级联类型。 默认情况下,没有操作级联。

级联配置选项接受一个CascadeTypes数组。 因此,如我们的示例所示,为了在一对多关系的级联操作中仅包括刷新和合并,您可能会看到以下内容:

  1. @OneToMany(cascade={CascadeType.REFRESH, CascadeType.MERGE}, fetch = FetchType.LAZY)
  2. @JoinColumn(name="EMPLOYEE_ID")
  3. private Set<AccountEntity> accounts;

上述级联将导致仅合并和刷新帐户集合。

Hiberate 级联类型

现在,让我们了解在哪种情况下使用 Hiberate 的 Hiberate 方式。

除了 JPA 提供的级联类型之外,Hiberate 中还有一个级联操作,它不是上面讨论的正常设置的一部分,称为“孤例删除”。 将拥有的对象从其拥有的关系中删除后,该对象将从数据库中删除。

让我们看一个例子。 在我们的“雇员和帐户”实体示例中,我对它们进行了如下更新,并在帐户上提到“orphanRemoval=true”。 从本质上讲,这意味着每当我要删除“帐户组中的帐户”时(即我要删除该帐户与Employee之间的关系); 与数据库上任何其他雇员(即孤例)没有关联的帐户实体也应删除。

EmployeeEntity.java

  1. @Entity
  2. @Table(name = "Employee")
  3. public class EmployeeEntity implements Serializable
  4. {
  5. private static final long serialVersionUID = -1798070786993154676L;
  6. @Id
  7. @Column(name = "ID", unique = true, nullable = false)
  8. private Integer employeeId;
  9. @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
  10. private String firstName;
  11. @Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
  12. private String lastName;
  13. @OneToMany(orphanRemoval = true, mappedBy = "employee")
  14. private Set<AccountEntity> accounts;
  15. }

AccountEntity.java

  1. @Entity (name = "Account")
  2. @Table(name = "Account")
  3. public class AccountEntity implements Serializable
  4. {
  5. private static final long serialVersionUID = 1L;
  6. @Id
  7. @Column(name = "ID", unique = true, nullable = false)
  8. @GeneratedValue(strategy = GenerationType.SEQUENCE)
  9. private Integer accountId;
  10. @Column(name = "ACC_NO", unique = false, nullable = false, length = 100)
  11. private String accountNumber;
  12. @ManyToOne
  13. private EmployeeEntity employee;
  14. }

TestOrphanRemovalCascade.java

  1. public class TestOrphanRemovalCascade
  2. {
  3. public static void main(String[] args)
  4. {
  5. setupTestData();
  6. Session sessionOne = HibernateUtil.getSessionFactory().openSession();
  7. org.hibernate.Transaction tx = sessionOne.beginTransaction();
  8. //Load the employee in another session
  9. EmployeeEntity employee = (EmployeeEntity) sessionOne.load(EmployeeEntity.class, 1);
  10. //Verify there are 3 accounts
  11. System.out.println("Step 1 : " + employee.getAccounts().size());
  12. //Remove an account from first position of collection
  13. employee.getAccounts().remove(employee.getAccounts().iterator().next());
  14. //Verify there are 2 accounts in collection
  15. System.out.println("Step 2 : " + employee.getAccounts().size());
  16. tx.commit();
  17. sessionOne.close();
  18. //In another session check the actual data in database
  19. Session sessionTwo = HibernateUtil.getSessionFactory().openSession();
  20. sessionTwo.beginTransaction();
  21. EmployeeEntity employee1 = (EmployeeEntity) sessionTwo.load(EmployeeEntity.class, 1);
  22. //Verify there are 2 accounts now associated with Employee
  23. System.out.println("Step 3 : " + employee1.getAccounts().size());
  24. //Verify there are 2 accounts in Account table
  25. Query query = sessionTwo.createQuery("from Account a");
  26. @SuppressWarnings("unchecked")
  27. List<AccountEntity> accounts = query.list();
  28. System.out.println("Step 4 : " + accounts.size());
  29. sessionTwo.close();
  30. HibernateUtil.shutdown();
  31. }
  32. private static void setupTestData(){
  33. Session session = HibernateUtil.getSessionFactory().openSession();
  34. session.beginTransaction();
  35. //Create Employee
  36. EmployeeEntity emp = new EmployeeEntity();
  37. emp.setEmployeeId(1);
  38. emp.setFirstName("Lokesh");
  39. emp.setLastName("Gupta");
  40. session.save(emp);
  41. //Create Account 1
  42. AccountEntity acc1 = new AccountEntity();
  43. acc1.setAccountId(1);
  44. acc1.setAccountNumber("11111111");
  45. acc1.setEmployee(emp);
  46. session.save(acc1);
  47. //Create Account 2
  48. AccountEntity acc2 = new AccountEntity();
  49. acc2.setAccountId(2);
  50. acc2.setAccountNumber("2222222");
  51. acc2.setEmployee(emp);
  52. session.save(acc2);
  53. //Create Account 3
  54. AccountEntity acc3 = new AccountEntity();
  55. acc3.setAccountId(3);
  56. acc3.setAccountNumber("33333333");
  57. acc3.setEmployee(emp);
  58. session.save(acc3);
  59. session.getTransaction().commit();
  60. session.close();
  61. }
  62. }
  63. Output:
  64. Hibernate: insert into Employee (FIRST_NAME, LAST_NAME, ID) values (?, ?, ?)
  65. Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
  66. Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
  67. Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
  68. Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_, employeeen0_.LAST_NAME as
  69. LAST_NAM3_1_0_ from Employee employeeen0_ where employeeen0_.ID=?
  70. Hibernate: select accounts0_.employee_ID as employee3_1_0_, accounts0_.ID as ID1_0_0_, accounts0_.ID as ID1_0_1_,
  71. accounts0_.ACC_NO as ACC_NO2_0_1_, accounts0_.employee_ID as employee3_0_1_ from Account accounts0_ where accounts0_.employee_ID=?
  72. Step 1 : 3
  73. Step 2 : 2
  74. Hibernate: delete from Account where ID=?
  75. Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_, employeeen0_.LAST_NAME as
  76. LAST_NAM3_1_0_ from Employee employeeen0_ where employeeen0_.ID=?
  77. Hibernate: select accounts0_.employee_ID as employee3_1_0_, accounts0_.ID as ID1_0_0_, accounts0_.ID as ID1_0_1_,
  78. accounts0_.ACC_NO as ACC_NO2_0_1_, accounts0_.employee_ID as employee3_0_1_ from Account accounts0_ where accounts0_.employee_ID=?
  79. Step 3 : 2
  80. Hibernate: select accountent0_.ID as ID1_0_, accountent0_.ACC_NO as ACC_NO2_0_, accountent0_.employee_ID as employee3_0_
  81. from Account accountent0_
  82. Step 4 : 2

这是从集合中删除匹配项/不匹配项的好方法(即多对一或一对多关系)。 您只需从集合中删除该项目,然后 Hiberate 即可为您处理其余所有事情。 它将检查是否从任何地方引用了实体; 如果不是,它将从数据库本身中删除该实体。

让我知道您对Hiberate 5 级联类型JPA 级联类型的想法和问题。

学习愉快!

阅读更多:

有关级联类型的 Oracle 博客