Notice
Recent Posts
Recent Comments
Link
관리 메뉴

look-forest

연관관계 매핑 기초 본문

JPA/JPA

연관관계 매핑 기초

studyHub 2024. 9. 9. 00:26

테이블 중심 모델링의 문제점

객체를 테이블에 맞추어 모델링

참조 대신에 외래 키를 그대로 사용한다.

//멤버의 팀을 조회하는 경우
Member findMember = em.find(Member.class, memberId);

//식별자로 다시 조회, 객체 지향적인 방법은 아니다
Team findTeam = em.find(Team.class, findMember.getTeamId());

위 예시 처럼 객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.

객체 중심에서는 데이터간의 연관관계가 아니라, 메시지를 보낼 필요가 있을 때 연관관계를 맺는다.

=> 식별자가 아닌 객체 자체로 연관관계를 만들어야 한다.


단방향 연관관계

객체 지향적 모델링

객체의 참조와 테이블의 외래 키를 매핑
teamId가 아닌 team으로 연관관계 매핑. 대신 FK 명시. @JoinColum : FK를 지정. 상대의 PK와 Join한다
객체 간의 협력 관계를 바로 만든다.
참조로 연관관계 조회 - 객체 그래프 탐색이 가능


양방향 연관관계와 연관관계의 주인

객체와 테이블이 관계를 맺는 차이

테이블은 FK 하나로, 객체는 참조가 2개.


테이블의 양방향 연관관계

  • 테이블은 외래 키 하나로 두 테이블이 연관관계를 맺음 (외래 키 하나로 양쪽 조인 가능)
  • MEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계 가짐 (양쪽으로 조인할 수 있다.)

객체의 양방향 관계

  • 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단뱡향 관계 2개다.
  • 객체 양방향 관계는 A->B, B->A 처럼 참조가 2군데. 둘 중 테이블의 외래 키를 관리할 곳을 지정해야 한다.

둘 중 하나로 외래 키를 관리해야 한다.

Member의 Team이 변경될 때 update 문을 날려야 할까? Team의 Members가 변경될 때 update 문을 날려야 할까?

둘이 다를 경우엔?

기준을 정해야 한다.

FK가 있는 쪽에 변경이 일어날 때 update 문 날리는게 자연스럽다

 

연관관계의 주인(Owner) - 객체 양방향 연관관계는 관리 주인이 필요

양방향 매핑 규칙

  • 객체의 두 관계중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래 키를 관리 (등록, 수정)
  • 주인이 아닌쪽은 외래 키에 영향을 주지 않음, 단순 조회만 가능
    mappedBy 속성으로 연관관계 주인에 포함된 참조의 이름 지정

Member 엔티티에 team이라는 필드로 mapping 되어 있으니 그걸 보고 데이터 조회해서 채워줘.

 

누구를 주인으로 정할까?

외래 키가 있는 있는 곳을 주인으로 정해라.

Team이 주인일 경우, Team에 변경을 가했는데 FK가 있는 Member 테이블이 변경되면 헷갈린다.

외래키가 있는 Member가 변경될때 Member 테이블이 변경되는 것이 직관적이다.

N:1 관계에서 N쪽에 FK가 있다. 따라서 N쪽이 주인이 된다. 예를 들어 자동차(1)와 바퀴(N)에서 바퀴가 주인이 된다.

DB 설계상 1:N일 경우 외래키는 N에 있어야 한다. 1에 FK가 있으면 1의 PK가 중복된다. FK도 N개가 되니까.
가령 teamA에 memeber1,2,3에 추가될 경우, PK인 teamA가 중복된다.
teamA - member1
teamA - member2
teamA - member3

 

양방향 매핑시 주의할 점

1. 양방향 매핑시 연관관계의 주인에 값을 입력/변경해야 한다!

연관관계의 주인에 값을 입력하지 않는 경우, DB에 반영되지 않는다.

주인이 아니기 때문에 읽기 전용. member.team_id가 들어가지 않는다.

그러나, 순수한 객체 관계를 고려하면 항상 양쪽 다 값을 입력해야 한다.

한쪽에만 입력할 경우, 1차 캐시를 이용하기 때문에 역뱡향 조회시 조회 불가하다.

em.clear()로 영속성 컨테이너를 비우지 않는 이상 1차 캐시를 이용하기 때문에 team에서 member 조회 불가

 

다만 양쪽 다 입력하는 것을 누락할 수 있으므로, 연관관계 편의 메소드를 생성하자.

Member에 Team을 업데이트할 때 Team에도 Member를 추가해주는 메소드
사용 예시

 

2. 양방향 매핑시에 무한 루프를 조심하자.

toString(), lombok, JSON 생성 등에서 조심

  -> Member 객체를 toString으로 바꿀 때 Team 객체의 toString을 호출, Team 내 Member의 toString 호출,,,

 => 롬복에서 toString 금지

 => 컨트롤러에서 엔티티 반환 금지 (무한루프 때문이 아니더라도, 엔티티 변경시 API 스펙이 변경되므로. DTO 반환)

 

결론

문제 소지가 많고 복잡하다. 가급적 단방향 매핑으로 끝내라.

단방향 매핑만으로도 이미 연관관계 매핑은 완료된다. 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐. 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 된다. (테이블에 영향을 주지 않음. Team 내에 Members 리스트가 필요할 경우와 같이 엔티티만 영향)

사실 JPQL에서 역방향으로 탐색할 일이 많고, 로직 상 필요할 수도 있다. 그럴때, 추후 개발 시점에 추가해라.

생각해보면 Member 에서 Team으로 조회하면 될 것을 굳이 Team에서 Member를 조회하려다보니 생긴 일이다.
이런건 설계가 잘못된 것일 수 있다.

 


참고 자료 & 이미지 출처
자바 ORM 표준 JPA 프로그래밍 - 기본편 (김영한 님)


 

'JPA > JPA' 카테고리의 다른 글

고급 매핑  (0) 2024.09.10
다양한 연관 관계 매핑  (0) 2024.09.09
엔티티 매핑  (1) 2024.09.08
영속성 관리 - 내부 동작 방식  (0) 2024.09.08
JPA 시작  (0) 2024.09.08