Chapter 5: 연관관계 매핑 기초
1. 단방향 연관 관계
객체 연관관계와 테이블 연관관계의 가장 큰 차이
- 참조를 통한 연관관계는 언제나- 단방향이다- 객체간에 - 양방향으로 만들고 싶으면 반대쪽에도 필드를 추가해서 참조를 보관해야한다- 결국 연관관계를 하나 더 만들어야 한다 
 
- 이렇게 양쪽에서 - 서로 참조하는 것을- 양방향 연관관계라 한다- 하지만 정확히 이야기하면, 이것은 양방향 관계가 아니라 서로 다른 - 단방향 관계 2개다
- 반면, 테이블은 - 외래 키하나로 양방향으로 조인할 수 있다
 
 
객체 연관관계 vs 테이블 연관관계
- 객체 - 참조 (주소)로 연관관계를 맺는다
- 연관된 데이터를 - 참조를 사용해 조회한다- 연관관계를 참조를 사용해서 탐색하는 것을 - 객체 그래프 탐색이라 한다
 
- 참조를 사용하는 객체의 연관관계는 - 단방향이다- 객체를 양방향으로 참조하려면, 단방향 연관관계를 2개 만들어야 한다 
 
 
- 테이블 - 외래 키로 연관관계를 맺는다
- 연관된 데이터를 - JOIN을 통해 조회한다- 외래 키를 사용해서 연관관계를 탐색하는 것을 - JOIN이라 한다
 
- 외래 키를 사용하는 테이블의 연관관계는 - 양방향이다
 
@JoinColumn
@JoinColumn- 외래 키를 매핑할 때 사용한다 
- 속성속성기능기본값- name - 매핑할 외래 키 이름을 지정한다 - 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명 - referencedColumnName - 외래 키가 참조하는 대상 테이블의 컬럼명 - 차모하는 테이블의 기본 키 컬럼명 - foreignKey (DDL) - 외래 키 제약조건을 직접 지정할 수 있다 (이 속성은 테이블을 생성할 때만 사용!) - unique , nullable , insertable , updatable, columnDefinition, table - @Column 의 속성과 같다 
- @JoinColumn생략- 해당 어노테이션을 생략하면 외래 키를 찾을 때 - 기본 전략을 사용한다
- 기본 전략 - 필드명 + _ + 참조하는 테이블의 컬럼명 
 
 
@ManyToOne
@ManyToOne- 이름 그대로 다대일 (N:1) 관계라는 매핑 정보다 
- 연관관계를 매핑할 때 이렇게 다중성을 나타내는 어노테이션을 필수로 사용해야 한다 
- 속성속성기능기본값- optional - false로 설정하면 연관된 엔티티가 항상 있어야 한다 - true - fetch - global fetch 전략을 설정 - FetchType.EAGER - cascade - 영속성 전이 기능을 사용 - targetEntity - 연관된 엔티티의 타입 정보를 설정한다 (이 기능은 거의 사용하지 않는다! 컬렉션을 사용해도 제네릭으로 타입 정보를 알 수 있기 때문!) 
2. 연관관계 사용
2-1. 저장
- JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 - 영속 상태여야 한다
- JPA는 참조한 객체의 - 식별자를- 외래 키로 사용해서 적절한- 등록 쿼리를 생성한다
2-2. 조회
연관관계가 있는 엔티티를 조회하는 방법
- 객체 그래프 탐색 - 객체 연관관계를 사용한 조회 
 
- 객체지향 쿼리 사용 (JPQL) - 예를 들어 회원을 대상으로 조회하는데 팀1에 소속된 회원만 조회하려면, 회원과 연관된 팀 엔티티를 검색 조건으로 사용해야 한다 - SQL은 연관된 테이블을 JOIN 해서 검색조건을 사용하면 된다 
- JPQL도 - JOIN을 지원한다 (문법은 약간 다름!)
 
- JPQL은 객체(엔티티)를 대상으로 하고, SQL보다 간결하다 
 
2-3. 수정
- 수정은 em.update() 같은 method가 없다 
- 단순히 불러온 - 엔티티의 값만 변경해두면, 트랜잭션을- 커밋할 때- 플러시가 일어나면서- 변경 감지기능이 작동한다- 그리고 변경사항을 데이터베이스에 자동으로 반영한다 
 
- 연관관계를 수정할 때도 같은데, - 참조하는 대상만 변경하면 나머지는 JPA가 자동으로 처리한다
2-4. 연관관계 제거
: 연관관계를 null로 설정
2-5. 연관된 엔티티 삭제
- 연관된 엔티티를 삭제하려면 기존에 있던 - 연관관계를 먼저 제거하고- 삭제해야 한다- 그렇지 않으면 외래 키 제약조건으로 인해, DB에서 오류가 발생한다 
 
