연관관계 매핑 기초

hyemin·2022년 1월 31일
0

JPA

목록 보기
4/7
post-thumbnail

연관관계 매핑 기초

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

테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾지만 객체는 참조를 이용해서 연관된 객체를 찾기 때문에 테이블과 객체는 이러한 큰 차이가 있다.

용어 정리

  • 방향(Direction)

    • 단방향 : 관계에서 어느 한 쪽만 참조하는 것을 단반향 관계라고 한다.
    • 양방향 : 관계에서 양쪽 모두가 서로를 참조하는 것을 양방향 관계라고 한다.
  • 다중성(Multiplicity)

    • 다대일(N:1)
    • 일대다(1:N)
    • 일대일(1:1)
    • 다대다(N:M)
  • 연관관계 주인(Owner) : 객체 양방향 연관관계는 연관관계의 주인을 필수로 정해야한다.

단방향 연관관계


먼저 회원과 팀의 관계를 이용해서 다대일 관계를 알아보면 회원은 하나의 팀에만 소속될 수 있으며, 이러한 관계는 회원과 팀이 다대일 관계입니다.

객체 연관관계

  • 회원 객체는 Member.team 필드(멤버변수)로 팀 객체와 연관관계를 맺는다.
  • 회원과 팀은 단반향 관계임을 알 수 있다.
    - 회원은 Member.team으로 Team의 정보를 알 수 있지만 Team은 Member의 정보를 알 수 없다.

테이블 연관관계

  • 회원 테이블은 TEAM_ID(FK) 외래 키로 팀 테이블과 연관관계를 맺는다.
  • 회원 테이블과 팀 테이블은 양방향 관계이다.
    - 회원 테이블의 TEAM_ID(FK) 외래 키를 통해서 회원과 팀을 조인할 수 있고, 반대로 팀과 회원도 조인할 수 있기 때문이다.
select * from TEAM t JOIN MEMBER m ON t.tema_id = m.team_id
//둘 다 가능
select * from MEMBER m JOIN TEAM t ON m.team_id = t.tema_id;

객체 관계 매핑

@Entity
public class Member { 

	@Id @GeneratedValue
 	private Long id;
 
 	@Column(name = "USERNAME")
 	private String name;
 
 	private int age;

	@ManyToOne //연관관계 매핑
 	@JoinColumn(name = "TEAM_ID") //join해야하는 컬럼명
 	private Team team;
}

Member 입장에서 자신은 Many이고, Team이 One이기 때문에 다대일 관계는 @ManyToOne으로 매핑한다.

  • 참조를 통한 연관관계는 단방향이며, 연관관계를 양방향으로 만들고 싶다면, 반대쪽에도 필드를 추가하여 참조를 보관해야한다.
  • 즉 연관관계를 하나 더 만드는 것이다.
  • 양쪽에서 참조를 하는 것을 양방향 연관관계라고 하는데 , 양방향 연관관계는 서로 다른 단반향 연관관계가 2개 있는 것이다.

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

앞서 말한 것과 같이 양방향 연관관계는 단반향이 2개 있는 것이다.

  • 두 객체가 각각 참조용 필드를 가지고 참조를 하면 양방향 관계인 것이다.
  • JPA 입장에서는 객체가 양방향 관계일 때 어떤 것을 사용해야하는지 혼란을 줄 수도 있기 때문에 연관관계의 주인을 정해주어야한다.
  • 참고 : 테이블은 join하면 서로 정보를 알 수 있고, FK, PK로 서로의 연관관계를 알 수 있기 때문에 방향이랄게 없다.

회원과 팀을 양방향 관계로 하고 싶다면?
Team 엔티티에도 참조용 필드를 추가해주어야한다.
회원엔티티는 단방향과 동일하게 그대로 작성해두고,

@Entity
public class Member { 

 @Id @GeneratedValue
 private Long id;
 
 @Column(name = "USERNAME")
 private String name;
 
