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));
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 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"));
Basically I am trying to select a field where I calculate with using a Postgres postgis query ST_distance_sphere.
Below code allows me to filter out results based on the distance. If the distance is greater than the given value record is filtered. I created the following query with the below predicate builder. However I also want to select the distance value returned by the ST_distance_sphere function as "distance"
Here is how I use JPQL:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<RestaurantEntity> criteriaQuery = criteriaBuilder.createQuery(RestaurantEntity.class).distinct(true);
Root<RestaurantEntity> restaurantEntityRoot = criteriaQuery.from(RestaurantEntity.class);
Predicate[] searchPredicates = createPredicates(criteriaBuilder, restaurantEntityRoot, restaurantSearch);
List<Order> orders = getSortingOrders(restaurantSearch, criteriaBuilder, restaurantEntityRoot);
criteriaQuery.where(searchPredicates).orderBy(orders);
List<RestaurantEntity> searchResults = entityManager.createQuery(criteriaQuery)
.setMaxResults(restaurantSearch.getSize())
.setFirstResult((restaurantSearch.getPage()) * restaurantSearch.getSize())
.getResultList();
Here is searchByDistance function and JpaRestaurantPredicateBuilder class I use.
class JpaRestaurantPredicateBuilder {
private CriteriaBuilder criteriaBuilder;
private Root<RestaurantEntity> restaurantEntityRoot;
private List<Predicate> predicateList;
JpaRestaurantPredicateBuilder(CriteriaBuilder criteriaBuilder, Root<RestaurantEntity> restaurantEntityRoot) {
this.criteriaBuilder = criteriaBuilder;
this.restaurantEntityRoot = restaurantEntityRoot;
this.predicateList = new ArrayList<>();
}
JpaRestaurantPredicateBuilder searchByDistance(Float lon, Float lat, Long maxDistance) {
Point userLocation = new GeometryFactory().createPoint(new Coordinate(lon, lat));
Join address = this.restaurantEntityRoot.join("addressEntity", JoinType.INNER);
predicateList.add(new WithinDistancePredicate((CriteriaBuilderImpl) criteriaBuilder, address.get("location"), userLocation, maxDistance));
return this;
}
Predicate[] buildArray() {
return predicateList.toArray(new Predicate[0]);
}
}
Here is createPredicates:
private Predicate[] createPredicates(CriteriaBuilder criteriaBuilder, Root<RestaurantEntity> restaurantEntityRoot, RestaurantSearch restaurantSearch) {
return new JpaRestaurantPredicateBuilder(criteriaBuilder, restaurantEntityRoot)
.chainId(restaurantSearch.getChainId())
.searchTerm(retrieveRestaurantIdsForSearchTerm(restaurantSearch.getSearchTerm()))
.cityIdAndDistrictId(restaurantSearch.getCityId(), restaurantSearch.getDistrictId())
.status(restaurantSearch.getStatus())
.searchByDistance(restaurantSearch.getLon(), restaurantSearch.getLat(), restaurantSearch.getMaxDistance())
.buildArray();
}
And here is the generated sql:
select distinct restaurant0_.id
from restaurant restaurant0_
inner join address addressent1_ on restaurant0_.address_id = addressent1_.id
where st_distance_sphere(addressent1_.location, ?) < 1000
order by restaurant0_.id desc
limit ?
Here is the sql I want to generate using JPA (note the line where I select the distance):
select distinct restaurant0_.id,
st_distance_sphere(addressent1_.location, ?) as distance
from restaurant restaurant0_
inner join address addressent1_ on restaurant0_.address_id = addressent1_.id
where st_distance_sphere(addressent1_.location, ?) < 1000
order by restaurant0_.id desc
limit ?
I couldn't find any other answers related with this question. How can I achieve this query using JPA? I tried to use CriteriaQuery's select method to no avail.
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%';