๊ด€๋ฆฌ ๋ฉ”๋‰ด

Tech Log ๐Ÿ› ๏ธ

[jpa] ์ปฌ๋ ‰์…˜ ์กฐํšŒ ์ตœ์ ํ™” - 2 ๋ณธ๋ฌธ

jpa

[jpa] ์ปฌ๋ ‰์…˜ ์กฐํšŒ ์ตœ์ ํ™” - 2

sehaan 2023. 3. 1. 02:16

์ปฌ๋ ‰์…˜ ์กฐํšŒ ์ตœ์ ํ™” - 1 ์—์„œ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•œ ํ›„์— DTO๋กœ ๋ณ€ํ™˜์‹œํ‚ค๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•˜์˜€์ง€๋งŒ,

์ด๋ฒˆ ์ฑ•ํ„ฐ์—์„œ๋Š” JPA ์—์„œ DTO๋ฅผ ์ง์ ‘ ์กฐํšŒํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•œ๋‹ค.

 

JPA์—์„œ DTO ์ง์ ‘ ์กฐํšŒ

DTO๋ฅผ ์กฐํšŒํ•  ๋•Œ, ToOne ๊ด€๊ณ„๋“ค์„ ๋จผ์ € ์กฐํšŒํ•˜๊ณ  ToMany ๊ด€๊ณ„๋“ค์€ ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด ์ข‹๋‹ค.ToOne ๊ด€๊ณ„๋ฅผ ๋จผ์ € ์ฒ˜๋ฆฌํ•˜๋Š” ์ด์œ ๋Š” Row  ์ˆ˜์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.ํ•˜์ง€๋งŒ ToMany ๊ด€๊ณ„๋Š” Row์ˆ˜๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋ฏ€๋กœ(๋ฐ์ดํ„ฐ ๋ปฅํŠ€๊ธฐ) ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ํŽธ์ด ์ข‹๋‹ค.

 

private List<OrderQueryDto> findOrders() {return em.createQuery(
"select new
jpabook.jpashop.repository.order.query.OrderQueryDto(o.id, m.name, o.orderDate,
o.status, d.address)" +
" from Order o" +
" join o.member m" +
" join o.delivery d", OrderQueryDto.class)
.getResultList();
}

/**
* 1:N ๊ด€๊ณ„์ธ orderItems ์กฐํšŒ
*/

private List<OrderItemQueryDto> findOrderItems(Long orderId) {
return em.createQuery(
"select new
jpabook.jpashop.repository.order.query.OrderItemQueryDto(oi.order.id, i.name,
oi.orderPrice, oi.count)" +
" from OrderItem oi" +
" join oi.item i" +
" where oi.order.id = : orderId",
OrderItemQueryDto.class)
.setParameter("orderId", orderId)
.getResultList();
}

์œ„์˜ ์ฝ”๋“œ์—์„œ findOrder() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์ฃผ๋ฌธ๋“ค์„ ๋ชจ๋‘ ์กฐํšŒํ•œ๋‹ค.

์ฃผ๋ฌธ๋“ค์„ ๋ชจ๋‘ ์กฐํšŒํ•˜๋Š” ์ด์œ ๋Š” ์—”ํ‹ฐํ‹ฐ์™€ ์ฃผ๋ฌธ์€ ToOne ๊ด€๊ณ„์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

์ฃผ๋ฌธ ์•„์ดํ…œ๋“ค์€ ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•ด์ฃผ์—ˆ๋Š” ๋ฐ ์ด ์—ญ์‹œ ์œ„์—์„œ ์„ค๋ช…ํ•œ๋Œ€๋กœ ToMany ๊ด€๊ณ„์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•˜์˜€๋‹ค.

 

public List<OrderQueryDto> findOrderQueryDtos() {
//๋ฃจํŠธ ์กฐํšŒ(toOne ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ํ•œ๋ฒˆ์— ์กฐํšŒ)
List<OrderQueryDto> result = findOrders();
//๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ ์ปฌ๋ ‰์…˜ ์ถ”๊ฐ€(์ถ”๊ฐ€ ์ฟผ๋ฆฌ ์‹คํ–‰)
result.forEach(o -> {
List<OrderItemQueryDto> orderItems =
findOrderItems(o.getOrderId());
o.setOrderItems(orderItems);
});
ret

 

์ด๋ ‡๊ฒŒ ์กฐํšŒ๋œ ๊ฒฐ๊ณผ๋“ค์„ DTO์— ๋งคํ•‘์‹œ์ผฐ๋‹ค.

 

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, id ๋ฅผ ์กฐํšŒํ• ๋•Œ ์ฟผ๋ฆฌ 1๋ฒˆ์ด ๋ฐœ์ƒํ•˜๊ณ  ๊ทธ ๋’ค๋กœ ์ปฌ๋ ‰์…˜์„ ์กฐํšŒํ•  ๋•Œ ์ฟผ๋ฆฌ N๋ฒˆ์ด ๋ฐœ์ƒํ•˜๋ฏ€๋กœ 

1+N ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค. 

๋”ฐ๋ผ์„œ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

JPA์—์„œ DTO ์ง์ ‘ ์กฐํšŒ - ์ปฌ๋ ‰์…˜ ์กฐํšŒ ์ตœ์ ํ™”

๊ธฐ์กด์˜ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ ์กฐํšŒํ–ˆ๋˜ ๋ฐฉ์‹์„ Map ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•จ์œผ๋กœ์จ 1+N ๋ฌธ์ œ๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

private Map<Long, List<OrderItemQueryDto>> findOrderItemMap(List<Long>
orderIds) {
List<OrderItemQueryDto> orderItems = em.createQuery(
"select new
jpabook.jpashop.repository.order.query.OrderItemQueryDto(oi.order.id, i.name,
oi.orderPrice, oi.count)" +
" from OrderItem oi" +
" join oi.item i" +
" where oi.order.id in :orderIds", OrderItemQueryDto.class)
.setParameter("orderIds", orderIds)
.getResultList();
return orderItems.stream()
.collect(Collectors.groupingBy(OrderItemQueryDto::getOrderId));
}

๊ธฐ์กด ์ฝ”๋“œ์™€ ๋‹ค๋ฅธ ์ ์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฃผ๋ฌธId (์‹๋ณ„์ž) ๊ฐ€ ๋‹ด๊ธด ๋ฆฌ์ŠคํŠธ๋ฅผ ๋„˜๊ฒจ์คฌ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ๊ธฐ์กด ๋ฃจํ”„ ์ฟผ๋ฆฌ๊ฐ€ ์•„๋‹Œ in์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด ์•„์ดํ…œ ์ •๋ณด๊ฐ€ ๋‹ด๊ธด ๋ฐ์ดํ„ฐ๋“ค์„ ํ•œ๋ฒˆ์— ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ์ด๋ ‡๊ฒŒ ๋ถˆ๋Ÿฌ์˜จ ๋ฐ์ดํ„ฐ๋“ค์„ Map ์„ ์ด์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌํ•œ๋‹ค.

 

result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));

