객체 관계 매핑(ORM)에서 가장 어려운 부분이 객체 연관관계와 테이블 연관관계를 매핑하는 일이다.
연관관계를 매핑할 때 다음 3가지를 고려해야 한다.
방향(Direction)
[단방향, 양방향] 존재
회원, 팀 관계가 있을 때
회원 → 팀 또는 팀 → 회원 둘 중 한 쪽만 참조한다면 단방향
회원 → 팀, 팀→ 회원 양쪽 모두 서로 참조하는 것은 양방향 관계
방향은 객체관계에만 존재하고, 테이블은 항상 양방향
다중성(multiplicity)
[다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)] 존재
회원, 팀 관계가 있을 때
여러 회원은 한팀에 속하므로 회원 : 팀 = N : 1
한팀에 여러 회원에 속하므로 팀 : 회원 = 1 : N
연관관계의 주인(owner)
객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 한다.
참조를 통한 연관관계는 언제나 단방향이다. 객체간에 연관관계를 양방향으로 만들고 싶으면 반대쪽에도 필드를 추가해서 참조를 보관해야 한다. 결국 연관관계를 하나 더 만들어야 한다. 하지만 이것은 엄밀히 말하면 양방향 관계가 아니라 서로 다른 단방향 관계 2개다. 반면에 테이블은 외래 키 하나로 양방향으로 조인할 수 있다.
단방향 관계(A->B)
class A{ B b; } class B {}
양방향 관계(A<->B)
class A { B b; } class B { A a; }
객체 연관관계 vs 테이블 연관관계 정리
테이블의 구조가 위와 같을 때, JPA를 사용해서 둘의 관계를 매핑해보자.
회원 엔티티
팀 엔티티
회원 엔티티는 팀 엔티티에 N:1(단방향) 관계를 갖는다.
@JoinColumn은 외래키를 매핑할 때 사용한다.
다대일 관계에서 사용한다.
지금까지 알아본것은 회원에서 팀으로만 접근하는 다대일 단방향 매핑이었다.
이번에는 반대 방향인 팀에서 회원으로 접근하는 관계를 추가하여 회원 → 팀, 팀 → 회원으로 접근할 수 있도록 양방향 연관관계 매핑을 해보자
팀 엔티티에 회원 엔티티를 추가하자
팀 엔티티
팀 엔티티에 컬렉션인 List를 추가하였고, @OneToMany 매핑 정보에 mappedBy 속성을 줬다.
mappedBy 속성은 양방향 매핑일 때만 사용하는데, 반대쪽 매핑의 필드 값을 주면된다. (Member 클래스의 Team 매핑 필드값인 team)
@OneToMany의 mappedBy는 왜 필요한 것일까?
사실 객체에는 양방향 연관관계라는 것이 없고, 서로 다른 단방향 연관관계 2개를 묶어서 양방향인 것처럼 보이게 하는 것이다. (반면에 데이터베이스는 외래 키 하나로 양쪽이 서로 조인할 수 있음. 따라서 외래 키 하나만으로 양방향 연관관계를 맺음)
객체 연관관계는 아래와 같다
회원 → 팀 연관관계 1개(단방향)
팀 → 회원 연관관계 1개(단방향)
테이블 연관관계
회원 ←> 팀 연관관계 1개(양방향)
엔티티를 단방향으로 매핑하면 참조를 하나만 사용하므로 이 참조로 외래키를 관리하면된다.
그런데 엔티티를 양방향으로 매핑하면 회원 → 팀, 팀 → 회원 두곳에서 서로를 참조한다. 따라서 객체의 연관관계를 관리하는 포인트가 2곳으로 늘어난다.
엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래 키는 하나다. 따라서 둘 사이에 차이가 발생한다. (어디서 외래 키를 관리해야 할까?)
이런 차이로 인해 JPA에서는 두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계의 주인(Owner)라고 한다.
양방향 매핑 시에는 두 연관관계 중 하나를 연관관계의 주인으로 정해야한다.
연관관계 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있다. 주인이 아닌 쪽은 읽기만 할 수 있다.
주인은 mappedBy를 사용하지 않는다.
주인이 아니면 mappedBy를 사용하여 주인을 지정해야 한다.
=> 연관관계의 주인은 외래 키가 있는 곳으로 설정해야 한다. 즉 위의 예에서 보면 외래키 team_id를 갖고있는 member테이블 즉, Member엔티티를 주인으로 설정해야 한다.
사실은 객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다. 이 부분은 편의 메소드를 만들어 두 객체의 관계를 저장하거나 삭제할 수 있다.