I was trying to help another user with a question related on how to transform SQL in criteria, when I stumbled across this problem: How to convert SQL Query using CriteriaQuery
For some reason the ParameterExpression
does not work with subqueries.
I used Hibernate 5.4.4.Final
for this.
I wonder whether this really a bug, or if you need to supply ParameterExpressions
for subqueries differently.
The entity:
@Entity
@Table(name="test")
public class Test {
@Id
private int id;
private String event;
private String name;
private int element_id;
// + getter & setter
}
The criteria query
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "test" );
EntityManager entityManager = entityManagerFactory.createEntityManager();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Test> criteriaQuery = cb.createQuery(Test.class);
Root<Test> root = criteriaQuery.from(Test.class);
Subquery<Integer> sub = criteriaQuery.subquery(Integer.class);
Root<Test> subRoot = sub.from(Test.class);
ParameterExpression<Integer> elementId = cb.parameter(Integer.class);
ParameterExpression<String> name1 = cb.parameter(String.class);
ParameterExpression<String> name2 = cb.parameter(String.class);
ParameterExpression<String> event = cb.parameter(String.class);
criteriaQuery.where(
cb.equal(subRoot.get(Test_.element_id), elementId),
cb.or(cb.notEqual(subRoot.get(Test_.name), name1),
cb.and(cb.equal(subRoot.get(Test_.name), name2),
cb.equal(subRoot.get(Test_.event), event))));
sub.select(cb.max(subRoot.get(Test_.id)));
criteriaQuery.where(cb.equal(root.get(Test_.id), sub));
criteriaQuery.select(root);
List<Test> result =
entityManager.createQuery(criteriaQuery)
.setParameter(elementId, 354)
.setParameter(name1, "foo")
.setParameter(name2, "bar")
.setParameter(event, "foo")
.getResultList();
When running above code on a normal query (for example the subquery only) it works, but with the subquery an exception is thrown:
Exception in thread "main" java.lang.NullPointerException
at org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter.setParameter(CriteriaQueryTypeQueryAdapter.java:392)
at org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter.setParameter(CriteriaQueryTypeQueryAdapter.java:61)
at <the first call of setParameter after creating the TypedQuery -> .setParameter(elementId, 354)>
When debugging I can see that the code goes to the org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter#explicitParameterInfoMap, but that one is empty so null is returned and the NPE is caused.
Edit:
this is not a discussion about the advantages of ParameterExpressions
, like
using a ParameterExpression versus a variable in JPA Criteria API, but instead a concrete question about how to use ParameterExpressions for subqueries so that hibernate can handle it. It might be that the answer to my question is that the usage is correct and that just the implementation is faulty.