1. 개요
이 예제에서는 JPA로 작업할 때 데이터베이스에서 엔티티를 제거하기 위한 두 가지 옵션 간의 차이점에 대해 논의할 것 입니다.
먼저 상위 항목이 삭제될 때 하위 항목 을 삭제하는 방법인 CascadeType.REMOVE 부터 시작 합니다 . 그런 다음 JPA 2.0에 도입된 orphanRemoval 속성을 살펴보겠습니다 . 이것은 데이터베이스에서 분리된 엔터티 를 삭제 하는 방법을 제공 합니다 .
사용방법(예제) 전체에서 간단한 온라인 상점 도메인을 사용하여 예제를 보여줍니다.
2. 도메인 모델
앞서 언급했듯이 이 문서에서는 간단한 온라인 상점 도메인을 사용합니다. 여기서 OrderRequest 에는 ShipmentInfo 및 LineItem List이 있습니다.
이를 고려하여 다음을 고려해 보겠습니다.
- 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);
}
}
OrderRequest 에서 LineItem 을 분리하는 removeLineItem 메서드를 강조 표시할 가치가 있습니다.
3. 캐스케이드 유형.제거
앞서 언급했듯이 참조 필드를 CascadeType.REMOVE로 표시하는 것은 상위 항목 이 삭제될 때마다 하위 항목 을 삭제하는 방법 입니다.
우리의 경우 OrderRequest 에는 CascadeType.REMOVE 가 있는 ShipmentInfo 가 있습니다.
OrderRequest 삭제가 발생 했을 때 데이터베이스에서 ShipmentInfo 가 삭제되었는지 확인하기 위해 간단한 통합 테스트를 생성해 보겠습니다.
@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. 고아제거
앞서 언급했듯이 그 사용법은 데이터베이스에서 분리된 엔터티 를 삭제 하는 것입니다 . 더 이상 상위에 연결되지 않은 엔티티는 고아 가 되는 정의입니다 .
우리의 경우에, 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 에 값을 재할당하는 대신 LineItem List을 수정 한다는 점을 언급할 가치가 있습니다. 후자를 수행하면 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 의 차이점을 살펴보았습니다 . 또한 엔티티가 데이터베이스에서 올바르게 삭제되었는지 확인하기 위해 몇 가지 통합 테스트를 만들었습니다.