본 글은 인프런의 김영한님 강의 자바 ORM 표준 JPA 프로그래밍 - 기본편
을 수강하며 기록한 필기 내용을 정리한 글입니다.
-> 인프런
-> 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의
- USER : ORDERS = 1 : N
- 한 명의 유저가 여러 주문을 할 수 있다. (이것도 사고 저것도 사고..)
- 하나의 주문을 여러 유저가 할 수는 없다. (여러명이 모여 하나의 주문을 할 수 없다.)
- ORDERS : COOKIE = N : M 관계이며, 중간에 'ORDER_COOKIE'를 두어 1 : N, N : 1 관계로 구현한다.
- 하나의 주문에 여러 쿠키들이 주문될 수 있다.
- 하나의 쿠키가 여러 주문에 의해 팔릴 수 있다.
- ORDERS : DELIVERY = 1 : 1
- 하나의 주문이 여러번의 배달로 처리될 수 없다.
- 하나의 배달은 하나의 주문만 처리한다.
- 테이블 : DB의 테이블
- 엔티티 : JPA를 활용하여 DB 테이블과 매핑되는 클래스
- 컬럼 : DB 테이블의 컬럼
- 필드 : JPA를 활용하여 DB의 컬럼과 매핑되는 멤버변수
(해당 용어들은 각각 다른 의미도 갖지만, 편의상 이렇게 설정한다.)
- 다대일 매핑
- 일대다 매핑
- 일대일 매핑
- 다대다 매핑
연관관계 주인 필드?
-> DB의 FK 컬럼과 매핑된 필드
@ManyToOne
@JoinColumn(name = "USER_ID")
private User user; // 연관관계 주인 필드
@OneToMany(mappedBy = "user")
private List<Order> orders = new ArrayList<>();
User user = entityManager.find(User.class, 1L);
List<Order> orders= user.getOrders();
for (Order o : orders){
System.out.println("Order id : " + o.getId());
}
@OneToMany
@JoinColumn(name = "USER_ID")
private List<Order> orders = new ArrayList<>();
<일대다 매핑이 권장되지 않는 이유>
- 일대다 매핑은 연관관계 주인 필드를 '일' 쪽의 엔티티가 갖게 된다.
- 하지만 DB 테이블 구조 상, '일' 쪽이 아닌 '다' 쪽이 FK 컬럼을 갖는다.
- 따라서 '일' 쪽에서 persist() 하면, '다' 쪽의 테이블로 쿼리가 나간다.
- 예제에서 User 객체를 DB에 저장하려고 persist() 했는데, ORDER 테이블로 쿼리가 나간다.- 즉, 개발 과정에서 예상치 못한 쿼리가 다른 테이블로 전달 될 수 있다.
Order order = new Order();
em.persist(order)
User user = new User();
user.setName("user1");
user.getOrders().add(order);
em.persist(user);
⇒ ⭐아무리 다대일 매핑이 안맞는 상황이라도, 일대다 매핑보다는 차라리 다대일 양방향 매핑을 사용하자.⭐
// Order class
@OneToOne
@JoinColumn(name = "DELIVERY_ID")
private Delivery delivery;
// Delivery class
@OneToOne(mappedBy = "delivery")
private Order order;
- 지금은 일대일이지만, 나중에 한쪽이 다수가 될 가능성이 있을 경우, 다수가 될 가능성이 높은 쪽에 FK를 두는게 더 이득이다.
- 만약 나중에 하나의 배달로 여러 건의 주문을 해결할 수 있도록 로직이 변경될 경우에 대해 생각해 본다.
- 즉, Order : Delivery = N : 1 로 로직이 변경된다.
- 처음부터 ORDER 테이블에 FK 컬럼을 뒀다면, 그냥 해당 FK에 유니크 제약조건만 없애면 된다.
- 하지만 DELIVERY 테이블에 FK 컬럼을 뒀다면, 해당 FK 컬럼을 지우고 ORDER 테이블에 FK 컬럼을 추가해야된다.
- 만약 그런 기획이 보이지 않으면, JPA 개발 과정에서 여러번 쓰이는 쪽에 FK를 두는게 좋을 수 있다.
- 예제를 두고 보면, 사실 배달 정보보다는 주문 정보가 더 많이 쓰일 것이다.
- 만약 주문 데이터들 중, 배달 상태에 따라 뭔가 작업을 해야하는 경우,
- ORDER 테이블에 FK가 있으면, JPA에서 Order 데이터 find() 할 때 어차피 Delivery 데이터도 딸려오게 된다.
-> 연관관계 주인 필드가 있으므로- 하지만 DELIVERY 테이블에 FK가 있으면 많진 않지만 추가해야 할게 생긴다.
- 큰 차이는 아닐 수 있지만, 쪼끔 더 나아질 수 있다.
- 또한, 덜 쓰이는 쪽에 FK를 두면, 지연 로딩 기능이 제한될 수 있다고 한다.
// Order class
@ManyToMany
@JoinTable(name = "ORDER_COOKIE",
joinColumns = @JoinColumn(name = "ORDER_ID"),
inverseJoinColumns = @JoinColumn(name = "COOKIE_ID"))
private List<Cookie> cookies = new ArrayList<>();
@ManyToMany(mappedBy = "cookies")
private List<Order> orders = new ArrayList<>();
<다대다 매핑 쓰면 안되는 이유>
- 보통은 연결 테이블이 단순히 연결만 시키는 용도로 쓰이지 않는다.
- 무조건 주문 시간, 수량 등 추가되는 데이터가 있다.
- 하지만 다대다 매핑 방식을 쓸 경우, 연결 테이블을 JPA에서 관리하지 않게 된다.
- JPA에서 그냥 자동으로 연결 테이블을 만들어 버리기 때문이다.
- 연결 테이블과 매핑된 클래스가 없다.
<다대다 대체 방법>
- 연결 테이블을 JPA에서 하나의 엔티티로 만든다.
- 예제에서는 'OrderCookie' 클래스를 만들고, DB의 ORDER_COOKIE 테이블과 매핑시킨다.
- @OneToMany - @ManyToOne 관계를 양쪽으로 두개 만든다.
- Order : OrderCookie = 1 : N
- Cookie : OrderCookie = 1 : N
- 이 때, 연결 테이블이어도 본인만의 PK를 갖고 있는게 좋다.
- ORDER_ID, COOKIE_ID 가 FK이자 PK 인 것으로 설정하는 것 보다, ORDER_COOKIE_ID 컬럼을 둬서 PK로 만들고, FK는 FK로만 두는게 좋다.
- FK이자 PK로 설정하게 되면, 미래에 예상치 못한 제약이 생길 수 있다. PK는 최대한 제약 없이, 다른 데이터들과 아무 상관 없는 값이어야 한다.