How to handle pagination in JPA (Criteria and Predicates) - java

I'm fetching the results from DB using criteria and predicates and I got my result list , and I'm trying to apply pagination and sorting but it's not working. Please help me where I'm missing, Here is my code:
private Page<Books> getFiltereBooks(Params params,
PageRequest sortOrder) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Books> criteria = builder.createQuery(Books.class);
Root<Books> booksRoot = criteria.from(Books.class);
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(booksRoot.get("id"), params.getRequestId()));
predicates.add(builder.like(builder.lower(booksRoot.get("name")),
"%" + params.getName().toLowerCase() + "%"));
criteria.where(builder.and(predicates.toArray( new Predicate[predicates.size()])));
criteria.orderBy(builder.desc(booksRoot.get("id")));
List<Books> result = em.createQuery(criteria).getResultList();
int total = result.size();
Page<Books> result1 = new PageImpl<>(result, sortOrder, total);
return result1;
}
when I use this code :
Page<Books> result1 = new PageImpl<>(result, sortOrder, total);
it's not working, I want to return a Page Object. any help is appreciated.

You can try this
private Page<Books> getFiltereBooks(Params params,
Pageable pageable) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Books> criteria = builder.createQuery(Books.class);
Root<Books> booksRoot = criteria.from(Books.class);
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(booksRoot.get("id"), params.getRequestId()));
predicates.add(builder.like(builder.lower(booksRoot.get("name")),
"%" + params.getName().toLowerCase() + "%"));
criteria.where(builder.and(predicates.toArray( new Predicate[predicates.size()])));
criteria.orderBy(builder.desc(booksRoot.get("id")));
// This query fetches the Books as per the Page Limit
List<Books> result = em.createQuery(criteria).setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList();
// Create Count Query
CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
Root<Books> booksRootCount = countQuery.from(Books.class);
countQuery.select(builder.count(booksRootCount)).where(builder.and(predicates.toArray(new Predicate[predicates.size()])));
// Fetches the count of all Books as per given criteria
Long count = em.createQuery(countQuery).getSingleResult();
Page<Books> result1 = new PageImpl<>(result, pageable, count);
return result1;
}

Predicate[] getPredicate(Root<Entity> root, CriteriaBuilder criteriaBuilder, filter params) {
Predicate[] predicatesArr = null;
List<Predicate> predicates = new ArrayList<>();
// fill your predicates list here
predicatesArr = predicates.toArray(new Predicate[predicates.size()]);
return predicatesArr;
}
You need to add a method for creating predicates for each query, Note here you are using the same predicates list "which drived from different query" for both queries [pagination and count]
Root<Books> booksRoot = criteria.from(Books.class);
predicates.add(builder.equal(booksRoot.get("id"), params.getRequestId()));

Related

Getting the Count of Filtered results using Criteria

