[TIL] 양방향 연관관계 다루기 feat.JPA

이형석·2024년 10월 5일

이 글은 다음 두 클래스가 양방향 연관관계를 가지고 있는 상황을 가정한다.

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<>();
}

양방향 연관관계 저장하는 법

상황

team1member1을 등록하려고 한다.
따라서 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 : 영속성 전이

일단 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들도 삭제상태 전이

개념 정리

TeamTeam에 소속된 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가지가 있다.

1. Team의 List<Member>에서, 해당 member를 삭제함

team.members.remove(member1);

2. Team자체가 삭제됨

em.remove(team);

생각 정리

위 내용에 따라 CascadeType.REMOVE는 orphanremoval 옵션으로 대체가 가능한 것 같다.
그렇다면 Cascade타입은 언제 사용할까?

일단, 위 예시에서 Team과 그에 소속된 Member를, Team과 함께 한 번에 저장할 때 필요한 것 같다.


김영한 JPA 책 참고
190p 연관관계 편의 메서드
307p 영속성 전이 : Cascade
311p 고아객체
90p 영속성 관리

profile
금융IT 개발자

0개의 댓글