자라선

[자바 ORM 표쥰 JPA 프로그래밍] 17일차 - CASCADE & orphanRemoval 본문

Develop/JPA

[자바 ORM 표쥰 JPA 프로그래밍] 17일차 - CASCADE & orphanRemoval

자라선 2021. 9. 2. 17:52

JPA는 엔티티를 관리하기 위해 무조건 영속성 컨텍스트를 통해 관리가 이루어지는데

이는 테이블의 CASCADE와 같은 기능을 사용하여 엔티티도 똑같이 부모 엔티티를 통해 자식 엔티티를 관리할 수 있다.

 

1. CASCADE - INSERT

영속성 전이라고 부르며 부모 엔티티를 통해 자식 엔티티를 관리할 수 있도록 설정한다.

@Data
@Entity
public class Member {	// 자식 엔티티

    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    @OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
    private List<Orders> orders = new ArrayList<Orders>();

}

@Data
@Entity
public class Team {	// 부모 엔티티

    @Id
    @GeneratedValue
    @Column(name = "TEAM_ID")
    private long id;

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<Member>();

}
        // Team 엔티티를 영속성 컨텍스트에 저장
        Team team = new Team();
        team.setName("팀");
        em.persist(team);
        
        Member member = new Member();
        member.setName("이름1");
        member.setTeam(team);   // 자식 엔티티 도한 부모와 관계를 맺은 후
        em.persist(member);     // 영속성 컨텍스트에 저장한다.
        
        Member member1 = new Member();
        member1.setName("이름2");
        member1.setTeam(team);
        em.persist(member1);

일반적으로는 엔티티를 DB에 INSERT 하고자 할때 부모 엔티티와 관계를 갖는 자식 엔티티 모두 EntityManager.persist 를 통해 영속성 컨텍스트에 등록을 해주어야한다.

 

하지만 CASCADE를 사용한다면 부모 엔티티만 등록하여도 관계를 갖는 자식엔티티 모두 등록이 된다.

@Data
@Entity
public class Team {

	...

    @OneToMany(mappedBy = "team", cascade = CascadeType.PERSIST)    // CASCADE 설정 추가
    private List<Member> members = new ArrayList<Member>();

}

@OneToMany 매핑 어노테이션에 cascade 속성을 추가하여 CascadeType.PERSIST 값을 지정해주었다.

        // Team 엔티티를 영속성 컨텍스트에 저장
        Team team = new Team();
        team.setName("팀");

        Member member = new Member();
        member.setName("이름1");

        Member member1 = new Member();
        member1.setName("이름2");

        // 자식 엔티티 2개 모두 부모 엔티티와 매핑을 맺는다.
        member.setTeam(team);   // 자식 엔티티 도한 부모와 관계를 맺은 후
        member1.setTeam(team);
        team.getMembers().add(member);
        team.getMembers().add(member1);

        em.persist(team);

위 코드를 보면 EntityManager.persistTeam 엔티티만 추가하였다.

관계만 맺게하고 Team 엔티티의 Members List에도 add 하여 엔티티를 넣어주어야한다.

 

이러면 부모 엔티티인 Team 의 자식 엔티티인 Member 모두 INSERT 처리가 된다.

 

이렇게 cascade 를 사용하여 물리적 관계를 맺은것은 연관관계 매핑과는 아무런 관계가 없으며 JPA에서는 관계된 테이블들만 영속성 컨텍스트로 끌고오는 편리함을 제공해주는 것 뿐이다.

 

+ 추가 1

INSERT 도 된다면 당연히 삭제 또한 가능하다.

@Data
@Entity
public class Team {

	...

    @OneToMany(mappedBy = "team", cascade = CascadeType.REMOVE)    // CASCADE 설정 추가
    private List<Member> members = new ArrayList<Member>();

}

cascade 설정을 REMOVE 로 한다면 부모 엔티티가 삭제가 된다면 이와 관계를 맺고있는 자식 엔티티들도 모두 삭제가 된다.

        // TEAM_ID 가 26인 ROW를 DELETE
        final Team team = em.find(Team.class, 26l);
        em.remove(team);    // Member에 team이 26인 ROW는 전부 DELETE

 

+ 추가 2