I have written JPA criteria query to perform Filtering and Pagination.
But I also require total count of the filtered results prior performing pagination. As I have used multiple joins I cannot understand from examples available online. Below is the code for filtering and paginating. Thanks in Advance!
List<Predicate> predicates = new ArrayList<>();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<ResponseDTO> query = cb.createQuery(ResponseDTO.class);
CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
Root<MasterPackagesV1> masterPackagesTable = query.from(MasterPackagesV1.class);
Join<MasterPackagesV1, PackagesV1> packageJoin = masterPackagesTable.join("masterPackagePackagesV1s", JoinType.INNER);
Join<MasterPackagesV1, PackageTypes> packageTypeJoin = masterPackagesTable.join("packageType", JoinType.INNER);
Join<MasterPackagesV1, TargetExams> targetExamJoin = masterPackagesTable.join("masterPackageTargetExamsMap", JoinType.INNER);
Join<PackagesV1, Grades> gradesJoin = packageJoin.join("packageAndGradesMap", JoinType.INNER);
Join<PackagesV1, PackageSubscriptionsV1> packageSubscriptionJoin = packageJoin.join("packagePackageSubscriptionsV1s", JoinType.INNER);
Join<PackageSubscriptionsV1, PackagePaymentPlanV1> packagePaymentJoin = packageSubscriptionJoin.join("packageSubscriptionPackagePaymentPlanV1s", JoinType.INNER);
if(targetYears.size() > 0) { predicates.add(masterPackagesTable.get(MasterPackagesV1_.targetYear).in(targetYears)); }
if(grades.size() > 0) { predicates.add(gradesJoin.get(Grades_.gradeName).in(grades)); }
if(targetExams.size() > 0) { predicates.add(targetExamJoin.get(TargetExams_.targetExam).in(targetExams)); }
query.multiselect(masterPackagesTable.get(MasterPackagesV1_.masterPackageName),
packageJoin.get(PackagesV1_.packageId),
packageJoin.get(PackagesV1_.packageName),
packageJoin.get(PackagesV1_.startSellingDate),
packageJoin.get(PackagesV1_.endSellingDate),
packageJoin.get(PackagesV1_.packageCreationDate),
packageJoin.get(PackagesV1_.packageValidationDate),
packagePaymentJoin.get(PackagePaymentPlanV1_.mrp));
query.where(predicates.stream().toArray(Predicate[]::new)).distinct(true);
TypedQuery<ResponseDTO> typedQuery = em.createQuery(query);
typedQuery.setFirstResult((int) pageable.getOffset());
typedQuery.setMaxResults(pageable.getPageSize());
return typedQuery.getResultList();
}

How to convert List<Long> to Expression<Long> int Java? [duplicate]

