15

我使用 JPA CriteriaQuery 构建动态查询并传入 Spring Data Pageable 对象:

sort=name,desc

在后端,我的存储库中有一个方法来支持动态查询:

public Page<User> findByCriteria(String username, Pageable page) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<User> cq = cb.createQuery(User.class);
    Root<User> iRoot = cq.from(User.class);
    List<Predicate> predicates = new ArrayList<Predicate>();

    if (StringUtils.isNotEmpty(username)) {
        predicates.add(cb.like(cb.lower(iRoot.<String>get("username")), "%" + username.toLowerCase() + "%"));
    }

    Predicate[] predArray = new Predicate[predicates.size()];
    predicates.toArray(predArray);

    cq.where(predArray);

    TypedQuery<User> query = em.createQuery(cq);

    int totalRows = query.getResultList().size();

    query.setFirstResult(page.getPageNumber() * page.getPageSize());
    query.setMaxResults(page.getPageSize());

    Page<User> result = new PageImpl<User>(query.getResultList(), page, totalRows);

    return result;
}

注意:我只放了一个参数用于演示目的。

但是返回的数据是未排序的,因此我想问的是在 CriteriaQuery 中实现 Pageable 的任何方法。

4

4 回答 4

11

您可以使用 Spring 中的 QueryUtils 添加排序: query.orderBy(QueryUtils.toOrders(pageable.getSort(), root, builder));

于 2020-05-20T06:55:45.237 回答
6

在您的条件查询中,我看不到我会以这种方式编写的排序信息:

     CriteriaBuilder cb = em.getCriteriaBuilder();
     CriteriaQuery<User> cq = cb.createQuery(User.class);
     Root<User> iRoot = cq.from(User.class);
     List<Predicate> predicates = new ArrayList<Predicate>();

     if (StringUtils.isNotEmpty(username)) {
         predicates.add(cb.like(cb.lower(iRoot.<String>get("username")), "%" + username.toLowerCase() + "%"));
     }

     Predicate[] predArray = new Predicate[predicates.size()];
     predicates.toArray(predArray);

     cq.where(predArray);

     List<Order> orders = new ArrayList<Order>(2);
     orders.add(cb.asc(iRoot.get("name")));
     orders.add(cb.asc(iRoot.get("desc")));

     cq.orderBy(orders);
     TypedQuery<User> query = em.createQuery(cq);
     Page<User> result = new PageImpl<User>(query.getResultList(), page, totalRows);

     return result;

我没有测试它,但它应该可以工作

安杰洛

于 2017-01-17T08:47:43.010 回答
1

Op 问题和 Angelo 的答案在现实生活中都不起作用。如果该表/查询返回数千条记录,则此方法将执行缓慢,您还可能会产生内存问题。

为什么?

  • int totalRows = query.getResultList() .size( );
  • new PageImpl( query.getResultList() , page, totalRows);

两个结果集都将运行相同的查询两次,并在这些集合中保留一个完整的表/查询数据,只是为了进行计数或切片,当您需要在前端显示少量记录时,这是没有意义的。

我对 Spring Data 的建议是在您的存储库中扩展 JpaSpecificationExecutor 接口

@Repository
public interface UserRepository extends JpaRepository<User, Integer>,
        JpaSpecificationExecutor {
}

此接口将使您的存储库能够使用findAll(Specification, Pageable)方法。

在那之后,一切都很容易。您只需要创建一个 Java 闭包即可将谓词注入到动态 jpa 查询中。

public Page<User> listUser( String name, int pageNumber, int pageSize ) {

Sort sort = Sort.by("id").ascending();
Pageable pageable = PageRequest.of(pageNumber, pageSize, sort);


return this.reservationRepository.findAll((root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if name!= null ) {
                predicates.add(builder.equal(root.get("name"), name));
            }
            // More simple/complex conditions can be added to 
            // predicates collection if needed.

            return builder.and(predicates.toArray(new Predicate[]{}));

        }, pageable);

}

正如您在此处看到的,此解决方案支持排序/过滤和数据库侧分页。

于 2021-11-16T21:10:18.267 回答
0
 CriteriaBuilder cb = em.getCriteriaBuilder();
 CriteriaQuery<User> cq = cb.createQuery(User.class);
 Root<User> iRoot = cq.from(User.class);
 List<Predicate> predicates = new ArrayList<Predicate>();

 if (StringUtils.isNotEmpty(username)) {
     predicates.add(cb.like(cb.lower(iRoot.<String>get("username")), "%" + 
     username.toLowerCase() + "%"));
 }


 Predicate[] predArray = new Predicate[predicates.size()];
 predicates.toArray(predArray);

 cq.where(predArray);

 cq.orderBy(cb.desc(user.get("name")));

 TypedQuery<User> query = em.createQuery(cq);
 Page<User> result = new PageImpl<User>(query.getResultList(), page, totalRows);

 return result;
于 2020-07-16T05:23:12.347 回答