[WARN]firstResult/maxResults specified with collection fetch; applying in memory!
원인
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