I have the following entity structure:
   +-----------+                +-------------+        
   |  User     | -------------> |    Role     |
   +-----------+                +-------------+
                                       ^
                                       |
                               +-------+---------+
                               |                 |
                        +-----------+      +-----------+           +------------+
                        |   Role1   |      |  Role2    |-------->  | SomeEntity |
                        +-----------+      +-----------+           +------------+
and I want to get the names of all User whose Role2 has a SomeEntity with a specific property value. I need to do this with the JPA criteria API.
What I did so far is:
CriteriaBuilder cb = ...
CriteriaQuery<String> query = cb.createQuery(String.class);
Root<User> user = query.from(User.class);
SetJoin<User, Role> userRolesJoin = user.join(User_.roles);
// As you can see the userRolesJoin is of type Role and a Role doesn't have
// an property someEntity. So how to "cast" the userRolesJoin into an 
// SetJoin<User, Role2>.
How to do the polymorphic query here? Any suggestions?
Unfortunatly the JPA criteria API is not as intuitive as Hibernate's critera API.
I created a subquery based on the Role2 type, joined it with the "SomeEntity" entity and applied the predicates. Then I connected the subquery to the "main" query using the Role2.ids that match the subquery predicates.
CriteriaBuilder cb = ...
CriteriaQuery<String> query = cb.createQuery(String.class);
Root<User> user = query.from(User.class);
SetJoin<User, Role> userRolesJoin = user.join(User_.roles);
Path<String> roleIdPath = userRolesJoin.get(User_.id);
Subquery<String> subquery = query.subquery(String.class);
Root<Role2> role2Root = subquery.from(Role2.class);
Join<Role2, SomeEntity> someEntityJoin = role2Root.join(Role2_.someEntity);
Path<String> someEntityPropertyPath = someEntityJoin.get(SomeEntity_.aProperty);
Predicate someEntityPropertyPredicate = cb.equal(someEntityPropertyPath,
            "a property value");
subquery.select(role2Root.get(Role2_.id));
subquery.where(someEntityPropertyPredicate);
In<String> idInSubqueryIds = cb.in(roleIdPath).value(subquery);
query.select(user.get(User_.username));
query.where(idInSubqueryIds);
EntityManager entityManager = ...;
TypedQuery<String> query = entityManager.createQuery(query);
List<String> usernames = query.getResultList();
                        You can use as() in Criteria to cast.
((Path)user.join(User_.roles).as(Role2.class)).join(Role2_.someEntity)
This may be provider specific previous to JPA 2.1, but I think it is standard in JPA 2.1.
In JPQL you can use the TREAT operation.
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Special_Operators
You can also use a second from(),
Root<Role2> role2 = query.from(Role2.class);
... cb.equal(user.join(User_.roles), role2)
                        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