# Hibernate JPA 级联类型

> 原文： [https://howtodoinjava.com/hibernate/hibernate-jpa-cascade-types/](https://howtodoinjava.com/hibernate/hibernate-jpa-cascade-types/)

我们已经在之前的教程中了解了 Hiberate 中的[**映射关联实体**](//howtodoinjava.com/hibernate/how-to-define-association-mappings-between-hibernate-entities/ "How to Define Association Mappings between Hibernate Entities")，例如[**一对一映射**](//howtodoinjava.com/hibernate/hibernate-one-to-one-mapping-using-annotations/ "Hibernate one-to-one mapping using annotations")和[**一对多映射**](//howtodoinjava.com/hibernate/hibernate-one-to-many-mapping-using-annotations/ "Hibernate one-to-many mapping using annotations")。 每当要保存关系所有者实体时，我们都想保存映射的实体。 为此，我们使用了“`CascadeType`”属性。 在此 **JPA 级联类型**教程中，我们将通过`CascadeType`了解各种可用的级联选项。

## JPA 级联类型如何工作？

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

**`EmployeeEntity.java`**

```java
@Entity 
@Table(name = "Employee")
public class EmployeeEntity implements Serializable
{
	private static final long serialVersionUID = -1798070786993154676L;
	@Id
	@Column(name = "ID", unique = true, nullable = false)
	private Integer           employeeId;
	@Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
	private String            firstName;
	@Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
	private String            lastName;

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

	//Getters and Setters Ommited
}

```

**`AccountEntity.java`**

```java
@Entity
@Table(name = "Account")
public class AccountEntity implements Serializable
{
	private static final long serialVersionUID = 1L;
	@Id
	@Column(name = "ID", unique = true, nullable = false)
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Integer           accountId;
	@Column(name = "ACC_NO", unique = false, nullable = false, length = 100)
	private String            accountNumber;

	@OneToOne (mappedBy="accounts",  fetch = FetchType.LAZY)
	private EmployeeEntity employee;

}

```

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

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

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

```

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

## JPA 级联类型

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

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

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

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

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

```

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

## Hiberate 级联类型

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

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

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

**`EmployeeEntity.java`**

```java
@Entity 
@Table(name = "Employee")
public class EmployeeEntity implements Serializable
{
	private static final long serialVersionUID = -1798070786993154676L;
	@Id
	@Column(name = "ID", unique = true, nullable = false)
	private Integer           employeeId;
	@Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
	private String            firstName;
	@Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
	private String            lastName;

	@OneToMany(orphanRemoval = true, mappedBy = "employee")
	private Set<AccountEntity> accounts;

}

```

**`AccountEntity.java`**

```java
@Entity (name = "Account")
@Table(name = "Account")
public class AccountEntity implements Serializable
{
	private static final long serialVersionUID = 1L;
	@Id
	@Column(name = "ID", unique = true, nullable = false)
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Integer           accountId;
	@Column(name = "ACC_NO", unique = false, nullable = false, length = 100)
	private String            accountNumber;

	@ManyToOne
	private EmployeeEntity employee;
}

```

**`TestOrphanRemovalCascade.java`**

```java
public class TestOrphanRemovalCascade
{
   public static void main(String[] args)
   {
      setupTestData();

      Session sessionOne = HibernateUtil.getSessionFactory().openSession();
      org.hibernate.Transaction tx = sessionOne.beginTransaction();

      //Load the employee in another session
      EmployeeEntity employee = (EmployeeEntity) sessionOne.load(EmployeeEntity.class, 1);
      //Verify there are 3 accounts
      System.out.println("Step 1 : " + employee.getAccounts().size());

      //Remove an account from first position of collection
      employee.getAccounts().remove(employee.getAccounts().iterator().next());

      //Verify there are 2 accounts in collection
      System.out.println("Step 2 : " + employee.getAccounts().size());

      tx.commit();
      sessionOne.close();

      //In another session check the actual data in database
      Session sessionTwo = HibernateUtil.getSessionFactory().openSession();
      sessionTwo.beginTransaction();

      EmployeeEntity employee1 = (EmployeeEntity) sessionTwo.load(EmployeeEntity.class, 1);
      //Verify there are 2 accounts now associated with Employee
      System.out.println("Step 3 : " + employee1.getAccounts().size());

      //Verify there are 2 accounts in Account table
      Query query = sessionTwo.createQuery("from Account a");
      @SuppressWarnings("unchecked")
      List<AccountEntity> accounts = query.list();
      System.out.println("Step 4 : " + accounts.size());

      sessionTwo.close();

      HibernateUtil.shutdown();
   }  

   private static void setupTestData(){
      Session session = HibernateUtil.getSessionFactory().openSession();
      session.beginTransaction();

      //Create Employee
      EmployeeEntity emp = new EmployeeEntity();
      emp.setEmployeeId(1);
      emp.setFirstName("Lokesh");
      emp.setLastName("Gupta");
      session.save(emp);

      //Create Account 1
      AccountEntity acc1 = new AccountEntity();
      acc1.setAccountId(1);
      acc1.setAccountNumber("11111111");
      acc1.setEmployee(emp);
      session.save(acc1);

      //Create Account 2
      AccountEntity acc2 = new AccountEntity();
      acc2.setAccountId(2);
      acc2.setAccountNumber("2222222");
      acc2.setEmployee(emp);
      session.save(acc2);

      //Create Account 3
      AccountEntity acc3 = new AccountEntity();
      acc3.setAccountId(3);
      acc3.setAccountNumber("33333333");
      acc3.setEmployee(emp);
      session.save(acc3);

      session.getTransaction().commit();
      session.close();
   }
}

Output:

Hibernate: insert into Employee (FIRST_NAME, LAST_NAME, ID) values (?, ?, ?)
Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_, employeeen0_.LAST_NAME as 
LAST_NAM3_1_0_ from Employee employeeen0_ where employeeen0_.ID=?
Hibernate: select accounts0_.employee_ID as employee3_1_0_, accounts0_.ID as ID1_0_0_, accounts0_.ID as ID1_0_1_, 
accounts0_.ACC_NO as ACC_NO2_0_1_, accounts0_.employee_ID as employee3_0_1_ from Account accounts0_ where accounts0_.employee_ID=?
Step 1 : 3
Step 2 : 2
Hibernate: delete from Account where ID=?
Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_, employeeen0_.LAST_NAME as 
LAST_NAM3_1_0_ from Employee employeeen0_ where employeeen0_.ID=?
Hibernate: select accounts0_.employee_ID as employee3_1_0_, accounts0_.ID as ID1_0_0_, accounts0_.ID as ID1_0_1_, 
accounts0_.ACC_NO as ACC_NO2_0_1_, accounts0_.employee_ID as employee3_0_1_ from Account accounts0_ where accounts0_.employee_ID=?
Step 3 : 2
Hibernate: select accountent0_.ID as ID1_0_, accountent0_.ACC_NO as ACC_NO2_0_, accountent0_.employee_ID as employee3_0_ 
from Account accountent0_
Step 4 : 2

```

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

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

学习愉快！

阅读更多：

[有关级联类型的 Oracle 博客](https://docs.oracle.com/cd/E19798-01/821-1841/bnbqm/index.html)