๋งˆ์ง€๋ง‰์œผ๋กœ DTO ์— ๋งคํ•‘์‹œํ‚ฌ ๋•Œ, ์ด๋ฏธ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌ๋˜์žˆ๋Š” ์ƒํƒœ์ด๋ฏ€๋กœ ์ถ”๊ฐ€ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ๋ฃจํŠธ ์ฟผ๋ฆฌ์—์„œ 1๋ฒˆ , ์ปฌ๋ ‰์…˜ ์กฐํšŒํ•  ๋•Œ 1๋ฒˆ๋งŒ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ 1+N ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

 

JPA์—์„œ DTO๋กœ ์ง์ ‘ ์กฐํšŒ, ํ”Œ๋žซ ๋ฐ์ดํ„ฐ ์ตœ์ ํ™”

์ฟผ๋ฆฌ๋ฅผ ํ•œ๋ฒˆ๋งŒ ๋‚ ๋ ค์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋“ค์„ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ , ์œ„์™€๋Š” ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์ด๋‹ค.

 

public List<OrderFlatDto> findAllByDto_flat() {
return em.createQuery(
"select new
jpabook.jpashop.repository.order.query.OrderFlatDto(o.id, m.name, o.orderDate,
o.status, d.address, i.name, oi.orderPrice, oi.count)" +
" from Order o" +
" join o.member m" +
" join o.delivery d" +
" join o.orderItems oi" +
" join oi.item i", OrderFlatDto.class)
.getResultList();
}

๋จผ์ € ์กฐํšŒํ•  ๋ฐ์ดํ„ฐ๋“ค์„ "๋ชจ๋‘" ๊ฐ€์ ธ์˜จ๋‹ค.

 

public List<OrderQueryDto> ordersV6() {
List<OrderFlatDto> flats = orderQueryRepository.findAllByDto_flat();

return flats.stream()
.collect(groupingBy(o -> new OrderQueryDto(o.getOrderId(),
o.getName(), o.getOrderDate(), o.getOrderStatus(), o.getAddress()),
mapping(o -> new OrderItemQueryDto(o.getOrderId(),
o.getItemName(), o.getOrderPrice(), o.getCount()), toList())
)).entrySet().stream()
.map(e -> new OrderQueryDto(e.getKey().getOrderId(),
e.getKey().getName(), e.getKey().getOrderDate(), e.getKey().getOrderStatus(),
e.getKey().getAddress(), e.getValue()))
.collect(toList());
}

๊ทธ ๋‹ค์Œ ์ด์ฒ˜๋Ÿผ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ๊ณ„์—์„œ API ์ŠคํŽ™์— ๋งž๊ฒŒ ์ถ”๊ฐ€ ์ž‘์—…์„ ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.

๋ณด๊ธฐ๋งŒ ํ•ด๋„ ์–ด์ง€๋Ÿฝ๋‹ค ..

 

์ด ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋‹จ์ ์ด ์žˆ๋‹ค.

1. ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋ง ์—†์ด ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๋ณต ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.     

    ์ค‘๋ณต ๋ฐ์ดํ„ฐ๊นŒ์ง€ ํ•จ๊ป˜ ๊ฐ€์ ธ์˜ค๋ฏ€๋กœ ์ฟผ๋ฆฌ๋Š” ์ค„์—ˆ์ง€๋งŒ ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์€ ์ €ํ•˜๋  ์ˆ˜ ์žˆ๋‹ค..

2. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹จ๊ณ„์—์„œ ์ถ”๊ฐ€ ์ž‘์—…์„ ํ•ด์ค˜์•ผํ•œ๋‹ค. 

3. ํŽ˜์ด์ง•์ด ์•ˆ๋œ๋‹ค!      

    ์ฃผ๋ฌธ ํ•˜๋‚˜(1)๋‹น ์—ฌ๋Ÿฌ๊ฐœ(N)์˜ ๋ฐ์ดํ„ฐ๋“ค์ด ๋”ธ๋ ค์˜ค๋Š”๋ฐ ์—ฌ๊ธฐ์„œ ํŽ˜์ด์ง•์„ ํ•˜๋ฉด (N)์„ ๊ธฐ์ค€์œผ๋กœ ํŽ˜์ด์ง•์ด ๋œ๋‹ค.