JPA 연관관계 매핑과 연관관계 주인

배태현·2021년 5월 3일
6

JPA

목록 보기
2/7
post-thumbnail

시작하기 앞서 저의 글에대한 피드백이나 지적은 언제나 환영입니다 😊

연관관계 매핑

용어를 먼저 이해해보자

  • 방향(Direction) : 단방향, 양방향
  • 다중성(Multiplicity) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M) 이해
  • 연관관계의 주인(Owner) : 객체 양방향 연관관계는 관리주인이 필요하다.

연관관계는 왜 필요해?

"객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다."
-조영호(객체지향의 사실과 오해)

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

  • 테이블은 외래키로 조인을 사용하여 연관된 테이블을 찾는다.
  • 객체는 참조를 사용해서 연관된 객체를 찾는다.
  • 테이블과 객체 사이에는 이런 큰 차이가 있다.
    즉, 제대로된 협력 공동체를 만들기 위하여 연관관계가 필요하다

연관관계 매핑시 고려사항 3가지

  • 다중성
  • 단방향, 양방향
  • 연관관계의 주인

다중성

  • 다대일(N:1) : @ManyToOne
  • 일대다(1:N) : @OneToMany
  • 일대일(1:1) : @OneToOne
  • 다대다(N:M) : @ManyToMany

모두 대칭성이 있다.

테이블과 객체의 연관관계 차이

테이블

  • 외래키 하나로 양쪽 조인가능
  • 사실 방향이라는 개념이 없다

객체

  • 참조용 필드가 있는 쪽으로만 참조가능
  • 한쪽만 참조하면 단방향
  • 양쪽이 서로 참조하면 양방향 (사실 단방향이 2개)

단방향 연관관계

  • @ManyToOne
    ex) 하나의 팀이 여러명의 멤버를 가질 수 있음
  • @JoinColumn(name = "TEAM_ID)
    ex) Join하는 컬럼은 TEAM_ID 즉 Team을 TEAM_ID로 Join

양방향 연관관계

단방향 -> Member에서 Team으로 접근 가능하지만 Team에서 Member로 접근 불가하다
하지만 참조(reference)를 넣어두면 양쪽 다 참조해서 접근할 수 있게된다.
양방향 연관관계를 만들어도 단방향과 비교해봐도 테이블에 변화가 없다 (테이블은 결국 다 Join으로 조회 할 수 있기때문이다) 테이블의 연관관계는 외래키 하나로 양방향이 다 있는것이다.

이때 객체에선 문제가 일어난다 Member에선 Team으로 갈 수 있지만 Team에선 Member에 접근 할 수 있는 방법이 없었다.(객체참조와 테이블의 외래키의 가장 큰 차이이다)

그래서 Team에 List형식의 memebers를 넣어주면 양쪽으로 다 접근할 수 있게된다.

  • JPA를 설계할 때 양방향매핑은 하지말고 단방향 매핑만 해놓자 (처음엔 단방향 매핑으로 설계를 끝내야한다)
  • JPQL에서 역방향으로 탐색할 일이 더 많다
  • 단방향 매핑을 잘 해놓고 양방향은 필요할 때 추가해도 됨 (테이블에 영향을 주지 않는다)

연관관계의 주인

  • 객체의 두 관계중 하나를 연관관계의 주인으로 지정한다.
  • 연관관계의 주인만이 외래키를 관리한다. (등록, 수정)
  • 연관관계의 주인이 아닌쪽(주인의 반대편)은 읽기(단순조회)만 가능하다. (외래키에 영향을 주지않음)
  • 주인은 mappedBy속성을 사용하지 않는다.
  • 주인이 아니면 mappedBy속성으로 주인을 지정해준다
    ex) List members가 Team team을 mappedBy = "team"으로 team을 주인으로 지정하고 주인인 team이 외래키를 관리한다.

그래서 누구를 주인으로 해야하는데?


Member.team이 연관관계의 주인 (진짜매핑) 이다.
Team.members는 주인의 반대편(가짜매핑)

