개인 프로젝트로 e-commerce 서비스를 개발하던 중 LazyInitializationException 에러가 발생하였다.
LazyInitializationException 발생 이유
서비스단에서 @Transactional을 선언한 method가 종료되면 트랜잭션을 커밋한 후 트랜잭션과 영속성 컨텍스트가 종료된다. 영속성 컨텍스트가 종료되었으므로 조회한 엔티티가 준영속 상태가 되었고 그렇기 때문에 더이상 영속성 컨텍스트의 특징 중 하나인 지연로딩을 사용할 수 없다. 이때 컨트롤러단에서 FetchType.LAZY로 매핑된 테이블을 조회해오면 LazyInitializationException이 발생한다.
LazyInitializationException 해결 과정
- 영속성 컨텍스트가 닫히기 전 서비스단에서 모든 로직을 구현하고 메소드마다 @Transactional 어노테이션을 걸어 주었는데도 LazyInitializationException 에러가 발생했다.
- 서비스단에서 컨트롤러단으로 값을 전달할 때 리스트의 제네릭타입을 DTO가 아닌 CartProduct 엔티티로 전달한 것이 문제였고 CartProduct 엔티티로 조회한 뒤 stream().map().collect(toList())로 타입 변경한 후 전달하여 문제를 해결했다.
- 서비스단의 코드를 간소화 하고자 조회할 때 DTO로 조회를 하도록 구현하였고 DTO 매핑이 쉬운 Querrydsl을 적용했다.
리스트 타입이 CartProduct
@Transactional(readOnly = true)
public List<CartProduct> getAllCartProduct(Long userId) {
Cart cart = cartRepository.findByUserId(userId)
.orElseThrow(() -> new ErrorCustomException(ErrorCode.NO_AUTHENTICATION_ERROR));
List<CartProduct> cartProductList = cartProductRepository.findAllByCartId(cart.getId());
return cartProductList;
}
리스트 타입을 CartProductResponDto로 수정
@Transactional(readOnly = true)
public List<CartProductResponseDto> getAllCartProduct(Long userId) {
Cart cart = cartRepository.findByUserId(userId)
.orElseThrow(() -> new ErrorCustomException(ErrorCode.NO_AUTHENTICATION_ERROR));
List<CartProduct> cartProductList = cartProductRepository.findAllByCartId(cart.getId());
List<CartProductResponseDto> responseDto = cartProductList
.stream()
.map(o -> new CartProductResponseDto(o))
.collect(toList());
return responseDto;
}
Querrydsl을 적용하여 CartProductResponseDto로 장바구니 조회
@Transactional(readOnly = true)
public List<CartProductResponseDto> getAllCartProduct(Long userId) {
Cart cart = cartRepository.findByUserId(userId)
.orElseThrow(() -> new ErrorCustomException(ErrorCode.NO_AUTHENTICATION_ERROR));
List<CartProductResponseDto> responseDto = cartProductRepository.findCartProductByCartId(cart.getId());
return responseDto;
}
CartProductResponseDto로 매핑된 조회 sql문 작성
@RequiredArgsConstructor
public class CartProductRepositoryImpl implements CartProductRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public List<CartProductResponseDto> findCartProductByCartId(Long cartId, Pageable pageable) {
return queryFactory
.select(Projections.constructor(CartProductResponseDto.class,
cartProduct.id,
product.id,
product.productname,
product.productinfo,
product.productprice,
cartProduct.productcount
))
.from(cartProduct)
.join(cartProduct.product, product)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.where(cartProduct.cart.id.eq(cartId))
.groupBy(cartProduct.id)
.orderBy(cartProduct.modifiedAt.desc())
.fetch();
}
}
'JPA' 카테고리의 다른 글
[JPA] 영속성 전이 (CASCADE), 고아객체 제거 (ORPHAN) (0) | 2022.10.05 |
---|---|
[JPA] 프록시, 즉시로딩, 지연로딩 (0) | 2022.10.04 |
[JPA] 연관관계 매핑 (0) | 2022.10.02 |
[JPA] 영속성 컨텍스트란(Persistence Context)? (0) | 2022.10.02 |
[JPA] JPA 란? (0) | 2022.10.01 |