Setter
가 모두 열려있으면, 변경 포인트가 너무 많아서 유지보수가 어려워짐즉시로딩(EAGER)
은 예측이 어렵고 어떤 SQL이 실행될지 추적하기가 어려움
특히 JPQL을 실행할 때 N+1 문제가 자주 발생함
public class Order {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "member_id")
private Member member;
order와 member 엔티티의 연관관계가 즉시로딩으로 설정되어 있으면, order를 조회하는 시점에 member를 꼭 가져와야 함
JPQL select o from oder o; -> SQL select * from order
order를 가져오는 요청 수행 후에 만약 100개의 order가 존재하면 그 100개의 order에 대한 member를 총 100번의 조회를 통해 가져와야 하는 N+1
문제가 발생함
실무에서 모든 연관관계는 지연로딩(LAZY)
로 설정해야 함
연관된 엔티티를 함께 DB에서 조회해야 하면, fetch join
또는 엔티티 그래프 기능을 사용함
@XToOne(OneToOne, ManyToOne)
관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정해야 함
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "delivery_id")
private Delivery delivery;
private LocalDateTime orderDate; //주문시간
@Enumerated(EnumType.STRING)
private OrderStatus status; //주문상태
}
null
문제에서 안전함@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
ember member = new Member();
System.out.println(member.getOrders().getClass());
em.persist(member);
System.out.println(member.getOrders().getClass());
//출력 결과
class java.util.ArrayList
class org.hibernate.collection.internal.PersistentBag
하이버네이트는 엔티티를 영속화할 때, 컬렉션을 감싸서 하이버네이트가 제공하는 내장 컬렉션으로 변경함
만약 getOrders()
처럼 임의의 메서드에서 컬렉션을 잘못 생성하면 하이버네이트 내부 메커니즘에서 문제가 발생할 수 있음
따라서 필드 레벨에서 생성하는 것이 가장 안전하고 코드도 간결함
SpringPhysicalNamingStrategy
memberPoint
-> member_point
order
entity의 orderDate
(주문시간) 컬럼의 경우 테이블 생성시 컬럼명이 order_date
로 지정되어 생성됨@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
...
private LocalDateTime orderDate; //주문시간
...
}
ImplicitNamingStrategy
사용spring.jpa.hibernate.naming.implicit-strategy
spring.jpa.hibernate.naming.physical-strategy
username
-> usernm
등으로 회사 룰로 바꿀 수 있음)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
Cascade(영속성전이) : 특정 엔티티를 영속 상태로 만들면 연관된 엔티티도 함께 영속 상태로 전이되는 것
Cascade 설정 전
persist(orderItemA)
persist(orderItemA)
persist(orderItemA)
persist(order)
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
...
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
...
}
persist(order)
JPA에서 양방향 연관 관계를 설정했을 때 연관 관계의 주인의 반대쪽 필드는 mappedby
를 설정하여 읽기 전용으로 쓰임
연관관계의 주인 쪽에만 연관관계를 설정하면 객체 지향적이지 않고, DB에서 데이터를 조회하기 전까지는 연관관계 주인의 쪽에만 값이 세팅되어 반대쪽 엔티티에서는 값을 출력해보면 데이터가 나오지 않음
따라서 연관관계 편의 메서드를 사용하면 코드의 중복을 방지하고 양방향 연관 관계의 매핑에서 한쪽만 연관 관계가 설정되는 것을 방지할 수 있음
연관관계 편의 메서드 사용 전
public static void main(String[] args) {
Member member = new Member();
Order order = new Order();
member.getOrders().add(order);
order.setMember(member);
}
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
...
// 연관관계 메서드
public void setMember(Member member){
this.member = member;
member.getOrders().add(this);
}
}
public static void main(String[] args) {
Member member = new Member();
Order order = new Order();
order.setMember(member);
}
많은 것을 배웠습니다, 감사합니다.