본문 바로가기

DB

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

객체와 테이블 사이의 연관관계 차이

Team, Member의 예시에서 Member → Team, Team → Member 로의 연관관계도 필요하다면 객체, 테이블 설계에서 어떻게 다를까?

테이블 연관관계

테이블 연관관계는 외래키 하나로 해결이 된다.

(Team→Member, Member→Team) 양쪽으로 조인이 가능하다.

그러나 객체의 관점에서 바라보면 다르다.

객체 연관관계

Member에서 team에 대한 참조를 갖는것은 물론, Team에서도 Member list에 대한 참조를 선언해줘야 한다.

연관관계의 주인과 mappedBy

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

객체와 테이블이 관계를 맺는 차이를 정리하면 다음과 같다.

  • 객체 연관관계 = 2개
    • 회원 → 팀 연관관계 1개 (단방향)
    • 팀 → 회원 연관관계 1개 (단방향)
  • 테이블 연관관계 = 1개
    • 회원 ↔ 팀의 연관관계 1개 (양방향)

객체의 양방향 관계

  • 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개다.
  • 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.
  • A → B (a.getB())
  • B → A (b.getA())

테이블의 양방향 연관관계

  • 반면, 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
  • MEMBER.TEAM_ID 외래키 하나로 양방향 연관관계를 가짐 (양쪽으로 조인할 수 있다.)
select *
from member M
JOIN team t on m.team_id = t.team_id

select *
from team t
join member m on t.team_id = m.team_id

그렇다면 테이블과 객체 사이의 간극을 어떻게 좁혀야 할까?
답은 둘 중 하나로 외래 키를 관리해야 한다. 그리고 이 외래 키를 관리하는 주체를 연관관계의 주인으로 정한다.

연관관계의 주인(Owner)

양방향 매핑 규칙

  • 객체의 두 관계중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래 키를 관리(등록, 수정)
  • 주인이 아닌쪽은 읽기만 가능
  • 주인은 mappedBy 속성 사용 x
  • 주인이 아니면 mappedBy 속성으로 주인 지정

누구를 주인으로?

  • 외래 키가 있는 곳을 주인으로 정해라
  • 여기서는 Member.team이 연관관계의 주인

mapped by

  • mappedBy : @OneToMany 어노테이션에 추가해줘야하는 속성, 어디에 매핑되어있는지를 지정해줘야한다. 연관관계의 주인의 필드명을 적어준다.
@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "TEAM_ID")
    private Team team; // mapping 되는 필드
}
@Entity
public class Team {

    @Id
    @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team") //mappedBy 속성으로 매핑되는 연관관계 외래키 필드명을 지정한다.
    private List<Member> members = new ArrayList<>();
}

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

  • 연관관계의 주인에 값을 입력하지 않음
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("harris");
em.persist(member);

team.getMembers().add(member); // 주인이 아닌쪽에서 추가
tx.commit();
  • 위의 예제 코드에서 연관관계의 주인은 Member 이다. 따라서 Member에서 team을 세팅해 줘야 정상적으로 외래키 값이 입력된다.
  • 반면 team의 member 필드에 add 해준다고 해서 Member의 외래키인 team_id 가 업데이트 되지 않는다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("harris");
member.setTeam(team); //주인에서 연관관계 세팅
em.persist(member);

tx.commit();

양방향 연관관계 주의

  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
  • 연관관계 편의 메소드를 생성하자
  • 양방향 매핑시에 무한 루프를 조심하자
    • 예: toString(), lombok, JSON 생성 라이브러리

연관관계 편의 메소드

public void setTeam(Team team) {
    this.team = team;
    team.getMembers().add(this); // member.setTeam(team) 을 호출했을 때 team 에도 member가 추가되게 한다.
}

 

Ref : 김영한님 inflearn 강의

'DB' 카테고리의 다른 글

Real MySQL 1장 ~ 3장  (0) 2022.05.23
JPA - 프록시  (0) 2021.09.12
JPA - @MappedSuperclass  (0) 2021.09.11
JPA - 다양한 연관관계 매핑  (0) 2021.09.11
JPA - 테이블 지향 모델링 vs 객체 지향 모델링 with jpa  (0) 2021.09.10