둘셋 개발!

[JPA] orphanRemoval 옵션 적용 버그 본문

JPA

[JPA] orphanRemoval 옵션 적용 버그

23 2023. 11. 2. 15:18

intro.

jpa를 공부하다가 orphanRemoval 옵션이 제대로 동작이 안됐다.

orphanRemoval 옵션을 true로 하면 고아객체 발생시 해당 고아객체는 삭제되어야 하는데 삭제가 안된 것이다.

 


클래스 구성

클래스로는 Parent와 Child가 있고,

Parent : Child는 1: N의 관계를 가지고 있다.

 

Parent.class

@Entity
public class Parent {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent", orphanRemoval = true) // 옵션 적용
    private List<Child> childList = new ArrayList<>();

    public void addChild(Child child) {
        childList.add(child);
        child.setParent(this);
    }
    
    public List<Child> getChildList() {
        return childList;
    }
    
    // ... set, get
}

 

 

@Entity
public class Child {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent = new Parent();

    public Parent getParent() {
        return parent;
    }

    public void setParent(Parent parent) {
        this.parent = parent;
    }
    
    // set, get, ...
}

 

test 코드

tx.begin();

Parent parent = new Parent();

Child child1 = new Child();
parent.addChild(child1);


Child child2 = new Child();
parent.addChild(child2);

em.persist(parent);
em.persist(child1);
em.persist(child2);

em.flush();
em.clear();

System.out.println("--------------------");

// child2를 자식에서 제외
Parent findParent = em.find(Parent.class, parent.getId());
List<Child> childList = findParent.getChildList();
childList.remove(1); // child2와 참조를 끊음!
tx.commit();

 

결과

delete문도 나가지 않았고, db에도 child2가 남아있었다.

 

고아 객체가 되는 조건은 1) 부모 객체가 삭제됨. 2) 부모객체의 자식컬랙션에서 제외됨 인데,

이는 2번에 해당하고 고아객체가 되어 자동으로 삭제하겠금(orphanRemoval=true) 설정했는데 삭제가 안된 것이다.

 

그래서 구글링 해본 결과 버그라고 나왔다.

만약 자식컬랜션에서 제외됐을 경우 자동으로 삭제하기를 원한다면 cascade 옵션과 같이 사용해야 한다.

 

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL,orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

 

@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST,orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

 

 

실무에 적용할 때는 그럼 어떻게?

(김영한님 커뮤니티의 답변을 바탕으로 작성)

보통 orphanRemoval을 단독으로 사용하는 경우는 드물고,

부모객체가 자식객체를 관리하는 경우에는 orphanRemoval와 CascadeType.PERSIST를 같이 사용하기 때문에

괜찮다고 한다.


ref.

https://velog.io/@byeongju/orphanRemoval%EC%9D%B4-%EB%8F%99%EC%9E%91%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4%EB%A9%B4

https://www.inflearn.com/questions/137740/orphanremoval%EA%B3%BC-cascade%EC%9D%98-%EA%B4%80%EA%B3%84