반응형

One Two Many fetch Join

페이징 페치 조인을 사용해 보았습니다. 이런 HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory 오류가 발생했습니다. 이 오류는 컬렉션 페치 조인에 적용된 상태에서 페이징을 적용할 때 발생하며, 이는 Hibernate가 컬렉션을 메모리에 로드한 후에 페이징을 적용해야 한다는 것을 의미합니다.

이러한 상황에서는 데이터가 적은 경우에는 큰 문제가 되지 않지만, 데이터가 많으면 성능 이슈가 발생할 수 있고, 때로는 메모리 초과 예외가 발생할 수도 있습니다. 이러한 이유로 Hibernate는 경고 메시지를 통해 알려줍니다

 

해결 방법

쿼리 수정

컬렉션 페치 조인 대신 필요한 데이터만 가져올 수 있는 쿼리를 작성해 페이징 처리에 대한 성능 문제를 해결할 수 있습니다.

@Repository
public interface ParentEntityRepository extends JpaRepository<ParentEntity, Long> {

    // 컬렉션 페치 조인 대신 단순한 쿼리 사용
    @Query("SELECT DISTINCT p FROM ParentEntity p")
    Page<ParentEntity> findAllPaged(Pageable pageable);
}

서브 쿼리

필요한 데이터만을 가져오는 방법입니다.

주 쿼리와는 별도로 컬렉션을 가져오므로 페이징과 사용할 때 유용합니다.

@Fetch(FetchMode.SUBSELECT)
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<ChildEntity> children = new ArrayList<>();

BetchSize

BatchSize를 설정하여 일괄적으로 컬렉션을 가져오는 방법

오류를 피하기 위해 일괄적으로 가져올 때 한 번에 가져올 데이터 양을 제어할 수 있습니다.

@BatchSize(size = 10) // 적절한 배치 크기 설정
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<ChildEntity> children = new ArrayList<>();

지연 로딩 사용

컬렉션을 지연 로딩으로 설정하여, 필요한 시점에만 컬렉션을 로드합니다.

@Entity
public class ParentEntity {
    // 이하 생략

    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) // Lazy 로딩 설정
    private List<ChildEntity> children = new ArrayList<>();

}

@Repository
public interface ParentEntityRepository extends JpaRepository<ParentEntity, Long> {

    // 페이징 처리를 위한 쿼리
    @Query("SELECT DISTINCT p FROM ParentEntity p")
    Page<ParentEntity> findAllPaged(Pageable pageable);
}

 

페이징 컬렉션 로딩 전략

Hibernate에서 제공하는 페이징 컬렉션 로딩 전략을 사용하여 필요한 만큼의 데이터만 가져옵니다.

 

이런 페치 조인은 객체 그래프를 유지할 때 사용하면 효과적이지만 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 한다면 페치조인보다는 여러 테이블에서 필요한 필드들만 조회해서 DTO 반환하는 것이 효과적일 수 있습니다.

728x90