1. 연관관계 매핑 시 고려사항 3가지
- 다중성
- 단방향 vs 양방향
- 연관관계의 주인
2. 다양한 연관관계
✔️다중성
데이터베이스 관점에서 보면 됨
- 다대일(N:1) : @ManyToOne
- 일대다(1:N) : @OneToMany
- 일대일(1:1) : @OneToOne
- 다대다(N:M) : @ManyToMany
실무에서 다대다는 쓰면 안됨.
✔️다대일 단방향
(N:1)
N쪽이 외래키를 가지고 있어야하고 외래키를 가지고 있는 쪽을 연관관계 주인으로 지정해줘야한다.
✔️일대다(1:N)
1이 연관관계 주인이다.
![](https://blog.kakaocdn.net/dn/oVZct/btsIn9zG7Se/m6LHqHxkeBfhU166b82ms0/img.png)
Q. 그동안 배웠던거랑 다른 내용이라 헷갈림
N쪽이 외래키를 가지고 있어야 설계상 맞는거고 외래키를 가지고 있는 쪽을 연관관계 주인으로 지정해주라했는데
왜 1이 연관관계 주인이 될 수 있는 거지?
A. N을 연관관계 주인으로 해주라는 것은 설계상 권장사항임.
1도 연관관계 주인이 될 수 있지만 권장하지 않는 방식임.
왜냐하면, 엔티티가 관리하는 외래키가 다른 테이블에 있기때문이다.
Team에 있는 List members가 업데이트 되면 MEMBER 테이블에 있는 TEAM_ID가 업데이트 된다.
코드만 보면 Team만 건드린것 같은데, MEMBER 테이블에 update 쿼리가 나가게 된다.
실무에서는 테이블이 많기 때문에 이 구조로 운영을 하면 유지보수하기가 복잡해질 수 있다.
✔️일대일 관계
1️⃣ 주 테이블에 외래 키가 있는 경우
![](https://blog.kakaocdn.net/dn/baXpbr/btsImLtjEif/tTZ20PdZF53ihSVkJWDjdk/img.png)
✅일대일 양방향 매핑 예시
@Entity
@Getter
@Setter
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@OneToOne
@JoinColumn(name="LOCKER_ID")
private Locker locker;
}
@Entity
public class Locker {
@Id @GeneratedValue
private Long id;
private String name;
@OneToOne(mappedBy = "locker")
private Member member;
}
연관관계 주인(member)에 @JoinColumn으로 외래키 매핑을 해준다.
비주인에는 mappedBy를 써서 무엇으로 매핑되는지 명시해준다.
외래키에는 UNIQUE 키 제약조건을 걸어줘야한다.
2️⃣ 대상 테이블에 외래키 있는 경우
단방향은 지원이 안되기 때문에, 양방향으로 매핑해야한다.
![](https://blog.kakaocdn.net/dn/dPQzQr/btsInvcqy7x/KPKKNwYDLswnS3CF07gOtk/img.png)
- 기존 비즈니스 요구사항이 '한 멤버당 하나의 자물쇠를 가질 수 있다'였지만,
만약에 '한 멤버당 여러개의 자물쇠를 가질 수 있다'로 바뀐다면?
LOCKER에 외래키를 가지고 있는 것이 더 유리하다.
왜냐하면, 테이블 구조를 변경할 필요가 없기 때문이다.
- 지연로딩으로 설정해도 항상 즉시로딩 된다. (추후 프록시 포스팅에서 자세히 설명)
✔️다대다(N:M)
관계형 데이터베이스는 정규화 된 테이블 2개로 다대다 관계를 표현할 수 없다.
그래서 연결 테이블을 추가해서 일대다 or 다대일 관계로 풀어내야한다.
![](https://blog.kakaocdn.net/dn/cr22o5/btsIpUC11DL/vseBIrDwUeojiPnk5eu0tK/img.png)
객체는 컬렉션을 사용해서 다대다 관계가 된다.
@ManyToMany를 사용하고 @JoinTable로 연결 테이블을 지정한다.
그러나 실무에서는 사용하지 않는다.
💡실무에서 사용하지 않는 이유
- 두 테이블간의 관계를 표현하기 위해 조인 테이블을 생성하는데, 이로 인해 쿼리 성능이 저하된다.
- 실무에서는 조인 테이블에 추가정보를 저장해야하는 경우가 많다.
💡한계극복
@OneToMany 및 @ManyToOne 관계로 풀어내서 더 명확하게 비즈니스 로직을 표현하는 것이 좋다.
![](https://blog.kakaocdn.net/dn/bShAqC/btsIo7b1KnD/mKULkKPemtIh2FMhKbAIx1/img.png)
코드 예시에서는 중간 테이블로 MemberProduct 테이블을 만들었고 @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;
}
@Entity
@Getter
@Setter
public class Member {
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProduct = new ArrayList<>();
}
@Entity
@Getter
@Setter
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "product")
private List<MemberProduct> memberProduct=new ArrayList<>();
}
실무에서는 PK값을 @GeneratedValue로 의미없는 값을 쓰는게 더 유연하고 좋다.
- 비즈니스 로직이 변경되더라도 PK가 영향을 받지 않는다.
@OneToMany를 사용할 때, member, product를 객체 자체로 받는 게 아니라 List<Member>, List<Product>로 받고 있다.
- 일대다 관계에서 다수쪽의 엔티티(MemberProduct)를 관리하기 위해서다.
'인프런 김영한 강의 정리 > 자바 ORM 표준 JPA 프로그래밍 기본편' 카테고리의 다른 글
JPA 기본 | @MappedSuperclass (0) | 2024.07.10 |
---|---|
JPA 기본 | 상속관계 매핑 (0) | 2024.07.10 |
JPA 기본 | 양방향 연관관계와 연관관계의 주인 (0) | 2024.07.02 |
JPA 기본 | 연관관계 매핑 기초 (0) | 2024.06.13 |
H2 새로운 데이터베이스로 접속 오류 해결하기 (0) | 2024.06.10 |