hibernate JPA CriteriaQuery实现了Spring Data 可分页,getSort()

nzk0hqpo  于 2023-03-03  发布在  Spring
关注(0)|答案(5)|浏览(120)

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

sort=name,desc

在后端,我的Repository中有一个支持动态查询的方法:

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。

ncecgwcz

ncecgwcz1#

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

wn9m85ua

wn9m85ua2#

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

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;

我没有测试过,但应该可以用
安杰洛

eqzww0vc

eqzww0vc3#

Op问题和Angelo的答案在现实场景中都不起作用。如果表/查询返回数千条记录,这种方法执行起来会很慢,而且还可能产生内存问题。
为什么?

  • 整数总行数=**查询结果列表()**大小();
  • 新的页面实现(查询.获取结果列表(),页面,总行数);

两个结果集将运行相同的查询两次,并在这些集合中保留完整的表/查询数据,只是为了计数或切片,当您需要在前端显示很少的记录时,这是没有意义的。
我对SpringData的建议是在存储库中扩展JpaSpecificationExecutor接口

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

此接口将使您的存储库能够使用**findAll(Specification,Pageable)**方法。
在那之后,一切都相当容易,您只需要创建一个Java闭包来将 predicate 注入到动态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);

}

如您所见,此解决方案支持排序/筛选和数据库端分页。

ffscu2ro

ffscu2ro4#

使用JpaSpecificationExecutor并将其封装在类中的完整示例:
首先将存储库定义为SomeEntity的JpaRepository和JpaSpecificationExecutor的扩展:

interface SomeEntityRepositoryPaged extends JpaRepository<SomeEntity, Integer>, JpaSpecificationExecutor<SomeEntity> {
 Page<SomeEntity> findAll(Specification<SomeEntity> spec, Pageable pageable);

}
按条件查找类:

class FindByCriteria{
     FindByCriteria() {
            ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext();
            repository = appCtx.getBean(SomeEntityRepositoryPaged.class);
        }
    
        private SomeEntityRepositoryPaged repository;
        
        public Page<SomeEntity> searchBy(Client client, SearchParams params,Pageable page){
            return repository.findAll(getCriteria(params), page);
        }
        private Specification<SomeEntity> getCriteria(SearchParams params){
              return (paymentRoot, query, cb)-> 
              { 
                    List<Predicate> predicates = new ArrayList<>(5);
            
                    filterByParams(params, cb, paymentRoot, predicates);

                    query.where(predicates.toArray(new Predicate[] {}));

                    //IMPORTANT: the order specify should be deterministic, meaning rows can never change order in 2 subsequent calls.
                    
                    List<Order> orders = new ArrayList<>(1);
                    orders.add(cb.asc(paymentRoot.get("date")));
                    
                    query.orderBy(orders);
                    
                    return  null;
              };
            }

        private void filterByParams(SearchParams params, CriteriaBuilder cb, Root<SomeEntity> payment,
                List<Predicate> predicates) {
        
            if (params.getInitialDate() != null)
                predicates.add(cb.greaterThan(payment.get("paymentDate"), params.getInitialDate()));

            if (params.getFinalDate() != null)
                predicates.add(cb.lessThanOrEqualTo(payment.get("paymentDate"), params.getFinalDate()));

            if (params.getState() != null)
                predicates.add(cb.equal(payment.get("state"), params.getState()));
            //... add here any criteria needed bases on params object
        }
    }

}
调用简单调用:

@Test pagedQueryTest
{
     var page = PageRequest.of(2, 10);
     var params = new SearchParams (...);
     var list = new FindByCriteria().searchBy(params, page);
     assert...
     //The generated native query should be optimized.
     //For mssql the native query should end with "offset ? rows fetch first ? rows only" 
     //and that means the store engine will only fetch the rows after first parameter (=page*size), and to the max of the page size.
}
vxf3dgd4

vxf3dgd45#

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;

相关问题