외래키가 있는 곳을 주인으로 정하자

그렇지 않으면 단점이 있다
1. 일단 헷갈린다
2. Team.members의 값을 바꿨다고 가정해보자 그럴 시 다른 테이블에 update쿼리문이 나가게된다
(외래키관련으로 (Team에서 insert쿼리, Member에서 update쿼리 등이 나가고 복잡해져버린다.))
3. 위와 같은 이유들로 성능이슈 또한 날 수 있다.

외래키가 있는곳을 주인으로 하는 규칙으로 하자!

이렇게 되면 설계가 깔끔해지며 외래키가 있는 테이블에서 관리가 된다는 장점이 있다.

  • DB입장에서 보면 외래키가 있는곳이 무조건 '다'( * , N )이다.
  • 외래키가 없는곳이 무조건 ( 1 )이 되는 것

즉 DB의 N쪽이 무조건 연관관계의 주인이 된다
ex)@ManyToOne

연관관계의 주인을 정하는 기준

  • 비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안됨
  • 연관관계의 주인은 외래키의 위치를 기준으로 정해야함

예를 들자면 자동차와 자동차의 바퀴가 있다.
비즈니스적으로 보면 자동차가 훨씬 중요하지만 연관관계의 주인을 바퀴로 잡는 격인 것이다.

정말 간단하게 말해서 연관관계의 주인은 그냥 N쪽 '다'인 쪽을 연관관계의 주인으로 설정하면 된다.

양방향 연관관계 매핑시 가장 많이 하는 실수

  • 연관관계 주인에 값을 입력하지 않는 것!
    실무에서 조회만 가능한 즉, 주인이 아닌쪽(가짜매핑)에 값을 넣고 DB에 값이 안들어가는 사고가 난 사례가 있음
    ex) members 즉, 연관관계의 주인이 아닌 곳 가짜매핑에 입력하고 있었던 것!
    그래서 Member에 TEAM_ID가 null값이 들어가는 것이다.
    연관관계 주인에서 값을 지정해주자
  • 양방향 매핑시 무한루프를 조심하자
    ex) toString(), lombok, JSON생성 라이브러리
    toString(), lombok : toString()을 양쪽에서 출력하여 스택오버플로우 오류가 일어난다.
    JSON생성 라이브러리 : 쭉 다 뽑아버리기 때문에 엔티티를 JSON으로 바꾸는 순간 무한루프 -> 스택오버플로우(장애) 컨트롤러에서 엔티티를 바로 반환할때 엔티티를 json으로 변환하며 연관관계가 양방향으로 걸려있다면 그때 계속 서로 파고들면서 장애가 남

오류해결

  • toString : 써도 매핑된 것들을 빼고 써라
  • json생성 라이브러리 : 컨트롤러에서 절대 엔티티를 반환하지마라 (DTO사용하기)

사실 양방향 연관관계 매핑에는 정답이 있다.
바로 순수한 객체 관계를 고려하여 항상 양쪽 다 값을 넣어주는 것이다.

  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
  • 연관관계 편의 메서드를 이용하자
    : 연관관계 편의 메소드를 사용시 어느쪽에 값을 넣어줄지는 개발자 마음대로이다.

연관관계 편의 메서드 예시

//연관관계 주인인쪽(Member)에서 작성
public void changeTeam(Team team) {
	this.team = team;
	team.getMembers().add(this);
}

//연관관계 주인이 아닌쪽(Team)에서 작성
public void addMembers(Member member) {
    member.setTeam(this);
    members.add(member);
}

글을 마치며

JPA에서 중요한 다른 한가지 연관관계 매핑에 대해 정리해보았습니다.
JPA설계 등 에서 매우 중요하다고 생각되었으며
다음 글은 다중성을 이용한 다양한 연관관계 매핑 입니다!
긴 글 읽어주셔서 감사합니다.😊

profile
일상의 불편함을 기술로 해결 할 방법을 고안합니다.

0개의 댓글