이 글은 다음 두 클래스가 양방향 연관관계를 가지고 있는 상황을 가정한다.
class Memeber{
@ManyToOne
@JoinColumn(name="team_id") //연관관계 주인
private Team team;
}
class Team{
@Id
@Column(name="team_id")
private Long id;
@OneToMany(mappedby="team")
private List<Member> members = new LinkedList<>();
}
team1에 member1을 등록하려고 한다.
따라서 team1의 members에는 member1이, member1의 team에는 team1이 저장되어야 한다.
즉, 양방향 연관관계를 설정해주어야 한다.
그렇다면 어떻게 양방향 연관관계를 설정해줄 수 있을까?
JPA에서는 다음과 같이, 연관관계의 주인을 통해서 양방향 연관관계 설정이 가능하다.
Team team1 = new Team();
Member member1 = new Member();
member1.team = team1; //연관관계의 주인인 멤버에 team을 설정
하지만 여기서 문제는, 연관관계 주인이 아닌 Team 쪽에서는 DB에 저장되고 난 이후에 적용이 된다는 것이다. (insert 쿼리가 flush() 되기 전까진 Team쪽에서는 Member가 저장되지 않아 사용할 수 없다.)
따라서 이 두 번째 방법을 사용하는 것이 적절하다. 다음과 같이 양쪽에 모두 직접 설정을 해주는 것이다.
Team team1 = new Team();
Member member1 = new Member();
member1.team = team1; //멤버에 팀을 설정
team1.members.add(member1); //팀에 멤버를 설정
TMI
이를 위해 보통, 연관관계 편의 메서드를 이용한다.
일단 cascade옵션을 사용하였을 때와 사용하지 않았을 때를 비교한 후, 설명하려 한다.
아래는 코드의 전제 상황이다.
Team team = new Team();
Member member1 = new Member();
Member member2 = new Member();
Member member3 = new Member();
//연관관계 편의메서드로, 양방향 연관관계 설정
member1.setTeam(team);
member2.setTeam(team);
member3.setTeam(team);
저장할 때 예시
Cascade 사용X
em.persist(team); //team 저장 em.persist(member1); //member1 저장 em.persist(member2); //member2 저장 em.persist(member3); //member3 저장Cascade 사용
em.persist(team); //team 저장과 함께, team에 저장된 member들도 저장상태 전이
삭제할 때 예시
Cascade 사용X
em.remove(member1); //member1 삭제 em.remove(member2); //member2 삭제 em.remove(member3); //member3 삭제 em.remove(team); //team 삭제Cascade 사용
em.remove(team); //team 삭제와 함께, team에 저장된 member들도 삭제상태 전이
Team과 Team에 소속된 member들은, 자바 객체의 관점에서 같은 상태를 가진다.
따라서 DB에서도 마찬가지로 상태를 동일 시 해주기 위해, 영속화 된 엔터티와 연관관계를 가진 엔터티에게, 영속성 상태를 전이해주는 것이라고 볼 수 있을 것 같다.
(SQL의 cascade와 다른 의미이다.)
TMI
아래 코드가 잘 실행되는 이유가 무엇인가
Team team = new Team(); Member member1 = new Member(); teamRepository.save(team); //영속화 후 member1.setTeam(team); //양방향 연관관계 설정영속화 된 엔터티에 대해선 변경감지를 통해 수정이 가능함. 그리고 Member는 (엔터티이긴 하지만) Team의 필드임.
이 때 Cascade 즉 영속성 상태 전이가 걸려 있으므로, Team의 필드인 members가 수정됨에 따라 member1도 영속화가 됨. (만약 Cascade옵션이 안걸려있다면, 수정이긴 하지만 영속성 상태 전이가 불가능 하므로 저장되지 않음)
[TIL] 영속성 관리 참고
orphanremoval 옵션에 대한 이야기다.
양방향 연관관계가 설정된 상태에서, Team쪽에서 member에 대한 연관관계를 잃으면, 해당 member는 고아상태가 되어 제거된다.
Team쪽에서 연관관계가 끊어지는 상황은 다음과 같이 2가지가 있다.
team.members.remove(member1);
em.remove(team);
위 내용에 따라 CascadeType.REMOVE는 orphanremoval 옵션으로 대체가 가능한 것 같다.
그렇다면 Cascade타입은 언제 사용할까?
일단, 위 예시에서 Team과 그에 소속된 Member를, Team과 함께 한 번에 저장할 때 필요한 것 같다.
김영한 JPA 책 참고
190p 연관관계 편의 메서드
307p 영속성 전이 : Cascade
311p 고아객체
90p 영속성 관리