spring - 当Specification和Pageable一起使用时如何禁用计数?

标签 spring spring-data-jpa spring-data

这些方法带有 JpaSpecificationExecutor不够,他们都没有给我我想要的:

Page<T> findAll(Specification<T> spec, Pageable pageable)

List<T> findAll(Specification<T> spec)

List<T> findAll(Specification<T> spec, Sort sort)

第一个方法执行分页查询和计数查询。接下来的 2 个根本不执行分页。我需要的是以下之一:

Slice<T> findAll(Specification<T> spec, Pageable pageable)

List<T> findAll(Specification<T> spec, Pageable pageable)

通过不扩展 JpaSpecificationExecutor,我能够执行两个查询,但计数查询也是如此。在我的情况下,必须避免计数查询,因为它非常昂贵。问题是如何?

最佳答案

查看 SimpleJpaRepositoryfindAll(Specification, Pageable)readPage(TypedQuery, Pageable, Specific) 方法。看来Spring的实现是在执行select查询之前总是执行计数查询并检查startIndex是否超出范围:

protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) {

    query.setFirstResult(pageable.getOffset());
    query.setMaxResults(pageable.getPageSize());

    Long total = QueryUtils.executeCountQuery(getCountQuery(spec));
    List<T> content = total > pageable.getOffset() ? query.getResultList() : Collections.<T> emptyList();

    return new PageImpl<T>(content, pageable, total);
}

我不认为这始终是最佳实践。例如,在我的用例中,我们很乐意预先执行一次计数查询,而不是在后续调用中执行,因为我们知道新数据的出现频率不足以保证计数更新,并且执行计数查询的成本非常昂贵。

如果 Spring Data 可以提供一个标志或替代方法来禁用条件查询的计数,那就太好了,类似于 simple find queries

同时,这是我的解决方案:

创建一个子类 SimpleJpaRepository 的内部类。重写 readPage 以禁用计数查询。创建一个 DAO,用 @Repository 注释它并实例化这个内部类以传递正确的 EntityManager。最后,在适用“无计数”条件搜索的地方注入(inject)此 DAO:

@Repository
public class CriteriaNoCountDao {

    @PersistenceContext
    protected EntityManager em;

    public <T, ID extends Serializable> Page<T> findAll(Specification<T> spec, Pageable pageable, Class<T> clazz){
        SimpleJpaNoCountRepository<T, ID> noCountDao = new SimpleJpaNoCountRepository<T, ID>(clazz, em);
        return noCountDao.findAll(spec, pageable);
    }

    /**
     * Custom repository type that disable count query.
     */
    public static class SimpleJpaNoCountRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {

        public SimpleJpaNoCountRepository(Class<T> domainClass, EntityManager em) {
            super(domainClass, em);
        }

        /**
         * Override {@link SimpleJpaRepository#readPage(TypedQuery, Pageable, Specification)}
         */
        protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) {
            query.setFirstResult(pageable.getOffset());
            query.setMaxResults(pageable.getPageSize());

            List<T> content = query.getResultList();

            return new PageImpl<T>(content, pageable, content.size());
        }
    }
}

关于spring - 当Specification和Pageable一起使用时如何禁用计数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60591109/

相关文章:

java - 不加载整个对象

java - 使用 AbstractExcelView 下载为 .html 文件

名称中带有方括号的 Javascript 对象

java - 使用用户定义的 Spring @Components 进行异步处理

mysql - jpa自定义查询多参数

hibernate - 查询存储在单独集合中的 @Embeddable 对象

spring-data - Spring 数据项目 - 状态?

java - Spring Boot Property Yml/Properties with List 结构

redis - 如何为 Spring 数据访问配置 ElastiCache Redis

java - 带有事务轮询器 Java 配置的 Spring Integration JPA 入站 channel 适配器