실전 예제 - 1. 요구사항 분석과 기본 매핑
도메인 모델 분석

회원-주문: 일대다
주문-상품: 다대다 => 주문-주문상품: 일대다 / 주문상품-상품: 다대일
테이블 설계 (in Database)

주문(ORDERS) 테이블은 주문한 회원의 기본키(MEMBER_ID)를 외래키로 가진다.
외래키를 통해 회원(MEMBER) 테이블에서 주문한 회원의 정보를 조회할 수 있다.
회원은 여러 번 주문할 수 있기 때문에 회원 테이블에 외래키를 두면, 주문을 할 때마다 동적으로 외래키를 추가해야 한다.
그러나 주문은 생성되는 동시에 1명의 주문 회원만을 가지기 때문에 연관관계에서 '다'에 해당하는 쪽에 외래키를 두는 것이 합리적이다.
같은 이유로 주문상품(ORDER_ITEM) 테이블에 주문과 상품(ITEM)의 외래키가 존재한다.
엔티티 설계와 매핑 (in java)

테이블의 외래키를 객체에 그대로 가져왔다.
외래키만 가지고 객체를 참조할 수 없다. 따라서, 객체 그래프 탐색이 불가능하다.
* 객체와 테이블이 연관관계를 맺는 패러다임의 차이: 참조(reference)-외래키(join)
=> JPA를 통해 객체를 외래키와 매핑하는 방법
Long memberId -> Member member
05. 연관관계 매핑 기초
연관관계에서 단방향, 양방향의 기준
: 객체 그래프에서 단방향 참조 or 양방향 참조.
테이블에서는 외래키 하나로 테이블을 join하면 되기 때문에 방향 개념이 없다.
단방향 연관관계
객체 지향 모델링

목표: 객체의 참조 Team team 와 테이블의 외래키 Team_ID 매핑
단방향이기 때문에 연관관계 주인에만 연관관계를 설정해주면 된다.
엔티티 연관관계 매핑 정보
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
// @Column(name = "TEAM_ID")
// private Long teamId;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
…
Member - Team
다대일 관계
@ManyToOne
해당 객체와 테이블에서 join할 컬럼: TEAM_ID
@JoinColumn(name = "TEAM_ID")
연관관계 저장
Team team = new Team();
//team setting
em.persist(team);
Member member = new Member();
//member setting
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member)
양방향 연관관계

객체: 단방향 2개. 양쪽 모두에 참조가 있다.
테이블: 방향이라는 개념이 없음. 외래키 하나로 양쪽 join
연관관계 주인
연관관계의 주인이 외래키를 관리(등록, 수정)
주인이 아닌 쪽은 읽기만 가능
주인은 mappedBy 속성을 사용하면 안된다. 주인이 아니면 mappedBy 속성으로 주인을 지정한다.
* JPA 양방향 연관관계 매핑 * 단방향은 주인만 명시
주인: @ManyToOne @JoinColumn("외래키 컬럼명")
거울: @OneToMany(mappedBy = "연관관계 주인 필드명")
연관관계 주인 선정 기준: 외래키가 있는 테이블과 매핑된 엔티티
비즈니스 로직을 기준으로 선택하는 것은 안된다. (ex. 회원이 주문을 생성하니까 회원이 주인? X)
외래키가 없는 테이블에서 필드를 수정했는데 외래키가 있는 다른 테이블에 update 쿼리가 나가는 상황... (OneToX)
양방향 매핑시 가장 많이 하는 실수
> 연관관계의 주인에 값을 입력하지 않아서 외래키 값이 null
양방향 매핑시 연관관계의 주인에 값을 입력해야 한다.
단방향 매핑만으로도 이미 연관관계 매핑은 완료
즉, 양방향 매핑에서 연관관계의 주인에만 값을 세팅해도 DB에서 조회한 엔티티는 Join을 통해 양방향 탐색이 가능하다. 그러나 처음 생성된 엔티티가 영속성 컨텍스트(1차 캐시)에 있을 때는 "순수한 객체 상태"로 직접 값을 세팅하지 않으면 null 값을 참조할 수 있기 때문에 양쪽에 값을 세팅하는 것이 바람직하다.
=> 둘 중 한 곳에 연관관계 편의 메서드 생성
>양방향 매핑시에 무한 루프를 조심하자
롬복의 toString(모든 필드의 toString 호출) 사용하지 않거나 해당 필드 빼고 쓰기
JSON 생성 라이브러리: 컨트롤러에서 엔티티를 직접 반환하지 말기
<양방향 매핑 정리>
1. 우선 단방향 매핑만으로 설계 완료하기
2. 역방향 조회가 필요할 때(ex.JPQL) 추가 (테이블에는 영향 없음)
개발상 편의 + 비지니스상
06. 다양한 연관관계 매핑
연관관계 매핑시 고려사항
1. 다중성: DB 매핑 관점에서의 다중성.
2. 단방향, 양방향: 객체의 레퍼런스
3. 연관관계 주인: 테이블의 외래키와 매핑하여 관리. 양방향에서 거울은 조회만
1. 다중성
* 테이블의 외래키는 무조건 N인 쪽에 존재 *
다대일 [N:1]
대부분의 연관관계
테이블 설계와 동일하게 N인 쪽이 주인
주인: 엔티티 객체, 거울: 엔티티 컬렉션
일대다 [1:N]
테이블 설계와 다르게 1인쪽이 주인
주인: 엔티티 컬렉션, 거울: 엔티티 객체
엔티티가 관리하는 외래 키가 다른 테이블에 있다.
연관관계 관리를 위해 추가로 UPDATE SQL 실행해야 한다.
유지보수 측면에서 권장하지 않는 방법. 코드만 보고서는 다른 테이블까지 미치는 결과를 이해하기 어렵다.
비지니스적으로 N->1의 참조가 필요없더라도 테이블의 설계에 맞추는 것이 유지보수에 좋다.
@JoinColumn을 꼭 사용해야 한다. (Default: @JoinTable 중간에 테이블을 하나 추가함)
*일대다 양방향
이런 매핑은 공식적으로 존재 X
거울에 @JoinColumn(insertable=false, updatable=false) 추가하여 연관관계 주인처럼 매핑을 걸지만 읽기 전용으로 만듦
다대일 양방향을 사용하자