거누의 개발노트

[WARN]firstResult/maxResults specified with collection fetch; applying in memory! 본문

트러블 슈팅

[WARN]firstResult/maxResults specified with collection fetch; applying in memory!

Gogozzi 2022. 7. 8. 15:15
반응형

원인

firstResult/maxResults specified with collection fetch; applying in memory!
라는 경고가 떴다.
동작에 문제는 없지만 메모리 낭비를 한다는 내용인 것 같다.
경고를 보아 아마 fetch join 시 offset limit 을 걸어둔 것과 연관이 있는 것 같다.

찾아보니 위 경고가 의미하는 것은
fetch join 과 pagination 을 같이 할 시
"모든 데이터"를 전부 가져와 메모리에서 걸러낸다는 것이다.

@EntityGraph(attributePaths = {"user", "boardTodo"}, type = EntityGraph.EntityGraphType.LOAD)
Page<Board> findByTitleContainingAndCategory(String keyword, Category category, Pageable pageable);

Board와 user, boardTodo는 oneToMany 연관관계를 가지고 있다.

Board가 여러개의 BoardTodo를 가질 수 있기 때문에 Board가 중복되어서 조회된다.

실제 쿼리에서도 중복되어서 조회되는것을 확인할 수 있다.

또, 모든 데이터를 가져와서 JVM 메모리에서 페이지네이션을 하기 때문에 만약 많은 사람들이 서비스를 이용한다면 속도는 확실히 느려질 것이다.

오류 해결

1. @EntityGraph 어노테이션 제거

//    @EntityGraph(attributePaths = {"user", "boardTodo"}, type = EntityGraph.EntityGraphType.LOAD)
    Page<Board> findAll(Pageable pageable);

2. application.properties 수정

  • spring.jpa.properties.hibernate.default_batch_fetch_size=1000

3. @BatchSize 어노테이션 적용

@OneToMany(mappedBy = "board", cascade = CascadeType.ALL)
@BatchSize(size=100)
private Set<BoardTodo> boardTodo = new LinkedHashSet<>();

@ManyToOne
@JoinColumn(nullable = false)
private User user;

적용했으나, org.hibernate.LazyInitializationException 해당 오류 발생

이 오류는 board를 조회까지는 성공했는데,

BoardTodoResponseDto.getBoardTodoList(board.getBoardTodo());

BoardResponseDto에서 위 처럼 BoardTodoList를 호출해서 사용할 때 영속성 컨텍스트가 종료되어 버려서, 지연 로딩을 할 수 없어서 발생하는 오류라고 판단 했다.

JPA에서 지연로딩을 하려면 항상 영속성 컨텍스트가 있어야 하는데, 해당 메서드를 호출하기 전에 영속성 컨텍스트가 종료되어서 지연로딩을 할 수 없던 것이다.

그래서 Service의 메소드 위에 @Transactional 어노테이션을 붙여주었다.

트랜잭션은 데이터베이스 상태를 변경하는 작업 또는 한번에 수행되어야 하는 연산들을 의미한다.

 

@Transactional 관련한 내용은 아래 링크를 참조했고, 나도 언제한번 제대로 정리해야겠다는 생각이 들었다.

https://devkingdom.tistory.com/287

 

[Spring] @Transactional 정리

하이 .. 요즘 지속적으로 JPA 에 대해 공부를 하고 있는데 JPA를 SpringBoot에서 사용하면서 습관적으로 생각하지 않고 CREATE, UPDATE, DELETE API 메서드를 만들때 @Transactional 어노테이션을 사용하였다. 그.

devkingdom.tistory.com

 

반응형
Comments