JPA 연관관계 매핑

Doyeon·2023년 2월 11일
0
post-thumbnail

연관관계 매핑

키워드

  • 방향(Direction)
    • 단방향 : 객체 관계에서 한 쪽만 참조
    • 양방향 : 객체 관계에서 양쪽이 서로 참조
    • 데이터베이스 테이블은 키 하나로 조인을 사용 → 항상 양방향 쿼리 사용 가능
  • 다중성(Multiplicity)
    • 다대일(N:1) : @ManyToOne
    • 일대다(1:N) : @OneToMany
    • 일대일(1:1) : @OneToOne
    • 다대다(N:M) : @ManyToMany
  • 연관관계의 주인(Owner) : 객체가 양방향 연관관계라면 주인을 정해야 한다.
    • 연관관계의 주인만 데이터베이스 연관관계와 매핑되고, 외래 키를 관리(등록, 수정, 삭제)할 수 있다.
    • 주인이 아닌 쪽은 읽기만 할 수 있다.
    • 주인이 아닌 쪽에 mappedBy 속성으로 연관관계 주인을 지정
    • 외래 키가 있는 곳이 연관관계 주인
   class Team {
   		@OneToMany(mappedBy="team") // 연관관계의 주인은 Member.team
   		private List<Member> members = new ArrayList<Member>();
   }

객체 연관관계 vs 테이블 연관관계

  • 객체 연관관계
    • 참조(주소)로 연관관계를 맺는다.
    • 양방향 참조하려면 단방향 연관관계가 2개 필요하다.
      • A → B (a.b)
      • B → A (b.a)
  • 테이블 연관관계
    • 테이블은 외래키로 연관관계를 맺는다.
    • 외래키 사용 연관관계는 양방향이다.
      • A JOIN B = B JOIN A

양방향 연관관계 주의점

  • 연관관계 주인이 아닌 곳에만 값을 설정하면 안된다.
    Member member1 = new Member("member1", "회원1"); // Member가 주인
    em.persist(member1)
    
    Team team1 = new Team("team1", "팀1");
    team1.getMembers().add(member1); // 주인이 아닌 곳(Team)에만 연관관계를 설정했다.
  • (해결1) 순수한 객체까지 고려한 양방향 연관관계
    • 양쪽 방향 모두에 값을 입력해준다.
      member1.setTeam(team1);  // 연관관계 설정 member1 -> team1
      team1.getMembers().add(member1); // 연관관계 설정 team1 -> member1
  • (해결2) 연관관계 편의 메소드
    • 연관관계 주인쪽에서 연관관계 설정할 때, 양방향 연관관계 설정을 해준다.
      // Member
      private Team team;
      
      public void setTeam(Team team) {
      		this.team = team;
      		team.getMembers().add(this);
      }

다대일(N:1)

다대일 단방향 [N:1]

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

// 연관관계 설정
public void setTeam(Team team) {
		this.team = team;
}
  • @ManyToOne
    • 다대일(N:1) 관계
    • 속성
      • optional : false면, 연관된 엔티티가 항상 있어야 한다.
      • fetch : FetchType.EAGER, FetchType.LAZY
      • cascade : 영속성 전이 기능 사용
  • @JoinColumn(name=”TEAM_ID”)
    • 외래 키 이름을 지정하여 매핑한다
    • 생략 가능 → (필드명 + _ + 참조하는 테이블 컬럼명)으로 외래 키 생성

다대일 양방향 [N:1, 1:N]

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

// Team
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();
  • 외래 키가 있는 쪽이 연관관계의 주인이다.
    • 다대일에서 다(N)에 외래키가 있다.
  • 양방향 연관관계는 항상 서로를 참조해야 한다.

일대다(1:N)

일대다 단방향 [1:N]

  • 연관관계의 주인이 1 이다.
  • 매핑한 객체(Team)가 관리하는 외래키가 다른 테이블(Member)에 있다. → UPDATE SQL 추가 실행
  • 일대다 단방향 매핑보다는 다대일 양방향 매핑 사용 권장

일대다 양방향 [1:N, N:1]

  • 일대다 양방향 매핑은 존재하지 않는다. → 다대일 양방향 매핑 사용해야 한다.
  • 굳이 일대다 양방향 매핑하려면, 같은 외래 키를 사용하는 다대일(@ManyToOne) 단방향 매핑을 읽기 전용으로 추가해야 한다.
    // Member
    @ManyToOne
    @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
    private Team team; // Member.team 은 읽기 전용이 되었다

일대일(1:1)

  • 주 테이블, 대상 테이블 중 누가 외래키 가질지 선택해야 한다.
  • 외래키를 갖고 있는 쪽이 주인이다.
  • 주인이 아닌 곳에 mappedBy를 넣어준다.

일대일 단방향 [1:1]

// Memebr
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;

일대일 양방향 [1:1]

// Memebr
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;

// Locker
@OneToOne(mappedBy = "locker")
private Member member;
  • MEMBER 테이블이 외래키를 갖고 있으므로 Member.locker가 연관관계 주인이다.
  • Locker.member에는 mappedBy 선언해야 한다.

다대다 [N:M]

  • 관계형 데이터베이스는 다대다 관계 표현 불가 → 일대다, 다대일로 풀어내도록 연결 테이블 사용
  • 객체는 객체 2개로 다대다 관계 가능
  • @ManyToMany로 매핑하면 데이터베이스는 두 엔티티를 연결하는 테이블을 자동으로 만든다.
    • ex) Member , Product 두 엔티티가 다대다로 매핑될 경우, 데이터베이스에는 Member_Product 가 생긴다.
  • 엔티티 객체에서는 @ManyToMany를 붙이기만 하면, 데이터베이스가 연결 테이블을 자동으로 생성해주니 단순하고 편리하다. 하지만, 실무에서는 보통 연결 테이블에 두 엔티티의 기본키 값 뿐만 아니라 다른 정보의 컬럼이 필요한 경우가 많다. → 연결 테이블을 매핑하는 연결 엔티티를 만들고, 연결 엔티티에 필요한 컬럼들을 추가해야 한다. → 엔티티 간의 관계도 일대다, 다대일 관계로 풀어야 한다.

연결 엔티티 생성

  • 다대다 관계를 일대다 다대일 관계로 풀기 위해 연결 테이블을 만들 때, 식별자(PK) 구성 방식이 나뉘어진다.
  • 식별 관계 : 두 엔티티에서 받아온 식별자를 기본키 + 외래키로 사용한다.
    • 복합키(기본키+외래키) 사용 방법은 복잡하다.
  • 비식별 관계 : 두 엔티티에서 받아온 식별자는 외래키로 사용하고, 새로운 식별자를 추가한다.
    • 단순하고 편리하게 ORM 매핑을 할 수 있다.

[참고자료]
<자바 ORM 표준 JPA 프로그래밍>

profile
🔥

0개의 댓글