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

양방향 매핑

@Entity
public class Member {

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

    @Column(name = "USERNAME")
    private String username;

    //Member와 Team은 다 대 1 관계
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

}
@Entity
public class Team {

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

    private String name;

    //회원에서 팀을 참조할 뿐만 아니라
    //팀에서도 회원을 참조할 수 있도록 팀에 멤버 리스트를 추가(양방향 매핑)
    @OneToMany(mappedBy = "team") //팀과 멤버는 1 대 다 관계이고, team은 Member의 team 변수로 매핑되어 있음을 설정
    private List<Member> members = new ArrayList<>();

}
//양방향 매핑 후 반대 방향으로 객체 그래프 탐색
Team findTeam = em.find(Team.class, team.getId());
int memberSize = findTeam.getMembers().size(); //역방향 조회
  • 객체와 테이블 간의 연관관계를 맺는 차이
    • 객체의 양방향 관계는 단방향 2개를 합친 것이다.
      • 회원 → 팀 연관관계
      • 팀 → 회원 연관관계
    • 테이블의 양방향 관계는 FK 하나를 지정하면 어느 쪽으로든 관계가 형성된다.(양쪽으로 조인)
      • 회원 ↔ 팀의 연관관계
  • 연관관계 주인 지정하기
    • 양방향 매핑을 하려면 객체의 두 관계 중 하나를 연관관계의 주인으로 지정한다.
    • 주인이 아닌 쪽에 mappedBy 속성을 지정하기만 하면 된다.
    • 연관관계의 주인만이 외래 키를 등록, 수정할 수 있다.
    • 주인이 아닌 쪽은 읽기만 가능하다.
    • 비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안 된다.
    • 외래 키가 있는 곳을 주인으로 지정한다.

양방향 매핑시 주의점

//양방향 매핑시 많이 하는 실수
//연관관계의 주인에 값을 입력하지 않음
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");

//역방향(주인이 아닌 방향)만 연관관계 설정하면 fk에 null이 들어간다.
team.getMembers().add(member);
em.persist(member);
team.getMembers().add(member);
member.setTeam(team); //연관관계의 주인에 값 설정
em.persist(member);
  • 양방향 매핑 시 연관관계의 주인에 값을 입력해야 한다.
  • 순수한 객체 관계를 고려해서 항상 양쪽에 값을 설정하자.
    • 코드를 간결히 하고 싶으면 연관관계 편의 메소드를 생성하자.
    • 연관관계 편의 메소드는 한 쪽만 사용해야 한다.
  • Member의 연관관계 편의 메소드를 이용한 경우
@Entity
public class Member {
    ...
    //연관관계 편의 메소드
    public void changeTeam(Team team){
        this.team = team;
        team.getMembers().add(this);
    }
}
member.changeTeam(team);
em.persist(member);
  • Team의 연관관계 편의 메소드를 이용한 경우
@Entity
public class Team {
    ...
    //연관관계 편의 메소드
    public void addMember(Member member){
        member.setTeam(this);
        members.add(member);
    }
}
team.addMember(member);
em.persist(member);
  • 양방향 매핑 시 무한 루프를 조심하자.
    • toString(), lombok, JSON 생성 라이브러리
    • lombok으로 toString()를 이용하지 말자.
    • Controller에는 Entity를 JSON으로 반환하지 말자.
      • 무한 루프 문제
      • Entity가 변경될 수도 있으며, 문제가 생길 수 있다.
      • DTO로 변환해서 반환하자.

양방향 매핑 정리

  • 단방향 매핑만으로 이미 연관관계 매핑이 완료된 것이다.
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
  • JPQL에서 역방향으로 탐색할 일이 많다.
  • 단방향 매핑을 잘 해두면 양방향은 필요할 때 추가하면 된다.
    • 테이블에 영향을 주지 않기 때문