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)
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.
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?
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();
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.