연관관계 매핑 기초
1. 목표
- 객체와 테이블 연관관계의 차이를 이해
- 객체의 참조와 테이블의 외래 키를 매핑
2. 연관관계가 필요한 이유
객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.
-조영호(객체지향의 사실과 오해)-
- 예를 들어 member와 team이 있다고 가정
- 하나의 member는 하나의 team에 소속될 수 있고 하나의 team에는 여러 member가 소속될 수 있다.(다대일 관계)
객체를 테이블에 맞추는 모델링
- 참조 대신 외래 키 식별자를 직접 다룸
- 참조객체를 찾기 위해서는 member객체를 찾고 team의 식별자로 다시 조회 해야 한다.(즉 select문이 2번 발생)
- 객체를 테이블에 맞추어 모델링하면 협력관계를 만들수 없음.
- 테이블은 외래 키로 조인을 사용하여 연관된 테이블을 찾음.
- 객체는 참조를 사용하여 연관된 객체를 찾음.
3. 단방향 연관관계(객체 지향 모델링)
- 객체지향 모델링을 사용하여 객체의 참조를 테이블의 외래키에 매핑하는 방법
- 객체의 참조 위에 @JoinColumn(매핑할 외래 키) 어노테이션 추가
- member는 team_id 외래키를 가지지 않고, team 참조 객체를 가짐
- @JoinColumn을 사용하면 JPA가 team 참조 객체를 DB MEMBER 테이블의 TEAM_ID(외래키)와 매핑
4. 양방향 연관관계와 연관관계의 주인
- 객체는 연관관계를 참조로 나타낸다.
- DB에서 테이블의 연관관계는 FK(외래키)로 나타낸다.
객체와 테이블의 연관관계 차이점
- 테이블끼리는 FK, PK로 테이블을 Join하여 서로의 정보를 알 수있다.
- 객체는 해당 객체를 참조하고 있어야 정보를 알수 있다.
- 즉, 테이블에서는 Fk(외래키) 하나만 두면 양방향 매핑이 연관관계가 형성된다.
- 객체는 해당 객체만 참조 객체를 가지고 있으면 단방향 매핑만 형성된다.
- 따라서 양방향 매핑을 위해서는 참조 객체의 클래스도 해당 객체를 참조하고 있어야 한다.
정리
- 양방향 연관관계를 맺기 위해서는
- 객체 연관관계 = 2개 필요
- 회원 -> 팀 연관관계 1개(단방향)
- 팀 -> 회원 연관관계 1개(단방향)
- 테이블 연관관계 = 1개 필요
- 회원 <-> 팀의 연관관계 1개(양방향)
- 객체 연관관계 = 2개 필요
- 객체의 양방향 관계는 양방향 관계가 아니라 서로 다른 단방향 연관관계 2개
- 테이블은 외래키 하나로 두 테이블의 양방향 연관관계 관리
@ManyToOne
- 다대일 관계에서
다
쪽에 해당
@OneToMany
- 다대일 관계에서
일
쪽에 해당
참고
FK(외래키)를 어느 쪽에 둘지 결정하기
M:1의 관계에서는 M쪽에 FK를 두어야 한다.
1쪽에서 M개의 데이터를 수용할 수는 없으므로...
외래키가 있는 테이블을 자식 테이블, 없는 테이블을 부모 테이블이라고 한다.
- 따라서 현재 테이블에서는 FK가 MEMBER테이블에 존재한다.
딜레마
- 단방향 매핑에서 @JoinColumn을 통하여 객체의 참조와 외래키를 매핑하였다.
- 양방향에서는 양쪽다 객체의 참조를 가지고 있으므로, 둘중 하나로 외래키를 관리해야 한다.
- 즉, 연관관계의 주인을 정해야 한다.
양방향 매핑 규칙
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만 외래 키를 관리(등록, 수정)
- 주인이 아닌 쪽은 read(조회)만 가능
- 주인은 mappedBy 속성 사용x
- 주인이 아니면 mappedBy 속성으로 주인을 지정해 주어야 함
주인 정하기
- 외래 키가 있는 곳을 주인으로 정하는 것이 정배이다.
- 해당 예제에서 외래키는
M(다)
쪽인 MEMBER테이블이 TEAM_ID(FK)를 가지고 있으므로, Member.team이 연관관계의 주인이 된다.
외래키가 있는곳을 주인으로 하는 이유
물론, 외래키가 없는 부모 테이블에 매핑되는 객체를 주인으로 정해도 된다.
해당 예제에서 그렇게 적용한다고 쳐보자. 그러면 Team.members 가 연관관계의 주인이 된다.
그러면 Team.members에서 MEMBER테이블의 외래키를 관리하게 된다.
즉 Team에서 멤버를 등록, 수정하게 되면, MEMBER테이블로 등록, 수정 쿼리가 날아간다.
실무에서는 테이블이 수백 수천개고 연관관계 또한 매우 복잡하게 얽혀있는데,
Team을 변경했는데 MEMBER테이블로 업데이트 쿼리가 가면 개발자 입장에서 매우 헷갈리게 된다. 나중에 배울 내용이지만 성능 이슈도 있다고 한다.
따라서 연관관계의 주인은 외래 키가 있는 곳으로 정해야한다.
양방향 매핑시 많이하는 실수
- 연관관계의 주인에 값을 입력해야 함
- 즉 member.setTeam을 하면 Member.team, Team.members에 값이 들어감.
- team.getMembers().add()만 하면 DB에 값이 들어가지 않음.
- team에서는 조회만 가능
양방향 연관관계 주의
- 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정
- 연관관계의 주인에만 값을 입력하게 되면, flush()전에는 주인에서만 참조값을 사용할 수 있음.
- JPA없이 동작하는 TEST케이스 작성시에도 마찬가지
- 연관관계 편의 메서드를 생성하자
- 편의 메서드를 만들어서 양쪽에 값 설정
- 양방향 매핑시 무한루프를 조심해야 함
- 예) toString, lombok, JSON 생성 라이브러리
- lombok의 toString을 사용하면 무한루프에 빠질 수있음(사용x)
- JSON데이터를 반환시 엔티티를 그대로 사용하게 되면 무한루프
- JSON데이터를 반환시 엔티티를 그대로 api로 반환하다가 엔티티 속성이 변경되면, 반환되는 api 스펙도 같이 변경됨.
- 엔티티는 DTO(Data Transfer Object)로 변경해서 반환.
- 예) toString, lombok, JSON 생성 라이브러리
5. 정리
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
- JPQL에서 역방향으로 탐색할 일이 많음
- 단방향 매핑로 먼저 설계를 완료하고 양방향은 필요할때 추가(테이블에 영향을 주지 않음)
'JPA > JPA Basic' 카테고리의 다른 글
[JPA] 5. 다양한 연관관계 매핑 (0) | 2023.01.29 |
---|---|
[JPA] 참고. @JoinColumn (0) | 2023.01.29 |
[JPA] 참고. Entity, DAO, DTO, VO (0) | 2023.01.27 |
[JPA] 에러. SQL Error 23505 (0) | 2023.01.27 |
[JPA] 3. 엔티티 매핑 (0) | 2023.01.26 |
[JPA] 2. 영속성관리 (0) | 2023.01.25 |
[JPA] 1. 환경세팅 (0) | 2023.01.25 |
[JPA] 0. JPA 소개 (0) | 2023.01.25 |