I'm stuck with a simple problem; struggling how to invoke order by on a joined entity. Essentially I am trying to achieve the following with JPA Criteria:
select distinct d from Department d
left join fetch d.children c
left join fetch c.appointments a
where d.parent is null
order by d.name, c.name
I have the following:
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Department> c = cb.createQuery(Department.class);
Root<Department> root = c.from(Department.class);
Fetch<Department, Department> childrenFetch = root.fetch(
Department_.children, JoinType.LEFT);
childrenFetch.fetch(Department_.appointments, JoinType.LEFT);
c.orderBy(cb.asc(root.get(Department_.name)));
c.distinct(true);
c.select(root);
c.where(cb.isNull(root.get(Department_.parent)));
How to achieve order by d.name, c.name with Criteria API? I tried with Expression, Path but didn't work.
Any pointers will be greatly appreciated.
If you need to add couple of orders you can make something like (but for your query and different root objects)
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Route> query = criteriaBuilder.createQuery(Route.class);
Root<Route> routeRoot = query.from(Route.class);
query.select(routeRoot);
List<Order> orderList = new ArrayList();
query.where(routeRoot.get("owner").in(user));
orderList.add(criteriaBuilder.desc(routeRoot.get("date")));
orderList.add(criteriaBuilder.desc(routeRoot.get("rating")));
query.orderBy(orderList);
I have the same problem with order by using Criteria API. I found this solution:
CriteriaQuery<Test> q = cb.createQuery(Test.class);
Root<Test> c = q.from(Test.class);
q.select(c);
q.orderBy(cb.asc(c.get("name")), cb.desc(c.get("prenom")));
I was having trouble doing the same, and I have found a solution on this page:
http://www.objectdb.com/api/java/jpa/criteria/CriteriaQuery/orderBy_Order_
//javax.persistence.criteria.CriteriaQuery
//CriteriaQuery<T> orderBy(Order... o)
Specify the ordering expressions that are used to order the query results. Replaces the previous ordering expressions, if any. If no ordering expressions are specified, the previous ordering, if any, is simply removed, and results will be returned in no particular order. The left-to-right sequence of the ordering expressions determines the precedence, whereby the leftmost has highest precedence.
Parameters:
o - zero or more ordering expressions
Returns:
the modified query
Since:
JPA 2.0
The solucion that work for me is the following
session=HibernateUtil.getSessionJavaConfigFactory_report().openSession();
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Object[]> criteriaQuery = builder.createQuery(Object[].class);
List<Order> orderList = new ArrayList();
orderList.add(builder.desc(ejeRoot.get("name")));
criteriaQuery.orderBy(orderList);
Note: ejeRoot is the class object
categoryRepository.findAll(predicates, new Sort(Direction.ASC, "sortOrder", "name"))
.forEach(categoryDtoList::add);
Related
I have a method in Query criteria which returns the "Covers" ordered by their generation date, but also includes those that have "re-entry" between those dates, the method returns the correct covers, but I need it to order them in a special way: If they have a "re-entry" tuple, it uses the re-entry date as order criteria, and if it does not have one, it uses the generation date as criteria. I need to join both criteria in a single order.
The method is the following:
public List<Cover> ListCover(Long startDate, Long finalDate) {
CriteriaBuilder criteria = em.getCriteriaBuilder();
CriteriaQuery<Cover> query = criteria.createQuery(Cover.class);
Root<Cover> cover = query.from(Cover.class);
Subquery<Long> miniQuery = query.subquery(Long.class);
Root<ReEntryCover> reEntryCover = miniQuery.from(ReEntryCover.class);
Predicate predicateMini1 = criteria.between(reEntryCover.get("reEntryDate"), startDate, finalDate);
Predicate predicateMini2 = criteria.equal(reEntryCover.get("coverNumberFK"), cover.get("coverNumber"));
Predicate predicateMiniFinal = criteria.and(predicateMini1, predicateMini2);
miniQuery.select(criteria.max(reEntryCover.get("reEntryDate"))).where(predicateMiniFinal);
Predicate predicate1 = criteria.between(cover.get("coverGenerationDate"), startDate, finalDate);
Predicate predicate2 = criteria.isNotNull(miniQuery);
Predicate predicateFinal = criteria.or(predicate1, predicate2);
query.select(cover).distinct(true).where(predicateFinal).orderBy(criteria.asc(cover.get("coverGenerationDate")));
return em.createQuery(query).getResultList();
}
In other words, I need to put a conditional inside the "order by" so that it doesn't order by generation date in case the cover has a tuple in the "re-entry" table, and instead use the "re-entry date" parameter like sort parameter (use "orderBy(criteria.asc(cover.get("coverGenerationDate"), criteria.asc(cover.get("reEntryDate"))" is not what I need since I want to use the reentry date (if any) as the generation date for ordering).
How is it possible to do this?
I tried to use:
1-
orderBy(criteria.asc(cover.get("coverGenerationDate"), criteria.asc(cover.get("reEntryDate"))
not working.
2-
multiSelect with differents predicates for 2 types of covers, I donĀ“t know how it works.
3-
2 querys is working, but is very slow, i need to do all in 1 query.
I'm trying to create a generic ordering and filtering abstraction on my backend, but some values on the web CRUD can be arbitrarily complex so, in some cases, we resorted to using subqueries. The query I'm trying to generate is in this format:
SELECT f, (SELECT COUNT(r.id) FROM Bar b WHERE b.foo = e) c
FROM Foo f
ORDER BY c
When creating the query I'm unable to reproduce that. The subquery is repeated in the WHERE clause, for some reason. This is the error I'm getting:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: query
[select generatedAlias0, (select count(generatedAlias1) from example.Bar as generatedAlias1
where generatedAlias0=generatedAlias1.foo) from example.Foo as generatedAlias0
order by (select count(generatedAlias1) from example.Bar as generatedAlias1
where generatedAlias0=generatedAlias1.foo) asc]
Excerpt from my query code:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> query = cb.createTupleQuery();
Root<T> root = query.from(Foo.class);
Subquery<Long> subquery = query.subquery(Long.class);
Root<?> subqueryRoot = subquery.from(Bar.class);
Expression<Long> count = cb.count(subqueryRoot);
subquery.select(count);
subquery.where(cb.equal(root, subqueryRoot.get("foo")));
query.multiselect(root, subquery.getSelection());
query.orderBy(cb.asc(subquery));
Even if you use CriteriaAPI you can take advantage of other alternatives for sorting. I have not managed to sort by the result of the subquery but you could do one of the following:
JAVA 8
return entityManager.createQuery(cq).getResultList().stream()
.sorted(Comparator.comparing(YourObject::getCount))
.collect(Collectors.toList());
ALL VERSIONS
List<YourObjet> list = entityManager.createQuery(cq).getResultList();
Collections.sort(list, new Comparator<YourObjet>(){
#Override
public int compare(YourObjet o1, YourObjet o2) {
return o1.getCount().compareToIgnoreCase(o2.getCount());
}
});
Sure there are more options, but Java 8 is really optimal
I want to join two tables and remove the on clause in join query to set a new custom on clause, however I always get a null predicate from "joinb" so a cant concatenate with more conditions.
My code below.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<myClass> query = cb.createQuery(myClass.class);
Root<a> a = query.from(a.class);
Join<a, b> joinb = a.join("b", JoinType.INNER);
Predicate joinBOn = b.getOn();
b.on(cb.or(joinBOn, cb.or("condition1", "condition2")));
The problem here is joinBOn is always null.
I'm new so I don't know what is happening.
As documented, getOn()
Return the predicate that corresponds to the ON restriction(s) on the
join, or null if no ON condition has been specified.
In this case there is clearly no ON condition specified, so null is expected. That is not an issue, because fetching current ON restriction is not necessary:
Predicate condition1 = ...
Predicate condition2 = ...
b.on(cb.or(condition1, condition2));
Spring-data, Oliver Gierke's excellent library, has something called a Specification (org.springframework.data.jpa.domain.Specification). With it you can generate several predicates to narrow your criteria for searching.
Can someone provide an example of using a Subquery from within a Specification?
I have an object graph and the search criteria can get pretty hairy. I would like to use a Specification to help with the narrowing of the search, but I need to use a Subquery to see if some of the sub-elements (within a collection) in the object graph meet the needs of my search.
Thanks in advance.
String projectName = "project1";
List<Employee> result = employeeRepository.findAll(
new Specification<Employee>() {
#Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Subquery<Employee> sq = query.subquery(Employee.class);
Root<Project> project = sq.from(Project.class);
Join<Project, Employee> sqEmp = project.join("employees");
sq.select(sqEmp).where(cb.equal(project.get("name"),
cb.parameter(String.class, projectName)));
return cb.in(root).value(sq);
}
}
);
is the equivalent of the following jpql query:
SELECT e FROM Employee e WHERE e IN (
SELECT emp FROM Project p JOIN p.employees emp WHERE p.name = :projectName
)
Here is what i am implementing.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> userRoot = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
Expression<String> userExpression = statusRoot.get("firstName");
for (String str : firstNames) {
predicates.add(cb.like(userExpression, "%" + str + "%"));
}
I want to apply like clause on list of values but its appending all Predicate with AND operator. please help.
Actually code given in example does not show use of predicates. That is where the problem is. I will assume it is equivalent to rather typical:
//conjunction, will combine with 'AND'
query.where(predicates.toArray(new Predicate[0]));
As documented, it will combine Predicates with AND:
Modify the query to restrict the query result according to the
conjunction of the specified restriction predicates
One way to combine Predicates with OR is simply using CriteriaBuilder.or:
//disjunction, will combine with 'OR'
Predicate firstNameLikeAnyOf = cb.or(predicates.toArray(new Predicate[0]));
query.where(firstNameLikeAnyOf);