How to write criteria builder api query for below given JPQL query?
I am using JPA 2.2.
SELECT *
FROM Employee e
WHERE e.Parent IN ('John','Raj')
ORDER BY e.Parent
This criteria set-up should do the trick:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> q = cb.createQuery(Employee.class);
Root<Employee> root = q.from(Employee.class);
q.select(root);
List<String> parentList = Arrays.asList(new String[]{"John", "Raj"});
Expression<String> parentExpression = root.get(Employee_.Parent);
Predicate parentPredicate = parentExpression.in(parentList);
q.where(parentPredicate);
q.orderBy(cb.asc(root.get(Employee_.Parent));
q.getResultList();
I have used the overloaded CriteriaQuery.where method here which accepts a Predicate.. an in predicate in this case.
You can also do this with Criteria API In clause as below:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> root = cq.from(Employee.class);
List<String> parentList = Arrays.asList("John", "Raj");
In<String> in = cb.in(root.get(Employee_parent));
parentList.forEach(p -> in.value(p));
return entityManager
.createQuery(cq.select(root)
.where(in).orderBy(cb.asc(root.get(Employee_.Parent)))
.getResultList();
Checkout my Github for this and almost all possible criteria examples.
List<String> parentList = Arrays.asList("John", "Raj");
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Employee> query = cb.createQuery(Employee.class);
final Root<Employee> employee = query.from(Employee.class);
query.select(employee).where(employee.get("Parent").in(parentList));
this should work fine. for more info please refer this article by baeldung. it is very resourceful https://www.baeldung.com/jpa-criteria-api-in-expressions
I hope it could be useful. The code tested in case when Entry has only one #Id column:
public static <Entry, Id> List<Entry> getByIds(List<Id> ids, Class<Entry> clazz, EntityManager entityManager) throws CustomDatabaseException
{
List<Entry> entries;
try
{
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Entry> q = cb.createQuery(clazz);
Root<Entry> root = q.from(clazz);
EntityType<Entry> e = root.getModel();
CriteriaQuery<Entry> all = q.where(root.get(e.getId(e.getIdType().getJavaType()).getName()).in(ids));
TypedQuery<Entry> allQuery = entityManager.createQuery(all);
entries = allQuery.getResultList();
}
catch (NoResultException nre)
{
entries = Collections.emptyList()
}
catch (Exception e)
{
throw new CustomDatabaseException(e);
}
return entries;
}
Not to break your head when you need to do this again using Criteria API - you can create a parent wrapper class for your Dao and put a hepler method there:
/**
* An SQL "WHERE IN" expression alternative.
*
* #param inList List to search in.
* #param pathToEntityField Path to a field in your entity object.
* #return A ready predicate-condition to paste into CriteriaQuery.where().
*/
#SuppressWarnings("unchecked")
private Predicate in(List<?> inList, Expression pathToEntityField) {
return pathToEntityField.in(inList);
}
Use WHERE IN then like:
query.where(**in**(myList, root.get(MyTypedEntity_.id)));

Jpa Criteria API count

I've been trying to get total rows count and to do so, I've used JPA Criteria API but it throws an error at Long count = em.createQuery(sc).getSingleResult(); line and saying java.lang.IllegalStateException: No criteria query roots were specified . I've done some research but couldn't narrow the problem.
Here's my code snippet;
#PersistenceContext
public EntityManager em;
......
public Page<UserDTO> findByCriteria(String filters, Pageable pageable) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<UserDTO> cq = cb.createQuery(UserDTO.class);
Root<UserDTO> iRoot = cq.from(UserDTO.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (StringUtils.isNotEmpty(filters)) {
predicates.add(cb.like(cb.lower(iRoot.<String>get("login")), "%" + filters.toLowerCase() + "%"));
}
Predicate[] predArray = new Predicate[predicates.size()];
predicates.toArray(predArray);
CriteriaQuery<Long> sc = cb.createQuery(Long.class);
sc.select(cb.count(iRoot));
sc.where(predArray);
Long count = em.createQuery(sc).getSingleResult();
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<UserDTO> query = em.createQuery(cq);
Page<UserDTO> result = new PageImpl<UserDTO>(query.getResultList(), pageable, count);
return result;
}
EDITED AND WORKING CODE;
public Page<UserDTO> findByCriteria(String columnName, String filters, Pageable pageable) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<UserDTO> cq = cb.createQuery(UserDTO.class);
Root<UserDTO> iRoot = cq.from(UserDTO.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (StringUtils.isNotEmpty(filters)) {
predicates.add(cb.like(cb.lower(iRoot.<String>get(columnName)), "%" + filters.toLowerCase() + "%"));
}
Predicate[] predArray = new Predicate[predicates.size()];
predicates.toArray(predArray);
Long count = calculateCount(filters);
cq.where(predArray);
List<Order> orders = new ArrayList<Order>(2);
orders.add(cb.asc(iRoot.get("firstName")));
orders.add(cb.asc(iRoot.get("lastName")));
cq.orderBy(orders);
TypedQuery<UserDTO> query = em.createQuery(cq);
Page<UserDTO> result = new PageImpl<UserDTO>(query.getResultList(), pageable, count);
return result;
}
public Long calculateCount(String filters) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> sc = cb.createQuery(Long.class);
Root<UserDTO> iRoot = sc.from(UserDTO.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (StringUtils.isNotEmpty(filters)) {
predicates.add(cb.like(cb.lower(iRoot.<String>get("login")), "%" + filters.toLowerCase() + "%"));
}
Predicate[] predArray = new Predicate[predicates.size()];
predicates.toArray(predArray);
sc.select(cb.count(iRoot));
sc.where(predArray);
Long count = em.createQuery(sc).getSingleResult();
return count;
}
As anticipated in the comment, you need to explicitly add CriteriaQuery#from() method:
sc.from(UserDTO.class);
The from method is often not used because the API provider defaults to the entity class specified in the CriteriaQuery constructor. Not in this case however, because the class is a Long and it doesn't correspond to an entity class.
See also:
Oracle's JAVA EE Tutorial

How to get selected fields from table using hibernate (I am not using Criteria object but CriteriaQuery object so setProjection solution won't work)

I want to get selected fields from table with filters on the top of it.
For example :
If I want to filter my results on the basis of an attribute in the table named "created_on", I would do something like http://example.com/users/?created_on = 01-12-2016
I am achieving above task by doing this :
public List<User> searchUser(List<SearchCriteria> params
, int offset, int limit) {
Session session = mysqlConnectionFactory.getSession();
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root r = query.from(User.class);
Predicate predicate = builder.conjunction();
if (params != null) {
for (SearchCriteria param : params) {
if (param.getOperation().equalsIgnoreCase(">")) {
predicate = builder.and(predicate,
builder.greaterThanOrEqualTo(r.get(param.getKey()),
param.getValue().toString()));
} else if (param.getOperation().equalsIgnoreCase("<")) {
predicate = builder.and(predicate,
builder.lessThanOrEqualTo(r.get(param.getKey()),
param.getValue().toString()));
} else if (param.getOperation().equalsIgnoreCase(":")) {
if (r.get(param.getKey()).getJavaType() == String.class) {
predicate = builder.and(predicate,
builder.like(r.get(param.getKey()),
"%" + param.getValue() + "%"));
} else {
predicate = builder.and(predicate,
builder.equal(r.get(param.getKey()), param.getValue()));
}
}
}
query.where(predicate);
}
List<User> Users = session.createQuery(query)
.setFirstResult(offset).setMaxResults(limit).getResultList();
return Users;
}
Now I also wish to incorporate the functionality which will return selected fields for me, how can I do that?

Spring Data JPA with PageRequest and Predicate failing

Im using Spring data Jpa specification and I would like to obtain non repetead registries, so I use distinct in query like that.
public static Specification<Actividad> criteriosConsultaGruposHandling(final String cif, final String razonSocial, final String nombreComercial, final List<Long> idsHandling) {
Specification<Actividad> spec = new Specification<Actividad>() {
#Override
public Predicate toPredicate(Root<Actividad> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.distinct(true);
List<Predicate> predicates = new ArrayList<Predicate>();
if(cif != null && !cif.equals("")){
predicates.add(cb.like(cb.upper(root.join(Actividad_.empresa).get(Empresa_.cif)), "%" + cif.toUpperCase() + "%" ));
}
if(razonSocial != null && !razonSocial.equals("")){
predicates.add(cb.like(cb.upper(root.join(Actividad_.empresa).get(Empresa_.razonsocial)), "%" + razonSocial.toUpperCase() + "%" ));
}
if(nombreComercial != null && !nombreComercial.equals("")){
predicates.add(cb.like(cb.upper(root.join(Actividad_.empresa).get(Empresa_.nombrecomercial)), "%" + nombreComercial.toUpperCase() + "%" ));
}
if(idsHandling != null && !idsHandling.isEmpty()){
Predicate pred = cb.conjunction();
pred.getExpressions().add(root.join(Actividad_.grupohandlingactividads).get(Grupohandlingactividad_.grupohandling).get(Grupohandling_.idsubgrupo).in(idsHandling));
predicates.add(pred);
}
return cb.and(predicates.toArray(new Predicate[0]));
}
};
return spec;
}
}
but when I do the findAll with the specification and pagerequest the page resulting from query is bad
Specification<Actividad> specificationActividad = ActividadSpecifications.criteriosConsultaGruposHandling(actividadForm.getEmpresa().getCif(),
actividadForm.getEmpresa().getRazonsocial(), actividadForm.getEmpresa().getNombrecomercial(),listaGruposAsignados);
PageRequest pageRequest = new PageRequest(actividadForm.getPagina() -1, actividadForm.getMaxRegistros(), actividadForm.getOrder());
Page<Actividad> resultados = actividadRepository.findAll(specificationActividad, pageRequest);
When I call findAll without pageRequest parameter I obtain rows without repetitions but when I call it with it donĀ“t elimininate the repetitive rows.
Anyone could help me? Thanks in advance!

Categories