실제로 DB에서 클래스의 상속이라는 관계는 존재하지 않음
다만 테이블 전략이라는 것을 통해 매핑할 수 있음
이런 연관 관계를 가지고 있을 때 Order를 조회한다고 하면
만약 EAGER(즉시로딩) 설정 시
이게 만약 100번 이루어지면 member도 100번 조회되야 하기에 쿼리가 100번 날라감
첫 쿼리가 가져온 결과가 100개라면 n + 1을 100으로 치환 --> 100 + 1
Order를 조회하는 시점에 반드시 Member가 존재해야하기 때문에 Order 조회 결과가 100개라면 이에 따라 Member 조회 쿼리가 100개 날라감
즉시 로딩은 JPA 성능 저하의 매우 크리티컬한 요소이기 때문에 절대 사용하면 안됨
Member member = new Member();
System.out.println(member.getOrders().getClass());
em.persist(team);
System.out.println(member.getOrders().getClass());
//출력 결과
class java.util.ArrayList
class org.hibernate.collection.internal.PersistentBag
스프링부트는 기본적으로 SpringPhysicalNamingStrategy라는 전략을 사용
DBA는 보통 언더스코어를 사용하기에 편리하게 맞출 수 있음
camelCase -> under_score
. -> _
UpperCase -> lower_case
스프링부트의 기본 전략
spring.jpa.hibernate.naming.implicit-strategy:
org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
spring.jpa.hibernate.naming.physical-strategy:
org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
@Id
@GeneratedValue
@Column(name = "order_id")
private String id;
@ManyToOne(fetch = LAZY)
@JoinColumn (name = "member_id")
private Member member;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(fetch = LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "delivery")
private Delivery delivery;
cascade 옵션은 단순히 생각하면, 그냥 persist() 호출을 줄여줄 수 있기 때문에, 유용해 보이지만, 반대로 생각하면, Order 엔티티를 저장할 때, 연관된 어떤 엔티티들이 함께 저장될까? 를 계속 코드를 보며 추적해야 합니다.
따라서 질문하신 것 처럼 어디까지는 cascade로 함께 저장하고, 어디까지는 함께 저장하면 안될까? 하는 명확한 기준이 필요합니다.
그래서 이런 기준을 잡기 애매한 경우에는 사실 사용하지 않는 것이 좋습니다.
통상적으로 권장하는 cascade 범위는, 완전히 개인 소유하는 엔티티일 때, 예를 들어서 게시판과 첨부파일이 있을 때 첨부파일은 게시판 엔티티만 참조하므로, 개인 소유 입니다. 이런 경우에는 사용해도 됩니다. 그럼 반대로 개인 소유하지 않는 엔티티는 무엇일까요? 예를 들어서, 회원, 상품 등등이 있습니다.
이 예제에서 Order -> OrderItem을 개인소유 하기 때문에 cascade를 사용했습니다. 그런데 Order 입장에서 Delivery는 좀 애매합니다. 여기서는 프로젝트 규모가 작기 때문에 매우 단순하게 표현했지만, 실무에서 프로젝트 규모가 커지면, Delivery로 여러곳에서 참조될 수 있습니다. 그러면 사용하면 안됩니다.
추가로 도메인 주도 설계(DDD)의 Aggregate Root 개념을 이해하고, 프로젝트 적용하면 여기에 맞추어 cascade 옵션을 더 잘 활용할 수 있습니다.
제어가 더 많은 쪽
완전 개인 소유인 경우에 사용할 수 있다.
DDD의 Aggregate Root와 어울린다.
애매하면 사용하지 않는다.
원래 양방향 추가의 경우
Member member = new Member();
Order order = new Order();
member.getOrders().add(order);
order.setMember(member);
이렇게 해줘야하는데 이런 걸 원자적인 한 코드로 묶어주는 메소드가 연관 관계 메소드
이런 메소드는 컨트롤러가 가지고 있는게 좋음
// 연관 관계 메소드
public void addChildCategory(Category child) { // 셀프 연관 관계
this.child.add(child); // 자기 자신을 넣기
child.setParent(this); // 부모 설정
}
// 연관 관계 메소드
public void setPerent(Category perent) { // 셀프 연관 관계
this.perent = perent; // 자기 자신을 넣기
perent.getChild.add(this); // 자식 설정
}
두 메소드는 동일하게 작동함
자기 자신에 부모를 설정하는 것
부모 자기 자신에 자식을 설정하는 것
public void setMember(Member member) { // 연관
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void setDelivery(Delivery delivery) { // 연관
this.delivery = delivery;
delivery.setOrder(this);
}