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
Related
I am tring to write a query to get few columns of a table along with total sum of a particular column. I am able to do so using sum() with over().
Example:
select id , amount, sum(amount) over ()
from orders
where status =0
However if I remove over(), I get the following error;
ERROR: column "orders.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 3: select id , amount, sum(amount)
The Java code using CriteriaQuery is like;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
Root<Orders> root = cq.from(Orders.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get("status"), 0));
cq.multiselect(root.get("id"), root.get("amount"), cb.sum(root.get("amount"))
.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
I am not able to find anything equivalent to over() to use in multiselect query. and getting same error;
ERROR: column "orders.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 3: select id , amount, sum(amount).
Please suggest how to fix this error and write CriteriaQuery with multiselect and aggregate functions.
I wanna know is there a way to do something like this in hibernate using criteriaBuilder
select users.first_name,orders.payable,order_item.product_title
from "order" orders
join users on orders.user_id_fk = users.id_pk
join order_item on orders.id_pk = order_id_fk
I need this specially if I have to use group by. I search and google and also read this article but have no clue how can I do this in hibernate:
Query Selection Other Than Entities
querycriteria
hibernate-facts-multi-level-fetching
also I code this for selecting field in first layer and it worked perfectly for selecting first layer but it need some change to work with join and select field from other table than root:
<R> List<R> reportEntityGroupBy(List<String> groupBy, List<String> selects, Class<T> root, Class<R> output) {
CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
CriteriaQuery<R> criteriaQuery = criteriaBuilder.createQuery(output);
Root<T> rootQuery = criteriaQuery.from(root);
if (selects == null || selects.isEmpty()) {
selects = groupBy;
}
criteriaQuery.multiselect(selects.stream().map(rootQuery::get).collect(Collectors.toList()));
criteriaQuery.groupBy(groupBy.stream().map(rootQuery::get).collect(Collectors.toList()));
I use Hibernate 5.4.22.Final and use entityGraph for my join.
I don't know how your selects look like, but I suppose you are using paths i.e. attributes separated by .. In that case, you have to split the paths and call Path#get for each attribute name.
List<Path> paths = selects.stream()
.map(select -> {
Path<?> path = rootQuery;
for (String part : select.split("\\.")) {
path = path.get(part);
}
return path;
})
.collect(Collectors.toList()));
criteriaQuery.multiselect(paths);
criteriaQuery.groupBy(paths);
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
)
I am implementing pagination using spring data and for sorting following query i want to prepare inside my specification.
SELECT *
FROM
(
SELECT distinct *
FROM
(
SELECT p.PROJECT_ID,
p.PROJECT_NAME,
p.PROJECT_TYPE
FROM PROJECT p
LEFT OUTER JOIN code c
ON p.codeId=c.ID
WHERE p.PROJECT_NAME IN ('test')
ORDER BY c.LABEL ASC
)
)
WHERE rownum <= 25;
but my specification is creating below script
SELECT *
FROM
(
SELECT distinct p.PROJECT_ID,
p.PROJECT_NAME,
p.PROJECT_TYPE
FROM PROJECT p
LEFT OUTER JOIN code c
ON p.codeId=c.ID
WHERE p.PROJECT_NAME IN ('test')
ORDER BY c.LABEL ASC
)
WHERE rownum <= 25;
Actually i am getting duplicate records after join so i am adding distinct function to fetch distinct records but as for order by clause to work the order by parameter should be present in select statement which is not possible in my case as in my entity it's not present neither i can change that.
So i just want to nest my select statement for project statement to another statement and there i want to put distinct as shown in the first script. I am very new to Spring jpa so any help would help.
Here is my specification.
public static Specification<Project> projectListSearchSpec(Set<String> deptNameList, ProjectSearchDto searchDTO) {
return new Specification<Project>() {
#Override
public Predicate toPredicate(Root<Project> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.distinct(true);
Predicate all = root.<Project>get("projectName").in(deptNameList);
return all;
}
};
}
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);