JPA - 영속성 전이(Cascade)와 고아 객체(Orphan)

2021. 4. 17. 01:36 Spring Data/Spring Data JPA

간단히 설명하면 영속성 전이란, 연관된 엔티티가 영속화되면, 그와 연관된 엔티티까지 모두 영속화시키는것 혹은 하나의 엔티티가 영속성 컨텍스트에서 제거가 된다면, 그와 관련된 엔티티마저 영속성 컨텍스트에서 제거가 되는 것 등의 작업흐름을 영속성 전이라고한다. 즉, 데이터베이스의 Cascade와 같은 의미이다.

 

고아객체란 하나의 엔티티에서 연관된 엔티티와의 참조가 끊어지면 끊어진 엔티티를 자동으로 삭제해주는 기능이다.

 

두개를 예제소스로 설명하겠다.

 

영속성 전이(Cascade = CascadeType.xxx)

 

우선 예제소스를 설명하기 전에 CascadeType의 종류를 나열한다면,

public enum CascadeType{
    ALL, //모두적용
    PERSIST, //영속
    MERGE, //병합
    REMOVE, //삭제
    REPRESH, //리프래쉬
    DETACH //준영속상태로 전환
}
import lombok.Getter;
import lombok.Setter;
import org.javers.core.metamodel.annotation.Entity;
 
@Entity
@Table(name = "TEAM_TB")
@Getter
@Setter
public class TeamFetchType {
    @Id
    private String id;
 
    private String name;
 
    @OneToMany(mappedBy="team",cascade = CascadeType.ALL)
    private List<MemberFetchTypeLazy> members = new ArrayList<MemberFetchTypeLazy>();
}
 
public class CascadeTest {
    //엔티티매니저 팩토리 생성
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
    //엔티티매니저 생성
    EntityManager em = emf.createEntityManager();
    public void create() {
        //트랜잭션 획득
        EntityTransaction tx = em.getTransaction();
 
        try {
            tx.begin();
 
            TeamFetchType team = new TeamFetchType();
            team.setId("team_1");
            team.setName("team_1");
            /*em.persist(team);*/
 
            MemberFetchTypeLazy member = new MemberFetchTypeLazy();
            member.setId("member_1");
            member.setName("윤여성");
            member.setTeam(team);
 
            team.getMembers().add(member);
 
            em.persist(team);
 
            tx.commit();
 
            em.clear();
        }catch (Exception e1) {
            // TODO: handle exception
            tx.rollback();
        }finally {
 
        }
    }
 
    public void remove() {
        //트랜잭션 획득
        EntityTransaction tx = em.getTransaction();
 
        try {
            tx.begin();
 
            TeamFetchType findTeam = em.find(TeamFetchType.class, "team_1");
 
            em.remove(findTeam);
 
            tx.commit();
        }catch (Exception e1) {
            // TODO: handle exception
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        CascadeTest t = new CascadeTest();
        t.create();
        t.remove();
 
        System.out.println();
    }
}

 

위의 소스에서 CascadeType.ALL로 모든 영속성 전이 속성을 허용하였다. Team엔티티 인스턴스를 만들고 Member엔티티 인스턴스를 만든 다음에 Team엔티티에 add(member)를 했다.(반드시 영속성 속성이 정의된 엔티티에 객체를 add해야한다. 그래야 Team엔티티를 persist할때 Team필드에 add된 Member까지 persist된다.) 그리고 em.persist()호출하니 Team엔티티는 물론 Member엔티티까지 데이터베이스에 insert 됬다. 그리고 위 remove()에서 em.remove(findTeam)을 호출하는 순간 Team 엔티티가 삭제됨과 동시에 Member 엔티티도 모두 삭제되는 것을 볼 수 있다. 만약 영속성 전이 속성을 사용하지 않았다면 Team 엔티티를 삭제하는 순간 외래키 참조무결성 조약에 어긋나서 예외가 발생할 것이다. 즉, 영속성 전이 속성없이 외래키의 주인인 Member를 지우는 것은 문제가 안되지만 이미 Member가 참조하고 있는 Team을 삭제하는 순간 문제가 되는 것이다. 그냥 쉽게 생각하면 데이터베이스에 Cascade 키워드를 사용하는 것이라고 생각하면 된다.

 

고아객체(orphan)

 

위에서도 간단히 설명했지만 고아객체란 부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면 자식 엔티티가 자동으로 삭제되는 기능이다.

소스에서도 보듯이 findTeam.getMembers().clear() 메소드를 호출했을 뿐인데, 플러쉬 단계에서 해당 Member엔티티를 삭제한다. 즉, 고아객체 속성이 적용된 하나의 엔티티에서 연관관계에 있는 엔티티의 참조를 제거하는 것만으로 연관된 엔티티가 삭제되는 것이다.

 

하지만 주의할 점은 고아객체 속성은 반드시 참조가 제거된 엔티티가 어디에서든 참조하지 않는 객체일때만 사용해야한다. 만약 다른데에서도 참조하는 엔티티인데 삭제가 된다면 문제가 생길 수 있다.



출처: https://coding-start.tistory.com/83?category=781616 [코딩스타트]