JPA CascadeType.REMOVE對比orphanRemoval

1.概述

在本教程中,我們將討論在使用JPA時從數據庫中刪除實體的兩種選擇之間的區別。

首先,我們將從CascadeType.REMOVE開始,這是在刪除父實體時刪除一個或多個子實體的一種方法。然後,我們來看看JPA 2.0中引入orphanRemoval這為我們提供了一種從數據庫中刪除孤立實體的方法。

在整個教程中,我們將使用一個簡單的在線商店域來演示我們的示例。

2.領域模型

如前所述,本文利用了一個簡單的在線商店域。其中, OrderRequest具有ShipmentInfoLineItem列表。

鑑於此,讓我們考慮:

  • 為了刪除ShipmentInfo,當刪除OrderRequest時,我們將使用CascadeType.REMOVE
  • 為了從OrderRequest LineItem ,我們將使用orphanRemoval

首先,讓我們創建一個ShipmentInfo 實體:

@Entity

 public class ShipmentInfo {



 @Id

 @GeneratedValue(strategy = GenerationType.AUTO)

 private Long id;



 private String name;



 // constructors

 }

接下來,讓我們創建一個LineItem實體:

@Entity

 public class LineItem {



 @Id

 @GeneratedValue(strategy = GenerationType.AUTO)

 private Long id;



 private String name;



 @ManyToOne

 private OrderRequest orderRequest;



 // constructors, equals, hashCode

 }

最後,讓我們通過創建OrderRequest實體將它們放在一起:

@Entity

 public class OrderRequest {



 @Id

 @GeneratedValue(strategy = GenerationType.AUTO)

 private Long id;



 @OneToOne(cascade = { CascadeType.REMOVE, CascadeType.PERSIST })

 private ShipmentInfo shipmentInfo;



 @OneToMany(orphanRemoval = true, cascade = CascadeType.PERSIST, mappedBy = "orderRequest")

 private List<LineItem> lineItems;



 // constructors



 public void removeLineItem(LineItem lineItem) {

 lineItems.remove(lineItem);

 }

 }

值得強調一下removeLineItem方法,該方法將LineItemOrderRequest分離。

3. CascadeType.REMOVE

如前所述,使用CascadeType.REMOVE標記引用字段是一種刪除**父實體的子實體**的方法

在我們的示例中, OrderRequest具有ShipmentInfo ,其具有CascadeType.REMOVE

為了驗證刪除ShipmentInfo從數據庫時刪除OrderRequest情況發生,讓我們創建一個簡單的集成測試:

@Test

 public void whenOrderRequestIsDeleted_thenDeleteShipmentInfo() {

 createOrderRequestWithShipmentInfo();



 OrderRequest orderRequest = entityManager.find(OrderRequest.class, 1L);



 entityManager.getTransaction().begin();

 entityManager.remove(orderRequest);

 entityManager.getTransaction().commit();



 Assert.assertEquals(0, findAllOrderRequest().size());

 Assert.assertEquals(0, findAllShipmentInfo().size());

 }



 private void createOrderRequestWithShipmentInfo() {

 ShipmentInfo shipmentInfo = new ShipmentInfo("name");

 OrderRequest orderRequest = new OrderRequest(shipmentInfo);



 entityManager.getTransaction().begin();

 entityManager.persist(orderRequest);

 entityManager.getTransaction().commit();



 Assert.assertEquals(1, findAllOrderRequest().size());

 Assert.assertEquals(1, findAllShipmentInfo().size());

 }

從這些斷言中,我們可以看到刪除OrderRequest成功刪除相關的ShipmentInfo

4. orphanRemoval

如前所述,其用法是從數據庫中**刪除**孤立的實體。不再附屬於其父實體的實體被定義為孤兒。

在我們的示例中, OrderRequest LineItem對象的集合,其中 我們使用@OneToMany批註來標識關係.在這裡,我們還將orphanRemoval屬性true 。要從OrderRequest LineItem ,我們可以使用之前創建removeLineItem

一切就緒後,一旦我們使用removeLineItem方法並保存OrderRequest ,就應該從數據庫中刪除孤立的LineItem

為了驗證從數據庫中刪除孤立的LineItem ,讓我們創建另一個集成測試:

@Test

 public void whenLineItemIsRemovedFromOrderRequest_thenDeleteOrphanedLineItem() {

 createOrderRequestWithLineItems();



 OrderRequest orderRequest = entityManager.find(OrderRequest.class, 1L);

 LineItem lineItem = entityManager.find(LineItem.class, 2L);

 orderRequest.removeLineItem(lineItem);



 entityManager.getTransaction().begin();

 entityManager.merge(orderRequest);

 entityManager.getTransaction().commit();



 Assert.assertEquals(1, findAllOrderRequest().size());

 Assert.assertEquals(2, findAllLineItem().size());

 }



 private void createOrderRequestWithLineItems() {

 List<LineItem> lineItems = new ArrayList<>();

 lineItems.add(new LineItem("line item 1"));

 lineItems.add(new LineItem("line item 2"));

 lineItems.add(new LineItem("line item 3"));



 OrderRequest orderRequest = new OrderRequest(lineItems);



 entityManager.getTransaction().begin();

 entityManager.persist(orderRequest);

 entityManager.getTransaction().commit();



 Assert.assertEquals(1, findAllOrderRequest().size());

 Assert.assertEquals(3, findAllLineItem().size());

 }

同樣,從斷言中可以看出,我們已成功從數據庫中LineItem

另外,值得一提的是removeLineItem方法修改LineItem列表,而不是為其重新分配值。後者將導致PersistenceException

為了驗證聲明的行為,讓我們創建一個最終的集成測試:

@Test(expected = PersistenceException.class)

 public void whenLineItemsIsReassigned_thenThrowAnException() {

 createOrderRequestWithLineItems();



 OrderRequest orderRequest = entityManager.find(OrderRequest.class, 1L);

 orderRequest.setLineItems(new ArrayList<>());



 entityManager.getTransaction().begin();

 entityManager.merge(orderRequest);

 entityManager.getTransaction().commit();

 }

5.結論

在本文中,我們使用一個簡單的在線商店域CascadeType.REMOVEorphanRemoval另外,為了驗證實體是否已從我們的數據庫中正確刪除,我們創建了幾個集成測試。