다양한 연관관계 매핑

Gyeongjae Ham·2023년 5월 26일
0

JPA

목록 보기
6/12
post-thumbnail

해당 시리즈는 김영한님의 JPA 로드맵을 따라 학습하면서 내용을 정리하는 글입니다

연관관계 매핑 시 고려 사항

  • 다중성
  • 단방향, 양방향
  • 연관관계의 주인'

다중성

  • 다대일: @ManyToOne
  • 일대다: @OneToMany
  • 일대일: @OneToOne
  • 다대다: @ManyToMany

일대다 단방향

  • 다대일 매핑은 테이블에서 외래키를 가진 객체가 연관관계의 주인 역할을 하기 때문에 큰 문제점이 없었습니다
  • 하지만 일대다 매핑은 그와 정반대로 외래키를 가지지 않은 테이블을 표현한 객체가 연관관계의 주인 역할을 수행합니다
  • 비지니스적으로 이런 요구사항이 요청되면 나올 수 있는 상황인데요. 이렇게 되면 위 예제에서 Team 엔티티에서 membersMember를 추가하게 되면 MEMBER 테이블의 외래키를 업데이트하는 쿼리가 추가적으로 나가게 되는 문제가 발생합니다

일대다 양방향

  • 그렇다면 어떻게 사용하는게 좋을까요?
    • 기본적으로는 다대일 방향을 사용하시되 특정 조건때문에 일대다 방향으로 설정해야 한다면 일대다 단방향보다는 다대일 양방향 매핑을 사용하는게 좋습니다
  • 그리고 양쪽 모두에게 연관관계를 설정해주되 외래키를 가진 테이블을 표현한 엔티티에는 읽기전용으로만 설정해주는 겁니다
@Entity
public class Team {
	@OneToMany
    @JoinColumn(name = "TEAM_ID")
	List<Member> members = new ArrayList<>();
}

@Entity
public class Member {
	@ManyToOne
    // 읽기 전용으로 설정하기 위해서 insert와 update가 안되도록 합니다
    @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
    private Team team;
}

일대일 방향

  • 다대일 방향처럼 외래키가 있는 곳이 연관관계 주인입니다
  • 반대편은 mappedBy를 설정해 주도록 합니다
  • 그렇다면 일대다 단방향처럼 외래키를 가지지 않은 쪽이 연관관계의 주인이 될 수 있을까요?
    • 결론은 JPA에서 지원하지 않습니다

일대일 정리

주 테이블에 외래키
  • 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래키를 두고 대상 테이블을 찾는 형태입니다
  • 개발자가 사용편의 때문에 선호하는 방식입니다
  • 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인이 가능합니다
  • 단점: 값이 없으면 외래키에 null 값이 들어갑니다
대상 테이블에 외래키
  • 대상 테이블에 외래키가 존재하는 형태입니다
  • DBA분들이 장기적인 관점에서 선호하는 형태입니다
  • 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지 가능합니다
  • 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩이 됩니다

다대다

  • 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없습니다
  • 연결 테이블을 추가해서 관계를 풀어내서 표현합니다
  • 다대다 단방향이나 양방향으로 아래와 같이 설정을 하면 그림과 같은 구조가 데이터베이스에 생성됩니다
// 단방향일 경우
@Entity
public class Member {
	@ManyToMany
    @JoinTable(name = "MEMBER_PRODUCT")
    private List<Product> products = new ArrayList<>();
}
// 양방향일 경우
@Entity
public class Member {
	@ManyToMany
    @JoinTable(name = "MEMBER_PRODUCT")
    private List<Product> products = new ArrayList<>();
}

@Entity
public class Product {
	@ManyToMany(mappedBy = "products")
    private List<Member> members = new ArrayList<>();
}

  • 두 테이블을 연결해서 표현해주는 Member_Product라는 테이블이 생성되었고, 각각 FK이자 PK 역할을 하는 값들이 생성되어 있습니다
  • 편리해 보이지만 실무에서 사용하는 건 지양해야 합니다
  • 보통 연결 테이블이 단순히 연결 기능만 하고 끝나는 경우가 없습니다
  • 예를 들어 주문 시간, 수량 같은 데이터가 들어오는 경우가 있습니다

다대다 한계 극복

  • 연결 테이블용 엔티티를 추가해서 해결합니다(연결 테이블을 엔티티로 승격시켜주는 겁니다)
  • @ManyToMany -> @OneToMany, @ManyToOne
@Entity
public class MemberProduct {
	@ManyToOne
    @JoinColumn(name = "MEMBER_ID")
	private Member member;
    
    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
	private Product product;
}

@Entity
public class Member {
	@OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts = new ArrayList<>();
}

@Entity
public class Product {
	@OneToMany(mappedBy = "product")
    private List<MemberProduct> memberProducts = new ArrayList<>();
}
profile
Always be happy 😀

0개의 댓글