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.REMOVEorphanRemoval 의 차이점을 살펴보았습니다 . 또한 엔티티가 데이터베이스에서 올바르게 삭제되었는지 확인하기 위해 몇 가지 통합 테스트를 만들었습니다.

항상 그렇듯이 기사의 전체 소스 코드는 GitHub에서 사용할 수  있습니다 .

Persistence footer banner