본문 바로가기
Back-End/Database

[JPA] 다양한 연관 관계 매핑

by 달의 조각 2023. 1. 23.
이 글은 김영한 님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 수강하며 정리한 글입니다.

 

다대일 [N:1]
일대다 [1:N]
일대일 [1:1]
다대다 [N:M]

 


 

대일 [N:1]

 

🌕 단방향

[테이블] '다'에 외래키가 있다 => [객체] '다'에 해당하는 쪽이 연관 관계의 주인

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

 

🌕 양방향

테이블 구조는 변화가 없다

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

 


 

대다 [1:N]

더보기

🌕 단방향

[테이블] '다'에 외래키가 있다 =>&nbsp; [객체] '일'에 해당하는 쪽이 연관 관계의 주인 (관리한다)
// Team.java
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();

엔티티가 관리하는 외래 키가 다른 테이블에 있다. 연관 관계 관리를 위해 추가로 UPDATE SQL를 실행하게 된다. (Team 엔티티만을 수정했는데 Member 테이블에 업데이트 쿼리가 날아가는 상황) 테이블이 복잡해지면 운영이 어려워진다.

일대다 단방향보다는 참조가 하나 더 늘어나더라도 다대일 양방향 매핑을 사용하는 것이 좋다.

 

🌕 양방향

@ManyToOne
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false) // insertable과 updatable 속성이 없으면 연관 관계의 주인처럼 됨
private Team team;

이런 매핑은 공식적으로 존재하지 않으나 읽기 전용 필드를 사용해서 양방향처럼 사용하는 방법이다.

 


 

일대일 [1:1]

 

  주 테이블이나 대상 테이블 중에 외래 키를 선택할 수 있다. 외래 키에 데이터베이스 유니크(UNI) 제약 조건이 추가된다. 

주 테이블에 외래 키를 두는 방법은 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래 키를 두고 대상 테이블을 찾는 것이다. JPA 매핑이 편리하고, 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인이 가능하기 때문에 객체 지향 개발자가 선호한다. 값이 없으면 외래 키에 null을 허용한다는 단점이 있다.

대상 테이블에 외래 키를 두는 방법은 비즈니스 로직상 일대일에서 일대다 관계로 변경해야 할 때 테이블 구조를 유지할 수 있다는 장점이 있어서 전통적인 데이터베이스 개발자가 선호한다. 단점으로는 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩된다는 것이다.

 

🌕 단방향

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

엔티티가 관리하는 외래 키가 다른 테이블에 존재하는 형태는 불가능하다. (양방향으로 만들어야 한다.)

 

🌕 양방향

@OneToOne(mappedBy = "locker")
private Member member;

엔티티가 관리하는 외래 키가 다른 테이블에 존재하는 형태가 가능하다.

 


 

다대다 [N:M]

 

  관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다. 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어낼 수 있다. 반대로 객체는 컬렉션을 사용해서 객체 2개로 다대다 관계를 표현할 수 있다.

편리해 보이지만 실무에서 사용하지 않는다. 실무에서는 연결 테이블이 단순히 연결 역할로만 끝나지 않고, 여러 필드가 추가될 수 있다. 하지만 다대다에서 만들어지는 연결 테이블은 딱 매핑 역할만 한다. (눈에 보이지 않는다.)

더보기

🌕 단방향

@ManyToMany
@JoinTable(name = "MEMBER_PRODUCT") // 연결 테이블 지정
private List<Product> products = new ArrayList<>();

 

🌕 양방향

@ManyToMany(mappedBy = "products")
private List<Member> members = new ArrayList<>();

 

연결 테이블을 엔티티로 승격하고, @ManyToMany@OneToMany@ManyToOne으로 풀어낸다.

@Entity
public class MemberProduct {

    @Id @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;
}
// Member.java
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts = new ArrayList<>();
// Product.java
@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProducts = new ArrayList<>();

댓글