❗️ 메뉴마다 옵션이 있을 수도 없을 수도 있다. 없는 경우에는 바로 cart 엔티티만 생성해서 insert 하면 되지만, 옵션이 있는 경우에는 cart와 그에 대응하는 cart_option을 저장하는 insert문이 n번 이상 실행돼야 한다.
한 개의 메뉴 (한 개의 카트)는 여러 개의 옵션을 가질 수 있다. 즉, 한 개의 카트와 n개의 옵션이 db에 모두 반영되던가, 전혀 반영되지 않아야 하는 원자성을 지녀야 한다.
기존 코드는 이러한 특성이 반영되지 않았기 때문에 CartController addItemToCart를 수정하고 CartService에 카트와 옵션을 함께 저장하는 메서드를 생성했다.
Controller
if (optionId == null) {
cartService.createCart(cart);
} else {
cartService.createCartAndCartOptions(cart, optionId);
}
컨트롤러 addItemToCart 먼저 분기 처리하기. optionId 리스트가 널이라면 옵션이 없는 메뉴이니 createCart만 호출한다.
그렇지 않다면 createCartAndCartOption을 호출하여 cart 엔티티와 optionId 리스트를 인자로 넘겨준다.
Service
@Transactional
@Override
public Cart createCartAndCartOptions(Cart cart, List<Integer> optionIdList) {
Cart createdCart = cartRepository.save(cart);
List<CartOption> cartOptionList = optionIdList.stream().map(id ->
CartOption.builder()
.cart(createdCart)
.option(optionService.getById(id))
.build())
.collect(Collectors.toList());
cartOptionRepository.saveAll(cartOptionList);
return createdCart;
}
cart를 먼저 저장하고 id를 받아온다. optionIdList에 있는 optionId를 이용해서 cartOption 엔티티를 생성한다. 그리고 생성된 엔티티들은 리스트에 담아준다. 기존 코드는 for문을 반복하여 save 를 했지만, saveAll을 사용하도록 수정했다.
save vs saveAll
save: 단일 엔티티를 데이터베이스에 저장하는데 사용
saveAll: 여러 엔티티를 데이터베이스에 한번에 저장하는 데 사용
성능 차이: saveAll()을 사용하는게 여러 엔티티를 저장할 때 성능적으로 유리하다
1. JDBC 배치 업데이트 최적화: 데이터베이스에 명령을 내리려면, 일반적으로 네트워크를 통해 각 명령을 데이터베이스에 전송해야 한다. 명령을 하나씩 보내는 것은 비효율적일 수 있기 때문에, 보다 효율적인 작업을 위해 여러 SQL문을 그룹화하여 한 번에 데이터베이스로 전송하는 것을 "배치 업데이트"라고 한다. saveAll은 이러한 배치 업데이트를 통해 여러 엔티티만 저장할 수 있지만 save 메서드는 한 번에 한 개의 엔티티만 저장하므로 이러한 최적화를 활용하기 어렵다.
2. 1차 캐시와 쓰기 지연: JPA는 성능 최적화를 위해 1차 캐시와 쓰기 지연이라는 개념을 사용한다. 1차 캐시는 JPA가 관리하는 엔티티를 임시 저장하는 공간이고, 이 캐시는 트랜잭션이 끝날 때까지 유지되며 트랜잭션이 커밋될 때 모든 변경사항이 데이터베이스에 반영된다. 이렇게 변경사항을 반영을 미루는 것을 쓰기 지연이라고 한다. saveAll 메서드는 여러 엔티티를 한번에 저장하므로 이러한 성능 최적화를 더 잘 활용할 수 있다.
또한 트랜잭션 처리를 위해 @Transactional 어노테이션을 붙여줬다.
근데 보니 javax와 spring.annotation 두가지 종류가 있다.
@Transactional 어노테이션 javax vs spring.annotaion
1. 기능적 차이: Spring의 @Ttransactional이 더 많은 설정 옵션을 가지고 있다. (트랜잭션 타임아웃, 읽기 전용 트랜잭션 등등) 반면 javax @Transactional은 이러한 기능을 제공하지 않는다.
2. 플랫폼 의존성: Spring @Transactional은 스프링 프레임워크에 의존적이다 👉 사용하려면 스프링 프레임워크가 필요하다. 반면 javax @Transactional은 JEE (Java Enterprise Edition) 환경에서 사용된다고 한다. 즉 스프링에 의존하지 않고 사용할 수 있는 어노테이션이다.
👉 스프링 프레임워크를 사용하고 있고, 스프링의 @Transactional이 제공하는 추가 기능을 활용하려면 스프링 @Transactional을 사용하는게 좋다.
참고:
spring data jpa save, saveAll 비교
source 는 Github 에 있습니다.save 동작 원리는 이전 글 참고하시면 됩니다.간략히 설명하면 save(xxx) 는 1건을 저장하는 메소드이며, saveAll 은 다건이상의 데이터를 저장하는 메소드입니다.아래 로직
velog.io
[Spring] JPA의 save와 saveAll의 성능 차이, 그리고 원인.
안녕하세요? 거두절미하고 바로 작성해보겠습니다. JPA에서 데이터를 DB에 INSERT하기 위해 사용하는 save와 saveAll이 있습니다. 이 두 가지의 큰 차이는 save는 1개를 저장하는 것이고, saveAll은 안에
maivve.tistory.com
어떤 @Transactional을 사용해야 할까?
애플리케이션을 개발하다 보면, 보통 @Transactional을 사용해서 Transaction을 사용합니다. 관습적으로 사용하다 보니, 내부적으로 어떻게 돌아가는지 원리에 대해서만 관심을 가졌습니다. 하지만 여
interconnection.tistory.com