[본 포스팅은 자바 ORM 표준 JPA 프로그래밍 기본 편을 기반으로 작성하였습니다.]
연관관계 매핑 시 고려사항 3가지로는 다음과 같다.
- 다중성
- 단방향, 양방향
- 연관관계의 주인
다중성
- 다대일(N:1): @ManyToOne
- 일대다(1:N): @OneToMany
- 일대일(1:1): @OneToOne
- 다대다(N:M): @ManyToMany
단방향, 양방향
- 테이블
- 외래 키 하나로 양쪽 조인 가능
- 사실 방향이라는 개념이 없음.
- 객체
- 참조용 필드가 있는 쪽으로만 참조 가능
- 한쪽만 참조하면 단방향
- 양쪽이 서로 참조하면 양방향
연관관계 주인
- 테이블은 외래 키 하나로 두 테이블이 연관관계를 맺음
- 객체 양방향 관계는 A->B, B->A처럼 참조가 2군데
- 연관관계의 주인: 외래 키를 관리하는 참조
- 주인의 반대편: 외래 키에 영향을 주지 않고 단순 조회만 가능
다대일(N:1)
@ManyToOne
[단방향]
- 가장 많이 사용하는 연관관계
[사용방법]
만약 다음과 같이 멤버(1)와 주문 내역(N)의 상황이 있을 때 테이블의 구성은
주문 내역에서 멤버 ID를 FK로 받아온다.
(DELEVERY_ID는 생략)
- Member.class(1)
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
private String city;
private String street;
private String zipcode;
- Order.class(N)
@Id
@GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
코드를 보면 N인 쪽에서
@ManyToOne
@JoinColumn(name = "Member 칼럼(pk) 이름")
을 통해서 서로 N:1의 관계를 맺어줬다.
[양방향]
- 외래 키가 있는 쪽이 연관관계 주인
- 양쪽을 서로 참조하도록 개발
[사용방법]
위와 같이 주문 내역과 주문 물품의 연관관계가 N:1인데 이 둘을 양방향으로 연관관계를 구성하게 되면 위와 같다.
그리고 N 쪽이 연관관계의 주인이 된다. 따라서 ORDER_ITEM에서는 읽기만 할 수 있다.
- Order.class(1)
@Id
@GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
@OneToMany(mappedBy = "N 쪽에서 참조되는 변수명")을 통해 List로 가져올 수 있다.
하지만 언제나 mappedBy 붙은 쪽은 주인이 아니기 때문에 ReadOnly로 작동해야 된다.
그렇지 않고 저장, 수정, 삭제와 같은 작업이 일어났을 때 딜레마가 생긴다.
- OrderItem.class(N)
@Id
@GeneratedValue
@Column(name = "ORDER_ITEM_ID")
private Long id;
@ManyToOne
@JoinColumn(name = "ORDER_ID")
private Order order;
private int orderPrice;
private int count;
N 쪽은 주인이 된다. 따라서 @ManyToOne과 @JoinColumn(name = "칼럼 이름") 애노테이션을 붙여주면 된다.
일대다(1:N)
@OneToMany
※권장하지 않고 양방향은 공식적으로 지원하지 않는다.
[단방향]
- 일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인
- 테이블 일대다 관계는 항상 다(N) 쪽에 외래 키가 있음
- @JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함(중간에 테이블을 하나 추가함)
일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자
[사용방법]
- Team.class(1)
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();
1(주인) 쪽에
@OneToMany
@JoinColumn(name = "칼럼 이름")
을 넣어주기만 하면 끝이다.
- Member.class(N)
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
N 쪽에는 아무것도 수정할 것이 없다.
일대일(1:1)
@OneToOne
[단방향]
위와 같이 멤버는 락커를 가질 수 있다고 가정하면
- Member.class(1)
@Id
@GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
@OneToOne
@JoinColumn(name = "칼럼 이름(안 넣어주면 지저분한 칼럼 이름 생성)")
이렇게 넣어주면 되고
- Locker.class(1)
@Id
@GeneratedValue
private Long id;
private String name;
Locker에서는 아무것도 안 해주면 된다.
그러면 1대 1 단방향이 완성된다.
[양방향]
1대 1 양방향 같은 경우 단방향에서 Locker 쪽에 다음과 같이 mappedBy 넣어주면 된다.
- Locker.class(1)
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne(mappedBy = "locker")
private Member member;
※참고로 다대다의 경우에 실무에서 사용하면 안 된다.
'JAVA > JPA' 카테고리의 다른 글
[JPA] 스프링 데이터 JPA 페이징과 정렬 (feat. DTO를 사용해야 하는 이유) (0) | 2023.01.02 |
---|---|
[JPA/JPQL] 페치 조인(fetch join)이란? 특징과 한계 - 엔티티 페치 조인, 컬렉션 페치 조인, DISTINCT로 중복 제거 (0) | 2022.12.29 |
[JPA] @MappedSuperclass로 공통 칼럼 상속받기 (0) | 2022.12.23 |
[JPA] 상속관계 전략과 매핑 (코드 예시) (0) | 2022.12.23 |
[JPA] 엔티티 매핑(@어노테이션)과 스키마 자동 생성 속성 (0) | 2022.12.20 |