I have the following criteria builder query
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object> critQuery = cb.createQuery();
Root<Role> role = critQuery.from(Role.class);
//create a join between role and permission
MapJoin<Role,String,Permission> perm = role.joinMap("permissions");
critQuery.multiselect(role.get("label"), perm.key(), perm.value());
//this line throws NPE
Query query = em.createQuery(critQuery);
The last line throws a null pointer exception.
java.lang.NullPointerException
at org.hibernate.ejb.criteria.path.AbstractPathImpl.prepareAlias(AbstractPathImpl.java:246)
at org.hibernate.ejb.criteria.path.AbstractPathImpl.render(AbstractPathImpl.java:253)
at org.hibernate.ejb.criteria.path.AbstractPathImpl.renderProjection(AbstractPathImpl.java:261)
This tutorial shows how to create INNER JOIN queries in JPA Criteria API. Generally speaking, INNER JOIN queries select the records common to the target tables. The method CriteriaQuery#from () returns a Root object. The Root interface extends From interface which has various methods to create objects which are equivalent to SQL JOINS.
In JPA Criteria API the map relations can be accessed via MapJoin interface. Following are two of the various methods of From interface (extended by Root interface) which can be used to obtain MapJoin instance:
The Criteria API is a predefined API used to define queries for entities. It is the alternative way of defining a JPQL query. These queries are type-safe, portable and easy to modify by changing the syntax i.e. the JPA queries are mainly used for building the dynamic queries whose exact structure is only known at the runtime.
Joining Tables with JPA Specifications We can observe from our data model that the Author entity shares a one-to-many relationship with the Book entity: The Criteria Query API allows us to join the two tables when creating the Specification. As a result, we'll be able to include the fields from the Book entity inside our queries:
For those stuck on Hibernate 3.6, I was able to join through a map and limit the results with a predicate. This solution comes with a significant caveat. This technique only works if the map key you're interested in exists for every record. If it does not, you will lose records that you might otherwise expect in the result set.
My case is very similar to Edwin's example. We have a Person entity with a map of current names where the map key is a name type and the value is a Name object. In my case, I wanted to retrieve a person's current standard name.
Root<Person> person = query.from(Person.class);
Join currentName = person.join("currentNames");
NameLookup standardName = NameLookup.lookup("ST");
Predicate useridMatches = criteria.equal(person.get("userid"), "user1");
Predicate isStandardName = criteria.equal(currentName.get("nameType"), standardName);
Predicate useridAndStandardName = criteria.and(useridMatches, isStandardName);
query.where(useridAndStandardName);
In JPQL, we typically use the WITH clause to limit the join.
inner join person.currentNames currentStandardName
with currentStandardName.nameType.id = :standardNameLookupId
I have exactly the same problem. After hours of dealing with the issue, and after debugging the Hibernate source code, and after checking over and over again the examples in books and in the JPA 2.0 Specification, I decided to give it a try in EclipseLink.
So, I created a very simple example: an employee with a map of phone numbers, where the key is the type of phone (home, office, mobile) and the value was the phone number.
@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(name="emp_phone")
@MapKeyColumn(name="phone_type")
@Column(name="phone_num")
private Map<String, String> phoneNumbers;
I could verify that this works perfectly with EclipseLink 2.1 and OpenJPA 2.1.0, but it fails in Hibernate 3.5.3, 3.6.1., 3.6.3
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> criteria = builder.createQuery(Employee.class);
Root<Employee> employeeRoot = criteria.from(Employee.class);
criteria.select(employeeRoot);
MapJoin<Employee, String, String> phoneRoot = employeeRoot.joinMap("phoneNumbers");
criteria.where(builder.equal(phoneRoot.key(), "HOME"));
System.out.println(entityManager.createQuery(criteria).getResultList());
I thought, well if Criteria API fails, perhaps I can do it with a named query. Interestingly, Hibernate does not support the KEY, VALUE or ENTRY keywords, and therefore queries proved to be malformed.
http://opensource.atlassian.com/projects/hibernate/browse/HHH-5396
This is what run:
String query = "SELECT e FROM Employee e JOIN e.phoneNumbers p WHERE KEY(p) IN ('HOME')";
System.out.println(entityManager.createQuery(query, Employee.class).getResultList());
In hibernate it generates the following SQL query:
select
employee0_.id as id0_,
employee0_.name as name0_
from
Employee employee0_
inner join
emp_phone phonenumbe1_
on employee0_.id=phonenumbe1_.Employee_id
where
KEY(phonenumbe1_.phone_num) in (
'HOME'
)
Which is evidently malformed.
Again, in EclipseLink and OpenJPA this works.
So, evidently, something must be wrong with Hibernate. I have submitted an bug in the Hibernate Jira Issue Tracker
http://opensource.atlassian.com/projects/hibernate/browse/HHH-6103
And have posted the question in the Hibernate Users Forum
https://forum.hibernate.org/viewtopic.php?f=1&t=1010411
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With