cascade 종류도 여러가지가 있다.

public enum CascadeType {
    ALL,		// 모두 적용
    PERSIST,	// 영속
    MERGE,		// 병합
    REMOVE,		// 삭제
    REFRESH,	// REFRESH
    DETACH;		// DETACH

    private CascadeType() {
    }
}

 

    @OneToMany(mappedBy = "team", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
    private List<Member> members = new ArrayList<Member>();

적용할때는 {} 를 사용하여 여러개를 cascade 를 적용할 수 있다.

 

2. orphanRemoval (고아 객체)

JPA는 부모 엔티티로부터 관계가 끊어진 자섹 엔티티를 자동으로 삭제할수 있도록 기능을 제공해준다.

이것을 고아 객체 제거라고 부른다.

@Data
@Entity
public class Team {

    ...

    @OneToMany(mappedBy = "team", orphanRemoval = true) // 고아 객체도 제거할 수 있도록 명시
    private List<Member> members = new ArrayList<Member>();

}

부모 엔티티에서 @OneToMany 의 매핑 어노테이션에 orphanRemoval 속성의 값을 true로 한다.

        // TEAM_ID 가 23인 ROW를 SELECT
        final Team team = em.find(Team.class, 23l);

        // 참조 조회만 할수 있는 Member 프록시 리스트 객체를 반환
        final Member member = team.getMembers().get(0);
        System.out.println("member.getName() = " + member.getName());

        em.remove(member);  // 주인이 아닌 관계에서 참조를 제거만 해도 실제 DB에도 반영된다.

그리고 부모 객체를 조회 후 Member 컬렉션 래퍼에서 Member를 호출 후 EntityManager.remove 를 하였다.

원래 같으면 Team.members 는 참조용이기 때문에 영속성 컨텍스트와 아무런 관계도 없어 DB에 적용이 되면 안되지만 고아 객체 제거를 true로 하였기 때문에 가능하였다.

 

더보기
        final Team team = em.find(Team.class, 23l);
        // Team 엔티티는 조회하였지만 JPA는 members와 같은 컬렉션은 지연 로딩으로 인해 비어있는 프록시 컬렉션으로 만들어준다
        // 이러한 프록시 컬렉션은 컬렉션 래퍼라고 부르며 프록시와 비슷한 역할을 해준다.
        System.out.println(team.getMembers().getClass().getName()); // org.hibernate.collection.internal.PersistentBag

 

3. CASCADE + orphanRemoval 혼합

엔티티를 관리하기 위해서는 엔티티를 영속성 컨텍스트로 등록해야만 했고 

등록하는 과정은 EntitiyManager.persist 와 같은 기능을 사용하였는데 위에서 보았던 CASCADEorphanRemoval을 혼합하여 사용한다면 관계를 가진 자식 엔티티는 부모 엔티티로 인해 관리를 할 수 있다.

@Data
@Entity
public class Team {

    ...
    
    @OneToMany(mappedBy = "team", cascade = CascadeType.PERSIST, orphanRemoval = true)
    private List<Member> members = new ArrayList<Member>();

}

@OneToMany 매핑 어노테이션에 CASCADEorphanRemoval 속성과 값을 추가

 

        // CASCADE의 영속성 전이로 자식 엔티티또한 영속성 컨텍스트로 올려 INSERT
        Team team = new Team();
        Member member = new Member();
        member.setTeam(team);
        team.getMembers().add(member);
        em.persist(team);

CASCADE 의 영속성 전이를 활용하여 자식 엔티티도 영속성 컨텍스트로 등록하여 INSERT를 한다.

        // 부모 엔티티를 조회
        final Team team = em.find(Team.class, 29l);
        // 부모 엔티티와 관계를 맺고 있는 모든 자식 엔티티의 참조들을 제거함으로서 고아 객체 제거를 한다.
        team.getMembers().clear();

그 후 부모 엔티티만 조회하고 부모 엔티티를 참조하고있는 모든 자식 엔티티를 clear() 시켜버림으로써 

고아 객체 제거를 해 DELETE 하게된다.

 

이렇게 된다면 자식 엔티티는 EntitiyManager사용하지 않고도 관리를 할 수 있게 된다.

Comments