1. Overview
1.概述
In this tutorial, we’ll be discussing the difference between two of the options we have for removing entities from our databases when working with JPA.
在本教程中,我们将讨论在使用JPA时,我们从数据库中删除实体的两个选项的区别。
First, we’ll start with CascadeType.REMOVE which is a way to delete a child entity or entities when the deletion of its parent happens. Then we’ll take a look at the orphanRemoval attribute, which was introduced in JPA 2.0. This provides us with a way to delete orphaned entities from the database.
首先,我们将从CascadeType.REMOVE开始,这是一种在删除父实体时删除一个或多个子实体的方法。然后我们来看看orphanRemoval属性,它是在JPA 2.0中引入的。这为我们提供了一种从数据库中删除无主实体的方法。
Throughout the tutorial, we’ll be using a simple online store domain to demonstrate our examples.
在整个教程中,我们将使用一个简单的网上商店域名来演示我们的例子。
2. Domain Model
2.领域模型
As mentioned earlier, this article makes use of a simple online store domain. Wherein the OrderRequest has a ShipmentInfo and a list of LineItem.
如前所述,本文使用了一个简单的在线商店域。其中,OrderRequest有一个ShipmentInfo和一个LineItem的列表。
Given that, let’s consider:
鉴于此,让我们考虑:
- For the removal of ShipmentInfo, when the deletion of an OrderRequest happens, we’ll use CascadeType.REMOVE
- For the removal of a LineItem from an OrderRequest, we’ll use orphanRemoval
First, let’s create a ShipmentInfo entity:
首先,让我们创建一个ShipmentInfo 实体:
@Entity
public class ShipmentInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
// constructors
}
Next, let’s create a LineItem entity:
接下来,让我们创建一个LineItem实体:。
@Entity
public class LineItem {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne
private OrderRequest orderRequest;
// constructors, equals, hashCode
}
Lastly, let’s put it all together by creating an OrderRequest entity:
最后,让我们通过创建一个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);
}
}
It’s worth highlighting the removeLineItem method, which detaches a LineItem from an OrderRequest.
值得强调的是removeLineItem方法,它将LineItem从OrderRequest中分离。
3. CascadeType.REMOVE
3.CascadeType.REMOVE
As stated earlier, marking a reference field with CascadeType.REMOVE is a way to delete a child entity or entities whenever the deletion of its parent happens.
如前所述,用CascadeType.REMOVE标记一个引用字段是一种删除子实体或实体只要删除其父实体发生。
In our case, an OrderRequest has a ShipmentInfo, which has a CascadeType.REMOVE.
在我们的案例中,一个OrderRequest有一个ShipmentInfo,它有一个CascadeType.REMOVE。
To verify the deletion of ShipmentInfo from the database when the deletion of an OrderRequest happens, let’s create a simple integration test:
为了验证在删除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());
}
From the assertions, we can see that the deletion of OrderRequest resulted in the successful deletion of the related ShipmentInfo as well.
从断言中,我们可以看到,删除OrderRequest导致相关的ShipmentInfo也被成功删除。
4. orphanRemoval
4.孤儿移除
As stated earlier, its usage is to delete orphaned entities from the database. An entity that is no longer attached to its parent is the definition of being an orphan.
如前所述,它的用途是删除无主实体从数据库。一个不再依附于其父辈的实体是作为一个孤儿的定义。
In our case, an OrderRequest has a collection of LineItem objects where we use the @OneToMany annotation to identify the relationship. This is where we also set the orphanRemoval attribute to true. To detach a LineItem from an OrderRequest, we can use the removeLineItem method that we previously created.
在我们的例子中。一个OrderRequest有一个LineItem对象的集合,其中我们使用@OneToMany注释来识别关系。这时我们也将orphanRemoval属性设置为true。要从OrderRequest中分离出一个LineItem,我们可以使用之前创建的removeLineItem方法。
With everything in place, once we use the removeLineItem method and save the OrderRequest, the deletion of the orphaned LineItem from the database should happen.
随着一切就绪,一旦我们使用removeLineItem方法并保存OrderRequest,从数据库中删除无主的LineItem应该发生。
To verify the deletion of the orphaned LineItem from the database, let’s create another integration test:
为了验证删除无主的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());
}
Again, from the assertions, it shows that we have successfully deleted the orphaned LineItem from the database.
同样,从断言来看,它显示我们已经成功地从数据库中删除了无主的LineItem。
Additionally, it’s worth mentioning that the removeLineItem method modifies the list of LineItem instead of reassigning a value to it. Doing the latter will lead to a PersistenceException.
此外,值得一提的是,removeLineItem方法修改了LineItem的列表,而不是给它重新赋值。做后者会导致PersistenceException。
To verify the stated behavior, let’s create a final integration test:
为了验证所述行为,让我们创建一个最终的集成测试。
@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. Conclusion
5.总结
In this article, we’ve explored the difference between CascadeType.REMOVE and orphanRemoval using a simple online store domain. Also, in order to verify the entities were deleted correctly from our database, we created several integration tests.
在这篇文章中,我们使用一个简单的在线商店域探索了CascadeType.REMOVE和orphanRemoval之间的区别。另外,为了验证实体从我们的数据库中被正确删除,我们创建了几个集成测试。
As always, the full source code of the article is available over on GitHub.
一如既往,文章的完整源代码可在GitHub上获得。