3. 양방향 연관관계
- 데이터베이스 테이블은 외래 키 하나로 양방향으로 조회할 수 있다 
- 객체는 연관관계가 맺어진 반대 방향에서도 접근할 수 있게, - 컬렉션과- @OneToMany매핑 정보, 그리고- mappedBy속성을 사용한다- ex) - 회원 → 팀 (Member.team) 
- 팀 → 회원 (Team.members) - 여기서 members는 list 
 
 
 
- mappedBy속성은 양방향 매핑일 때 사용하는데, 반대쪽 매핑의 필드 이름을 값으로 주면 된다
4. 연관관계의 주인
- mappedBy가 필요한 이유- 테이블은 - 외래 키하나로 두 테이블의 연관관계를 관리
- 엔티티를 단방향으로 매핑하면 참조를 하나만 사용하므로 이 참조로 외래키를 관리하면 됨 
- but, 엔티티를 - 양방향으로 매핑하면 양쪽에서- 서로를 참조하게 됨- 따라서 객체의 연관관계를 관리하는 포인트는 2곳으로 늘어난다 
- 객체의 참조는 둘인데, 외래 키는 하나다 
 
- 이러한 차이로 인해 JPA에서는 두 객체 연관 관계 중 하나를 정해서 테이블의 외래 키를 관리해야 하는데, 이것을 - 연관관계의 주인이라 한다
 
- 연관관계의 주인- 연관관계의 주인만이 DB 연관관계와 매핑되고, 외래키를 관리할 수 있다 - 주인이 아닌 쪽은 읽기만 할 수 있다 
 
- 주인은 - mappedBy속성을 사용하지 않는다- 주인이 아니면 - mappedBy속성을 사용해서- 속성의 값으로- 연관관계의 주인을- 지정해야 한다
 
- 연관관계의 주인을 정한다는 것은 - 외래 키 관리자를 선택하는 것이다- 연관관계의 주인은 - 외래 키가 있는 곳을 뜻한다
- 연관관계의 주인만 데이터베이스 연관관계와 매핑되고, 외래 키를 관리할 수 있다 
- 주인이 아닌 반대편은 읽기만 가능하고, 외래키를 변경하지는 못한다 
 
 
5. 양방향 연관관계 저장
- 양방향 연관관계는 연관관계의 주인이 외래 키를 관리한다 - 따라서 주인이 아닌 방향은 값을 설정하지 않아도 데이터베이스에 외래 키 값이 정상 입력된다 
 
6. 양방향 연관관계 주의점
- 양방향 연관관계를 설정하고 가장 흔히 하는 실수는, 연관관계의 주인에는 값을 입력하지 않고, 주인이 아닌 곳에만 값을 입력하는 것이다 - 연관관계의 주인만이- 외래 키의- 값을- 변경할 수 있다는 것을 기억하자!
 
6-1. 순수한 객체까지 고려한 양방향 연관관계
- 연관관계의 주인에만 값을 저장하고 주인이 아닌 곳에는 값을 저장하지 않는 것이 아니라, - 객체 관점에서- 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다- 양쪽 방향 모두 값을 입력하지 않으면 JPA를 사용하지 않는 순수한 객체 상태에서 심각한 문제가 발생할 수 있다 
 
- 결론 - 객체의 양방향 연관관계는 - 양쪽 모두 관계를 맺어주자!
 
6-2. 연관관계 편의 메소드
- 연관관계 설정 method를 각각 호출하다 보면 실수로 둘 중 하나만 호출해서 양방향이 깨질 수 있다 - 양방향 관계에서 두 코드는 하나인 것처럼 사용하는 것이 안전한다 
 
- 한 번에 양방향 관게를 설정하는 method를 - 연관관계 편의 method라 한다
7. 정리
- 단방향 매핑과 비교해서 양방향 매핑은 복잡하다 - 연관관계의 주인도 정해야 하고, 두 개의 단방향 연관관계를 양방향으로 만들기 위해 로직도 잘 관리해야 한다 
- 중요한 사실은 연관관계가 하나인 단방향 매핑은 언제나 연관관계의 주인이라는 점이다! - 양방향은 여기에 주인이 아닌 연관관계를 하나 추가했을 뿐이다 
 
- 결국 단방향과 비교해서 양방향의 장점은 - 반대 방향으로 객체 그래프 탐색 기능이 추가된 것뿐이다
 
- 단방향 매핑만으로 테이블과 객체의 연관관계 매핑은 이미 완료되었다 
- 단방향을 양방향으로 만들려면 반대방향으로 - 객체 그래프 탐색기능이 추가된다
- 양방향 연관관계를 매핑하려면 객체에서 - 양쪽 방향을- 모두 관리해야 한다
- 양방향 매핑은 복잡하다 - 우선 단방향 매핑을 사용하고, 반대 방향으로 객체 그래프 탐색 기능(JPQL 쿼리 탐색 포함)이 필요할 때 양방향을 사용하도록 코드를 추가해도 된다 
 
Last updated
Was this helpful?