Hibernate警告在查询期间-使用Pageable进行左联接获取

发布时间:2020-07-07 17:28

我有两个班级,一对一。 User

@Entity
public class User {
    @Id
    private Long id;
    ...
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private Set<Note> notes = new HashSet<>();
    ...
}

Note

@Entity
public class Note {

    @Id
    private Long id;
    ...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
    ...
}

UserRepository中,我想从findAll(Pageable<T> va1)覆盖PagingAndSortingRepository<T, ID>方法,以避免N + 1查询问题。使用以下代码,一切正常:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    @Override
    @Query(value = "select distinct u from User u left join fetch u.notes")
    Page<User> findAll();
} 

但是当我添加分页时:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    @Override
    @Query(value = "select distinct u from User u left join fetch u.notes",
            countQuery = "select count(u) from User u")
    Page<User> findAll(Pageable page);
}

我在控制台中看到警告:

HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!

我的问题是,如何解决?

回答1

当您希望join fetch进行分页时,Hibernate进行不分页的SQL查询意味着获取完整的结果集。并在内存中进行分页。

使用两个查询解决此问题的简便方法

  • 仅通过分页获取用户ID
  • 然后用户通过这些ID将获取的注释与IN查询一起加入

代码示例

@Query("select u.id from User u")
List<Long> getAllIds(Pageable page);

@Query(value = "select distinct u from User u left join fetch u.notes where u.id IN (:ids)")
List<User> findAll(@Param("ids")List<Long> ids);
回答2

这是Blaze-Persistence的完美用例。

Blaze-Persistence是JPA之上的查询构建器,它支持JPA模型之上的许多高级DBMS功能。随附的分页支持可解决您可能遇到的所有问题。

它还具有Spring Data集成,因此您可以像现在一样使用相同的代码,只需添加依赖项并进行设置:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-setup

Blaze-Persistence具有许多可以配置的分页策略。默认策略是将ID查询内联到主查询中。像这样:

select u 
from User u 
left join fetch u.notes 
where u.id IN (
  select u2.id
  from User u2
  order by ...
  limit ...
)
order by ...