1. Обзор
В этом уроке мы обсудим разницу между двумя вариантами удаления сущностей из наших баз данных при работе с JPA .
Во-первых, мы начнем с CascadeType.REMOVE , который является способом удаления дочерней сущности или сущностей, когда происходит удаление ее родительской сущности . Затем мы рассмотрим атрибут orphanRemoval , который был введен в JPA 2.0. Это предоставляет нам способ удаления осиротевших объектов из базы данных .
На протяжении всего урока мы будем использовать простой домен интернет-магазина, чтобы продемонстрировать наши примеры.
2. Модель предметной области
Как упоминалось ранее, в этой статье используется простой домен интернет-магазина. В котором Запрос заказа содержит Информацию об отгрузке и список Номенклатуры .
Учитывая это, давайте рассмотрим:
- Для удаления Информации об отправке когда произойдет удаление Запроса на заказ , мы будем использовать CascadeType.REMOVE
- Для удаления Позиции из Запроса на заказ мы будем использовать orphanRemoval
Во-первых, давайте создадим Информацию об отправке сущность:
@Entity
public class ShipmentInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
// constructors
}Далее давайте создадим Позицию сущность:
@Entity
public class LineItem {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne
private OrderRequest orderRequest;
// constructors, equals, hashCode
}Наконец, давайте соберем все это вместе, создав Запрос на заказ сущность:
@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 lineItems;
// constructors
public void removeLineItem(LineItem lineItem) {
lineItems.remove(lineItem);
}
} Стоит выделить метод удалить строку , который отделяет Строку от Запроса заказа .
3. Каскадный ТИП.УДАЛИТЬ
Как указывалось ранее, пометка ссылочного поля с помощью CascadeType.REMOVE является способом удаления дочерней сущности или сущностей всякий раз, когда происходит удаление ее родительского объекта .
В нашем случае, Запрос заказа имеет Информация об отправке , который имеет , который имеет .
Чтобы проверить удаление Информации об отправке из базы данных при удалении Запроса на заказ , давайте создадим простой интеграционный тест:
@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());
}Из утверждений мы видим, что удаление Запроса на заказ также привело к успешному удалению связанного ShipmentInfo .
4. Сиротство
Как указывалось ранее, его использование заключается в удалении осиротевших объектов из базы данных . Сущность, которая больше не привязана к своему родителю, определяется как сирота .
В нашем случае запрос Заказа содержит коллекцию линейных объектов , где мы используем аннотацию @OneToMany для идентификации связи . Здесь мы также устанавливаем атрибут orphanRemoval в true . Чтобы отделить Номенклатуру от Запроса на заказ , мы можем использовать метод removeLineItem , который мы создали ранее.
Со всем на месте, как только мы используем удалить Элемент Строки способ и сохраните Запрос заказа , удаление осиротевших Линейный элемент из базы данных должно произойти.
Чтобы проверить удаление потерянной строки из базы данных, давайте создадим еще один интеграционный тест:
@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 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());
} Опять же, из утверждений видно, что мы успешно удалили потерянную строку из базы данных.
Кроме того, стоит упомянуть, что метод removeLineItem изменяет список Элемента строки вместо переназначения ему значения. Выполнение последнего приведет к исключению 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.REMOVE и orphanRemoval с использованием простого домена интернет-магазина. Кроме того, чтобы убедиться, что объекты были правильно удалены из нашей базы данных, мы создали несколько интеграционных тестов.
Как всегда, полный исходный код статьи доступен на GitHub .