 private int age;

 @ManyToOne //연관관계 매핑
 @JoinColumn(name = "TEAM_ID")
 private Team team;
}

@Entity
public class Team {

 @Id @GeneratedValue
 private Long id;
 
 private String name;
 
 @OneToMany(mappedBy = "team") //연관관계 매핑
 List<Member> members = new ArrayList<Member>();
}

일(1)인 Team에 @OneToMany를 추가해 준 것을 확인할 수 있다.
그리고 또한 연관관계의 주인을 mappedBy로 지정해준다.
mappedBy로 지정할 때 값은 대상이 되는 변수명을 따라 지정하면 된다.

연관관계의 주인과 mappedBy

객체와 테이블간에 연관관계를 맺는 차이를 이해하도록 하자.

객체의 양방향 관계


객체 연관관계는 아래와 같이 2개이다.

  • 회원 -> 팀 연관관계 1개 (단방향)
  • 팀 -> 회원 연관관계 1개 (단반향)
    • 객체의 양방향 관계는 양방향 관계가 아니라, 서로 다른 단방향 관계 2개인 것을 다시 한 번 상기시키도록하자.
    • 즉, 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야한다.

테이블 연관관계는 아래와 같이 1개이다.

  • 회원 <-> 팀의 연관관계 1개(양방향)
    • 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
    • MEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계를 가진다.(양쪽으로 조인 가능)

그럼 여기서 MEMBER의 team 값을 바꿀지, TEAM의 members의 값을 바꿀지 의문이 생긴다.
둘 중 하나를 정해서 외래 키를 관리해야하는 것이다.

연관관계의 주인(Owner)

양방향 매핑 규칙을 보자

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

그럼 누구를 주인으로 정해야 할까?

답은 외래 키가 있는 곳을 주인으로 정해자!

  • 외래키가 있는 Member를 연관관계 주인으로 정하면, 자기 테이블에 있는 외래 키를 관리하면 되지만 팀 엔티티에 있는 Team.members를 주인으로 선택하면 전혀 다른 테이블의 외래 키를 관리해야 하므로 복잡해질 수 있다.
  • DB 입장에서 외래키가 있는 곳이 '다'이다.
  • Many인 쪽을 주인으로 정하자.
  • 또한, 양방향 매핑시에 연관관계의 주인에 값을 입력하도록 하자.
  • 비즈니스 로직을 기준으로 연관관계의 주인을 선택하지 않도록 하자.

양방향 연관관계 설정 시 주의할 것.

  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하도록 하자.
  • 연관관계 편의 메서드를 생성하여 값을 설정하자.
//Member의 setTeam에 이런 식으로 편의 메서드를 생성하자
public void setTeam(Team team){
    this.team = team;
    team.getMember().add.(this);
}

setTeam()보단 changeTeam()으로 만들어서 작성하는 것도 괜찮다.

  • 또한, 양방향 매핑시에 무한루프를 조심해야한다.
    • controller에서는 엔티티 반환하지 않도록 하자, 반환하면 무한루프가 생성되거나 API 스펙이 바뀌는 등 문제가 발생한다.
    • 엔티티는 DTO로 반환해서 넘기자.
  • 설계 시 단방향 매핑만으로도 이미 연관관계 매핑은 완료된다.
  • 단방향 매핑으로 설계를 잘하고, 양방향은 필요할 때 추가하도록 하자.
    (테이블에 영향을 주지 않기 때문에 이렇게 작업해도 된다.)



참고 : https://www.inflearn.com/course/ORM-JPA-Basic/dashboard 김영한 님의 JPA 프로그래밍 강의
https://book.naver.com/bookdb/book_detail.nhn?bid=9252528 자바 ORM 표준 JPA 프로그래밍
위의 책을 참고하고, 강의를 수강하면서 작성한 글입니다.
틀린 부분 등 다양한 피드백 환영합니다.

profile
열심히 성장 중 :)

0개의 댓글