You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合Specification实现Spring Data的Window分页查询?

Spring Data 3.1 Scrollable API结合Specification查询启动失败的解决方案

Spring Data 3.1引入了Scrollable API支持数据迭代,但官方仅给出仓库内特定方法的示例:

Window<User> findFirst10ByLastnameOrderByFirstname(String lastname, OffsetScrollPosition position);

为实现灵活的多条件过滤,尝试定义带Specification的查询方法:

Window<User> findAll(Specification<User> specification, ScrollPosition position, Limit limit, Sort sort );

但项目启动失败,核心异常如下:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository' defined in io.d4e.graphql.domain.repository.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Could not create query for public abstract org.springframework.data.domain.Window io.d4e.graphql.domain.repository.UserRepository.findAll(org.springframework.data.jpa.domain.Specification,org.springframework.data.domain.ScrollPosition,org.springframework.data.domain.Limit,org.springframework.data.domain.Sort); Reason: Failed to create query for method public abstract org.springframework.data.domain.Window io.d4e.graphql.domain.repository.UserRepository.findAll(org.springframework.data.jpa.domain.Specification,org.springframework.data.domain.ScrollPosition,org.springframework.data.domain.Limit,org.springframework.data.domain.Sort); No property 'findAll' found for type 'User'
   at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786) ~[spring-beans-6.1.4.jar:6.1.4]
   ... 19 common frames omitted
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property 'findAll' found for type 'UserEntity'
   ... 46 common frames omitted

解决建议

1. 调整方法命名,适配Spring Data解析规则

Spring Data JPA会将findAll后的参数逻辑当作实体属性解析,导致错误。改成findBy前缀的方法名即可避免这个问题,同时能正确识别Specification参数:

Window<User> findBy(Specification<User> spec, ScrollPosition position, Limit limit, Sort sort);

2. 自定义仓库实现类(保留findAll命名)

如果必须使用findAll作为方法名,需要手动实现查询逻辑:

  • 定义仓库接口,继承基础接口和自定义接口:
public interface UserRepository extends JpaRepository<UserEntity, Long>, JpaSpecificationExecutor<UserEntity>, UserRepositoryCustom {
}
  • 定义自定义查询接口:
public interface UserRepositoryCustom {
    Window<User> findAll(Specification<User> specification, ScrollPosition position, Limit limit, Sort sort);
}
  • 实现自定义接口(命名必须为原接口名+Impl,Spring Data会自动扫描识别):
@Repository
public class UserRepositoryImpl implements UserRepositoryCustom {

    private final EntityManager entityManager;
    private final JpaEntityInformation<UserEntity, ?> entityInformation;

    public UserRepositoryImpl(EntityManager entityManager, JpaEntityInformation<UserEntity, ?> entityInformation) {
        this.entityManager = entityManager;
        this.entityInformation = entityInformation;
    }

    @Override
    public Window<User> findAll(Specification<User> specification, ScrollPosition position, Limit limit, Sort sort) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<UserEntity> query = cb.createQuery(UserEntity.class);
        Root<UserEntity> root = query.from(UserEntity.class);

        // 应用Specification条件
        if (specification != null) {
            query.where(specification.toPredicate(root, query, cb));
        }

        // 应用排序规则
        if (sort != null) {
            query.orderBy(sort.stream()
                    .map(order -> order.isAscending() ? cb.asc(root.get(order.getProperty())) : cb.desc(root.get(order.getProperty())))
                    .collect(Collectors.toList()));
        }

        // 应用滚动位置和分页限制
        TypedQuery<UserEntity> typedQuery = entityManager.createQuery(query);
        if (position instanceof OffsetScrollPosition offsetPosition) {
            typedQuery.setFirstResult(offsetPosition.getOffset());
        }
        typedQuery.setMaxResults(limit.getMaxSize());

        List<UserEntity> content = typedQuery.getResultList();
        boolean hasNext = content.size() == limit.getMaxSize();
        ScrollPosition nextPosition = hasNext ? position.forward(content) : position;

        return Window.of(content, nextPosition, hasNext);
    }
}

3. 确认仓库继承必要接口

确保你的UserRepository继承了JpaSpecificationExecutor,这是Spring Data支持Specification查询的基础,没有该接口会导致框架无法识别Specification参数:

public interface UserRepository extends JpaRepository<UserEntity, Long>, JpaSpecificationExecutor<UserEntity> {
    Window<User> findBy(Specification<User> spec, ScrollPosition position, Limit limit, Sort sort);
}

内容的提问来源于stack exchange,提问作者Aracos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 14:05:02