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

17756 단어 JPAJPA

참고

양방향 매핑

  • member entity는 단방향과 동일, team entity는 컬렉션 추가
@Entity
public class Member {

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

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

//    @Column(name = "TEAM_ID")
//    private Long teamId;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Team getTeam() {
        return team;
    }

    public void setTeam(Team team) {
        this.team = team;
    }
}
@Entity
public class Team {

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

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Member> getMembers() {
        return members;
    }

    public void setMembers(List<Member> members) {
        this.members = members;
    }
 }

사실 객체에서 양방향 연관관계는 없다. 단방향 관계 2개가 존재

테이블은 단방향이든 양방향이든 동일하다. 그 이유는 member는 FK로 PK를 조인하면 되고
팀은 PK와 FK를 조인하면 되기 때문이다.

연관관계의 주인과 mappedBy

  • mappedBy = JPA의 멘탈붕괴 난이도
  • mappedBy는 처음 이해하기 어렵다.
  • 객체와 테이블간에 연관관계를 맺는 차이를 이해해야 한다.
  • 여기서 mappedBy는 1:N 매핑에서 나랑 어떤 변수랑 관계를 맺었는지 그 변수명을 값으로 적어준다.

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

  • 객체 연관관계 = 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

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

연관관계 주인

  • TEAM_ID를 변경할 때 Member의 team을 수정해야 할까? 아니면 Team의 members를 수정해야할가?
  • 해결책은 연관관계 주인을 찾는 것이다.
  • 양방향 매핑 규칙
    • 객체의 두 관계중 하나를 연관관계의 주인으로 지정
    • 연관관계 주인만이 외래키를 관리 (등록, 수정)
    • 주인이 아닌 쪽은 읽기만 가능
    • 주인은 mappedBy 속성 사용하지 않는다.
    • 주인이 아니면 mappedBy 속성으로 주인 지정

누구를 주인으로?

  • 결론은 많은 쪽으로 주인을 정한다.
  • 외래키가 있는 곳이 주인이다.
  • 여기서 주인은 Member.team

그렇게 하지 않고 반대로 하면 team.member를 바꾸면 member쪽에서 update 쿼리가 나가는데
엄청 헷갈리기 시작하고 성능적인 이슈가 있다.

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

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

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

//역방향(주인이 아닌 방향)만 연관관계 설정
team.getMembers().add(member);
em.persist(member);
IDUSERNAMETEAM_ID
1member1null
  • 양방향 매핑시 연관관계의 주인에 값을 입력해야 한다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);

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

team.getMembers().add(member); //연관관계의 주인에 값 설정
member.setTeam(team); //**
em.persist(member);
IDUSERNAMETEAM_ID
1member12

양방향 연관관계 주의

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

롬복에서 왠만해서 toStrong 만들지 말라

JSON 생성 라이브러리는 Controller에서 만드는데 그때 반환값을 엔티티로 하지 말고 DTO로 하자
이유는 첫째, StackOverflow 둘째, 엔티티는 충분히 변경이 가능한데 엔티티를 반환하면 api spec이
변경된다.

정리

  • 단방향 매핑만으로도 이미 연관관계 매핑은 완료
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
  • JPQL에서 역방향으로 탐색할 일이 많음
  • 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 됨 (테이블에 영향을 주지 않음)
  • 비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안됨
  • 연관관계의 주인은 외래 키의 위치를 기준으로 정해야함

꿀팁은 처음 설계시에 전부 단방향 매핑으로 끝내라! 그리고 application 개발시에 필요 시, 추가하자!

좋은 웹페이지 즐겨찾기