연관관계 시 고려사항 3가지
- 다중성
- 단방향, 양방향
- 연관관계의 주인
다중성
설계시 DB기준으로 어떻게 할지 생각해보고, 외래키를 두자, 외래키는 N 쪽에 두자!
햇갈린다면, 생각하고있던 서로의 위치를 바꿔도 적절한지 확인(다대일 -> 일대다)
- 다대일 @ManyToOne
- 일대다 @OneToMany
- 일대일 @OneToOne
- 다대다 @ManyToMany
- 실무에서 안씀
단방향, 양방향
- 테이블
- 외래 키 하나로 양쪽조인 가능
- 방향이라는 개념없음
- 객체
- 참조용 필드가 있는 쪽만 참조가능
- 한쪽만 참조하면 단방향
- 양쪽이 서로 참조하면 양방향 -> 사실은 단방향이 두개임(주인, 하인 나뉨)
연관관계의 주인
- 테이블은 외래 키 하나로 두 테이블이 연관관계 맺음
- 객체 양방향 관계는 참조가 2군대 있 둘중 테이블의 외래 키를 관리할 곳을 지정해야함(주인, 외래키 보유)
- 하인(주인의 반대편): 외래키에 영향주지않음, 단순 조회
다대일
- 외래키 N:1 중 N쪽이 가짐
- N쪽은 연관관계 연결
- 보편적인 방법. 가장 추천하는 방법
일대다
DB에서는 외래키 N:1 중 N쪽 항상 가짐
하지만 객체 연관관계의 1:N 관계 중 1이 주인임
객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하 는 특이한 구조
N쪽은 연관관계 연결
@JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함(중간에 테이블을 하나 추가함)
예시
- 단점은 Team 내용을 변경했지만, 실제 DB에는 Team에서의 내용은 없고, Member의 FK을 업데이트해야하므로.. 무조건 쿼리 한번더 나감
- 개발자입장에서 Team만 건드렸는데, Member Update 쿼리가 나가서 이상하고, 운영상에도 많은 연관관계가있을텐데…관리어려움
1 | public class Team { |
- 다대일 양방향으로 만드는게 DB 설계와 비슷하고, 장점많음
일대다 단방향 정리
- 일대다 단방향 매핑의 단점
- 엔티티가 관리하는 외래 키가 다른 테이블에 있음
- 연관관계 관리를 위해 추가로 UPDATE SQL 실행
- 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자
일대다 양방향
- 읽기 전용 만들기 전략
- 이런 매핑은 공식적으로 존재X
- @JoinColumn(insertable=false, updatable=false)
- 읽기 전용 필드를 사용해서 양방향 처럼 사용하는 방법
- 다대일 양방향을 사용하자
1 | public class Member { |
일대일 관계
- 일대일 관계는 그 반대도 일대일
- 주 테이블이나 대상 테이블 중에 외래 키 선택 가능
- 외래 키에 데이터베이스 유니크(UNI) 제약조건 추가
예시: 단방향 일대일
- Member는 반드시 하나의 Locker를 갖는다.
- 다대일(@ManyToOne) 단방향 매핑과 유사
1 |
|
1 |
|
예시: 양방향 일대일
- OneToMany와 유사하게 걸어주면됨
1 |
|
1 |
|
그렇다면 외래키를 Member(주 테이블) 또는 Locker(대상테이블)중 어떤 것이 주인(외래키 가짐)되어야할까? -> 때에 따라다르다
- DB관점에서는
외래키는 Locker가 갖게하는게 합리적
추후 Member가 Locker를 여러개 들고있을수있다는 조건이 추가될 확률이 높기 때문 - 개발자 입장에서는
외래키는 Member가 갖게하는게 합리적
Member는 여러곳에서 쓰이고, 이때 Locker 외래키를 가지고있다면, Locker관련 로직에서 따로 DB에 쿼리 필요없이 바로 로직을 실행할수있기때문
일대일 정리
- 주 테이블(자주 사용 테이블)에 외래키
- 주 객체가 대상 객체의 참조를 가지는 것 처럼 주 테이블에 외래 키를 두고 대상 테이블을 찾음
- 객체지향 개발자 선호
- JPA 매핑 편리
- 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
- 단점: 값이 없으면 외래 키에 null 허용
- 대상 테이블에 외래키
- 대상 테이블에 외래 키가 존재
- 전통적인 데이터베이스 개발자 선호
- 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
- 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨(프록시는 뒤에서 설명), => JPA는 프록시기능쓰면 어차피 객체를 만들때, 연관관계에 대해 조회하게되어잇기때문 => 쿼리가 다른 테이블로나가야함
- 무조건 양방향으로 만들어야함
다대다(N:M)
실무에서 쓰지말자
- 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없음
- 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야함
- 중간 테이블로 해결
객체는 컬렉션을 사용해서 객체 2개로 다대다 관계 가능
사용 방법
- @ManyToMany
- @JoinTable로 연결 테이블 지정
- 다대다 매핑: 단방향, 양방향 가능
단점
- 연결 테이블이 단순히 연결만 하고 끝나지 않음
- 중간 테이블에 다른 정보 포함할수없음
- 주문시간, 수량 같은 데이터가 들어올 수 있음.
하지만, 중간 테이블에는 정보 넣을수없다.
다대다 한계 극복 => 연결 테이블용 엔티티 추가
- 연결 테이블용 엔티티 추가(연결 테이블을 엔티티로 승격)
- @ManyToMany -> @OneToMany, @ManyToOne
- Member
1 |
|
- Product
1 |
|
- MemberProduct
1 |
|
TIP: PK값 정의
애플리케이션은 계속 확장해야하므로! 확장성 중요
PK값은 보통 두개의 의미있는 필드를 만드는 것이 아닌, 유일한 하나의 값을 추천함
왜냐하면, 의미가 들어가는 순간 중복되면서, 아예 안들어갈수도있기때문
또한 두 개의 필드가 PK라면 유연성도 떨어짐. 확장성 감소
속성 정리
@JoinColumn
@ManyToOne
- 무조건 연관관계 주인이 되어야만한다.
- mappedby 속성 없음