I have orders (id, name) with oneToMany to items (id, name):
How can I fetch only these orders which have items of name 'banana' with a CriteriaBuilder:
#Override
public Order orderQuery() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = cb.createQuery(Order.class);
Root root = criteriaQuery.from(Order.class);
root.fetch("items", JoinType.INNER);
CriteriaQuery d = criteriaQuery.select(root);
criteriaQuery.where(cb.equal(root.get("item.name"), "banana"));
return (Order)this.entityManager.createQuery(criteriaQuery).getSingleResult());
}
I get the error:
Unable to locate Attribute with the the given name [item.name] on
You need to use a Join. Something around these lines:
Join< Order ,Item> joinItems = root.join("items");
criteriaQuery.where(cb.equal(joinItems.get("item.name"), "banana"));
alternativly you can do something like:
criteriaQuery.where(cb.equal(root.get("items").get("name"), "banana"));
Related
I have two tables A and B, A has B's foreign key, with the old Criteria API everything works fine however with the CriteriaQuery API I'm getting an error. What different is happening behind the scenes? To me it's logical that these two pieces of code should do the same thing.
Error
Column 'A.PK' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
This works:
Criteria criteria = getSession().createCriteria(A.class);
criteria.setProjection(Projections.groupProperty("b"));
return criteria.list();
this doesn't:
Session session = getSession();
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<A> criteriaQuery = builder.createQuery(A.class);
Root<A> root = criteriaQuery.from(A.class);
criteriaQuery.groupBy(root.get("b"));
return session.createQuery(criteriaQuery).list();
Assuming that you have the following mapping:
#Entity
class A {
#ManyToOne
private B b;
}
#Entity
class B {
#Id
private Long id;
}
Outdated Criteria:
Criteria criteria = getSession().createCriteria(A.class);
criteria.setProjection(Projections.groupProperty("b"));
List<B> result = criteria.list();
actually execute the query like below:
select b_id
from A
group by b_id
and then fetch B's one by one by executing query like this:
select * from B where id = ?
When you use CriteriaBuilder hibernate actually tries to fetch data by one query. To fix the query you can do something like this:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<B> criteriaQuery = builder.createQuery(B.class);
Root<A> root = criteriaQuery.from(A.class);
Join<A, B> bJoin = root.join("b");
criteriaQuery.groupBy(bJoin.get("id"));
criteriaQuery.select( root.get("b") );
List<B> result = session.createQuery(criteriaQuery).list();
I have a scenario where I need to get the row with maximum date. The SQL query for this would be
SELECT c.*
FROM course c
INNER JOIN
(SELECT moduleId ,MAX(endDate) AS max_date
FROM course
WHERE moduleId = 12345
GROUP BY moduleId
) customSelect
ON customSelect.moduleId = c.moduleId AND c.endDate = customSelect.max_date
WHERE c.moduleId = 12345
I need to convert this query to JPA using CriteriaBuilder.Since this isn't a direct entity join, rather a selection join to root entity, I'm having trouble to figure out how to join the custom select part to the root entity Course with below syntax:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Course> criteriaQuery = builder.createQuery(Course.class);
Root<Course> root = criteriaQuery.from(Course.class);
And then how to root.join(JoinType.INNER) to join customSelect part?
If someone can show some pointers to the syntax of of joining the root to a custom selection, that would be great.
Try this
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Course> criteriaQuery = builder.createQuery(Course.class);
Root<Course> root = criteriaQuery.from(Course.class);
criteriaQuery.select(root).where(cb.equal(root.get(Course_.moduleId), 12345));
criteriaQuery.orderBy(cb.desc(r.get(Course_.endDate)));
TypedQuery<Course> query = em.createQuery(criteriaQuery);
query.setMaxResult(1);
Course result = query.getSingleResult();
I am trying to get the total row count based on the CriteriaQuery but got an exception
org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.package.id' [select count(generatedAlias0) from com.test.Product as generatedAlias0 where ( generatedAlias1.package.id like :param0 )]
Code
CriteriaBuilder cb = session().getCriteriaBuilder();
CriteriaQuery<Product> query = cb.createQuery(Product.class);
Root<Product> entity = query.from(Product.class);
query.where(where_clause);
CriteriaQuery<Long> queryCount = cb.createQuery(Long.class);
Root<Product> entity = queryCount.from(query.getResultType());
queryCount.where(query.getRestriction()) -- this is where the problem is creating
Entity
class Product{
Package package;
int quantity;
/// getter setter method
}
class Package{
String id;
String name;
String type
/// getter setter method
}
mapping is done using hbm xml file.
can you please let me know how to fix it ?
You have two different queries. So you can't use the same predicate for both of them, because they have different roots.
org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path:
'generatedAlias1.package.id' [select count(generatedAlias0) from
com.test.Product as generatedAlias0 where ( generatedAlias1.package.id
like :param0 )]
generatedAlias1 is alias for Product from another query
To make the predicate reusable you should create method returns predicate
Predicate getPredicate(Root<Product> root, CriteriaBuilder builder, Parameter param) {
// returns predicate using root, builder and param you need
return builder.equal(root.get("fieldName"), param);
}
And then use it in queries
CriteriaBuilder cb = session().getCriteriaBuilder();
CriteriaQuery<Product> query = cb.createQuery(Product.class);
Root<Product> entity = query.from(Product.class);
query.where(getPredicate(entity, cb, param));
CriteriaBuilder cbCount = session().getCriteriaBuilder();
CriteriaQuery<Long> queryCount = cbCount.createQuery(Long.class);
Root<Product> entityCount = queryCount.from(Product.class);
queryCount.where(getPredicate(entityCount, cbCount, param));
Before Hibernate 5 deprecated the Criteria class, you could add restrictions to a Criteria to act as a constraint, and projections to act as select statements, like so
Criteria criteria = session.createCriteria(T.class)
.add(Restrictions.or(Restrictions.eq(property, constraintValue)
.set(Projection(Projections.projectionList()
.add(Projections.property(selectValue)));
But, since you now need to use CriteriaQuery like so,
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(T.class);
Root<T> root = criteriaQuery.from(T.class);
criteriaQuery.select(root);
Query<T> query = session.createQuery(criteriaQuery);
However I have been unable to figure out how to add certain things which are required in SQL statements, mainly because searching for documentation tends to wind up on documentation about Criteria, due to the naming similarity.
So, how can I recreate a simple query, like the one below, using CriteriaQuery?
SELECT selectValue
FROM tables.T
WHERE property = constraintValue
Source.
Has multiple examples, but turns out that the simple select statement we were trying to recreate can be done like so:
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<SELECTVALUETYPE> criteriaQuery = criteriaBuilder.createQuery(SELECTVALUETYPE.class);
Root<PARENTCLASS> root = criteriaQuery.from(PARENTCLASS.class);
criteriaQuery.select(root);
criteriaQuery.where(criteriaBuilder.equal(root.get(property), constraintValue));
Query<SELECTVALUETYPE> query = session.createQuery(criteriaQuery);
Note that this is a generic answer, and won't actually run. The reason being, SELECTVALUETYPE needs to be replaced with the data type of selectValue.
For example, CriteriaQuery might become:
String selectValue -> CriteriaQuery
T selectValue -> CriteriaQuery
Therefore, a working example for the statement
Select name
From Users
Where ID = 1
Could be expressed with the following block
int ID = 1;
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<String> criteriaQuery = criteriaBuilder.createQuery(String.class);
Root<User> root = criteriaQuery.from(User.class);
criteriaQuery.select(root.get("name");
criteriaQuery.where(criteriaBuilder.equal(root.get("ID"), ID));
Query<String> query = session.createQuery(criteriaQuery);
List<String>results = query.getResultList();
for(String name : results){
System.out.println("Name: " + name);
}
I'm trying to crete Criteria API query with CONTAINS function(MS SQL):
select * from com.t_person where contains(last_name,'xxx')
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> root = cq.from(Person.class);
Expression<Boolean> function = cb.function("CONTAINS", Boolean.class,
root.<String>get("lastName"),cb.parameter(String.class, "containsCondition"));
cq.where(function);
TypedQuery<Person> query = em.createQuery(cq);
query.setParameter("containsCondition", lastName);
return query.getResultList();
But getting exception:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node:
Any help?
If you want to stick with using CONTAINS, it should be something like this:
//Get criteria builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Create the CriteriaQuery for Person object
CriteriaQuery<Person> query = cb.createQuery(Person.class);
//From clause
Root<Person> personRoot = query.from(Person.class);
//Where clause
query.where(
cb.function(
"CONTAINS", Boolean.class,
//assuming 'lastName' is the property on the Person Java object that is mapped to the last_name column on the Person table.
personRoot.<String>get("lastName"),
//Add a named parameter called containsCondition
cb.parameter(String.class, "containsCondition")));
TypedQuery<Person> tq = em.createQuery(query);
tq.setParameter("containsCondition", "%näh%");
List<Person> people = tq.getResultList();
It seems like some of your code is missing from your question so I'm making a few assumptions in this snippet.
You could try using the CriteriaBuilder like function instead of the CONTAINS function:
//Get criteria builder
CriteriaBuilder cb = em.getCriteriaBuilder();
//Create the CriteriaQuery for Person object
CriteriaQuery<Person> query = cb.createQuery(Person.class);
//From clause
Root<Person> personRoot = query.from(Person.class);
//Where clause
query.where(
//Like predicate
cb.like(
//assuming 'lastName' is the property on the Person Java object that is mapped to the last_name column on the Person table.
personRoot.<String>get("lastName"),
//Add a named parameter called likeCondition
cb.parameter(String.class, "likeCondition")));
TypedQuery<Person> tq = em.createQuery(query);
tq.setParameter("likeCondition", "%Doe%");
List<Person> people = tq.getResultList();
This should result in a query similar to:
select p from PERSON p where p.last_name like '%Doe%';