Total row count for pagination using JPA Criteria API

后端 未结 4 2069
深忆病人
深忆病人 2020-12-02 15:46

I am implementing \"Advanced Search\" kind of functionality for an Entity in my system such that user can search that entity using multiple conditions(eq,ne,gt,lt,like etc)

相关标签:
4条回答
  • 2020-12-02 16:27

    I guess both of the answers work. But none of them is optimal. The problem with ThinkFloyd is that createQuery is used two times. And Vladimir Ivanov has created two instances of CriteriaQuery which I think is unnecessary.

    val cb = entityManager.criteriaBuilder
    val cq = cb.createQuery(ManualQuery::class.java)
    val manualQuery = cq.from(ManualQuery::class.java)
    
    val predicates = ArrayList<Predicate>()
    
    /*
    predications..... 
    */ 
    
    cq.select(manualQuery)
            .where(*predicates.toTypedArray())
            .orderBy(cb.desc(manualQuery.get<ZonedDateTime>("createdDate")))
    
    val query = entityManager.createQuery(cq)
    
    // total rows count
    val count = query.resultList.size
    
    val indexedQuery = query
            .setFirstResult((currentPage - 1) * pageSize)
            .setMaxResults(itemsPerPage)
    

    Doing go it works. And it is done in Kotlin. You do it the same way in Java.

    0 讨论(0)
  • 2020-12-02 16:29

    Thanks Vladimir! I took your idea and used separate count query to use my existing array of predicates in it. Final implementation looks like this:

    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<Brand> cQuery = builder.createQuery(Brand.class);
    Root<Brand> from = cQuery.from(Brand.class);
    CriteriaQuery<Brand> select = cQuery.select(from);
    .
    .
    //Created many predicates and added to **Predicate[] pArray**
    .
    .
    CriteriaQuery<Long> cq = builder.createQuery(Long.class);
    cq.select(builder.count(cq.from(Brand.class)));
    // Following line if commented causes [org.hibernate.hql.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.enabled' [select count(generatedAlias0) from xxx.yyy.zzz.Brand as generatedAlias0 where ( generatedAlias1.enabled=:param0 ) and ( lower(generatedAlias1.description) like :param1 )]]
    em.createQuery(cq);
    cq.where(pArray);
    Long count = em.createQuery(cq).getSingleResult();
    .
    .
    select.where(pArray);
    .
    .
    // Added orderBy clause
    TypedQuery typedQuery = em.createQuery(select);
    typedQuery.setFirstResult(startIndex);
    typedQuery.setMaxResults(pageSize);
    List resultList = typedQuery.getResultList()
    

    Though this is working fine but still I am not sure why I have to write

    em.createQuery(cq);
    

    to get it working. Any Idea?

    0 讨论(0)
  • 2020-12-02 16:34

    Why don't you just use count?

    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<Long> cQuery = builder.createQuery(Long.class);
    Root<Brand> from = cQuery.from(Brand.class);
    CriteriaQuery<Long> select = cQuery.select(builder.count(from));
    .
    .
    //Created many predicates and added to **Predicate[] pArray**
    .
    .
    select.where(pArray);
    // Added orderBy clause
    TypedQuery<Long> typedQuery = em.createQuery(select);
    typedQuery.setFirstResult(startIndex);
    //typedQuery.setMaxResults(pageSize);
    // here is the size of your query 
    Long result = typedQuery.getSingleResult();
    
    0 讨论(0)
  • 2020-12-02 16:36

    If you're using Hibernate as your JPA-Provider have a look at projections, especially Projections.rowCount().

    You might have to execute the query twice though, first get the count then get the results.

    Note that for plain JPA you might need some other approach.

    0 讨论(0)
提交回复
热议问题