public class Order
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
@JoinColumn(name="delivery_id")
private Delivery delivery;
@OneToMany(mappedBy = "order",cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
public class OrderItem {
@Id
@GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
}
public class Item {
@Id
@GeneratedValue
@Column(name = "item_id")
private Long id;
}
컬렉션을 가진 엔티티를 페이징 할때는 컬렉션에 fetch옵션이 들어가면 안된다.
https://keyboardfoot.tistory.com/95
public List<Order> findAllWithMemberDelivery(int offset, int limit) {
return em.createQuery(
"select o from Order o"+
" join fetch o.member m "+
"join fetch o.delivery d ",Order.class)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
따라서 toMany관계가 아닌 부분만 fetch옵션을 주고 컬렉션 부분은 지연로딩에 의한 프록시로 남겨둬야한다.
출력데이터
[
{
"orderId": 11,
"name": "userB",
"orderDate": "2022-09-21T12:47:52.25447",
"orderStatus": "ORDER",
"address": {
"city": "진주",
"zipcode": "2222",
"street": "2"
},
"orderItems": [
{
"itemName": "JPA1 BOOk",
"orderPrice": 10000,
"count": 1
},
{
"itemName": "JPA2 BOOK",
"orderPrice": 40000,
"count": 2
}
]
}
]
(위의 표기된 단계는 그냥 설명을 위해서 붙여놓은 것이다. 단계라는 개념은 없다.)
1단계에서는 컬렉션을 제외한 XtoOne관계의 엔티티들을 한방쿼리로 가져온다.
2,3단계에서 orderItems를 프록시로 만들었던 것을 처리한다.(fetch아님)
2단계에서는 OrderItem의 외래키 order_id를 통해, 즉 order에 해당되는 OrderItem을 모두 가져온다.(2개 가져온다)
3단계에서는 OrderItem안에 있는 Item엔티티(컬렉션)를 가져온다.(2개 가져온다)
이 상태로 실행하게된다면 출력데이터
Order를 조회하는 sql
OrderItem들을 조회하는 sql
OrderItem안에 있는 Item을 조회하는 sql이 나가게 된다.
만약 OrderItem이 100개라면?(OrderItem안에는 Item엔티티가 지연로딩으로 있다) 3단계에서 100개의 sql이나가게되고 db서버와 100번의 통신을 하게 되는것이다.(2단계에서는 한개만 나간다. Order_id로 해당되는 OrderItems를 찾을 수 있기때문이다. 하지만 OrderItem에 해당되는 Item을 조회하기 위해서 Order_id로 찾은 OrderItem들의 개수만큼 Item_id를 통해 Item을 찾는 sql문이 나가게된다. OrderItem에는 Item의 id를 가지고 있다.) 생각보다 많은 sql을 보내게 되고 이는 잦은 db서버와의 통신을 통해 서버에 부하를 준다.
jpa:
hibernate:
ddl-auto: none
properties:
hibernate:
# show_sql: true
format_sql: true
default_batch_fetch_size: 100
logging.level:
org.hibernate.SQL: debug
하지만 hibernate:default_batch_fetch_size:100 옵션을 사용한다면
3단계에서 나가는 sql문이 획기적으로 줄게된다.
hibernate:default_batch_fetch_size:100을 안줬다면 100개의 sql문이 나갔겠지만
hibernate:default_batch_fetch_size:100을 줬기때문에 in안에 100개의 아이디가 담겨 단 하나의 sql만 나가게된다.
이를 통해 db서버와의 통신을 줄일 수 있다