[JPA] 다양한 연관관계 매핑 (2)
06 Jul 2022해당 포스트는 인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 을 듣고 정리한 글입니다.
다양한 연관관계 매핑 (2)
일대일 (1:1)
- 일대일 관계는 그 반대도 일대일
- 주 테이블이나 대상 테이블 중에 외래 키 선택 가능
(기존 다의 관계에 들어가야할 외래키의 종속이 없다. 둘다 1의 관계라 가능)
- 주 테이블 외래키
- 대상 테이블 외래키
- 외래 키에 데이터베이스 유니크(UNI) 제약조건 추가
일대일: 주테이블에 외래키 단방향
해당 관계는 하나의 멤버는 하나의 락커를 쓸 수 있다는 매핑을 보여준다.
양방향으로 만든다면 락커에 멤버의 외래키를 추가만 해주면 된다.
일대일: 주 테이블에 외래키 양방향
- 다대일 양방향 매핑 처럼 외래 키가 있는 곳이 연관관계의 주인
- 반대편은 mappedBy 적용 (읽기전용)
일대일: 대상 테이블에 외래키 단방향
Member를 연관관계의 주인으로 하려고 하지만 외래키가 Locker에 있을 경우 단방향에서는 JPA에서 지원이 되지 않는다.
하지만 양방향으로는 가능하다. mapperBy를 이용해 양방향 처리를 해주면 된다.
정리
- 주 테이블에 외래 키
- 주 객체가 대상 객체의 참조를 가지는것처럼 주 테이블에 외래키를 두고 대상테이블을 조회
- 객체지향 개발자가 선호
- 장점 : 주 테이블만 조회해서 대상 테이블에 데이터가 있는지 확인가능
- 단점 : 값이 없으면 외래 키에 null 허용
- 대상 테이블에 외래 키
- 대상 테이블에 외래키가 존재
- 전통적인 데이터베이스 개발자 선호
- 장점 : 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
- 단점 : 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시로딩 됨
주의 해야할 점
일대일 관계에서 외래키를 설계할 때 위의 예시가 Member가 여러개의 Locker를 가지게 된다던지 반대가 되는 경우에 대해 일대다 다대일과 같이 변결 될 수 있으니, 설계에 주의 해야한다.
다대다 (N:N)
결론부터 말하자면, 실무에서는 절대 사용하면 안될 연관관계 매핑이다. 왜인지 같이 알아보도록 하자
- @ManyToMany 사용
- @JoinTable로 연결 테이블지정
- 단방향, 양방향 가능
이렇게만 보면 다른 연관관계 매핑과 동일하다. 하지만?
- 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
- 연결 테이블을 주가해서 일대다, 다대일 관계로 풀어내야한다.
다대다의 딜레마
객체는 컬렉션을 사용해서 객체 2개로 다대다 관계가 가능하다..
객체가 지원하기때문에 ORM인 JPA의 입장에서는 해당 다대다 관계를 만들었다.
/* Member Entity */
@Entity
@Getter @Setter
public class Member {
@Id @GeneratedValue
private long id;
@Column(name = "USERNAME")
private String username;
@ManyToMany
@JoinColumn(name = "MEMBER_PRODUCT", insertable = false, updatable = false)
private List<Product> product;
}
/* Product Entity */
@Entity
@Getter @Setter
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
}
수없이 생성되는 제약조건과 테이블들…
create table Member (
id bigint not null,
USERNAME varchar(255),
primary key (id)
)
create table Member_Product (
Member_id bigint not null,
product_id bigint not null
)
create table Product (
id bigint not null,
name varchar(255),
primary key (id)
)
alter table Member_Product
add constraint FKilnr7a1g687t0svjediiw44p1
foreign key (product_id)
references Product
alter table Member_Product
add constraint FKhoq1biduawpfxe9ccd0f8mknq
foreign key (Member_id)
references Member
한계
- 편리해 보이지만 실무에서는 사용 X
- 연결 테이블이 단순히 연결만 하고 끝나지 않음
- 중간 테이블에는 주문시간, 수량 같은 데이터를 넣을 수 없어서 실무에서 사용하기가 힘들다.
- 조회를 할 경우 조인 쿼리가 이상하게 생성되서 날라감
다대다 한계 극복
- 연결 테이블용 엔티티 추가 (연결 테이블을 엔티티로 승격)
- @MenyToMany -> @OneToMany, @ManyToOne
연관관계에 관한 팁
연관관계를 맺을때 ID값은 memberId나 email같은 의미있는 데이터 말고 @Id
, @GeneratedValue
와 같은 의미없는 값을 PK로 삼는것이 좋다. 비지니스적으로 조건이 추가되거나, 로직이 변경 될 경우 의미있는 값을 PK로 쓰면 크게 구조나 설계가 변경 될 수 있다.
정리하자면 좀더 유연성을 가지고 개발을 할 수 있다.
N:M 관계는 1:N, N:1로
- 테이블의 N:M 관계는 중간 테이블을 이용해서 1:N, N:1
- 실전에서는 중간 테이블이 단순하지 않다.
- @ManyToMany는 제약 : 필드 추가 X, 엔티티 테이블 불일치
- 실전에서는 @ManyToMany 사용 X