8

JPA 2.0 provided a means to filter by subclass using the JPQL expressions TYPE, for example :

SELECT e
FROM entity e
WHERE TYPE(e) = :entityType

where parameter entityType would be the value of the discriminator column.

What is the recommended way for achieving the same thing with the JPA criteria builder, considering the discriminator column appears to be off limits?

I'm using JPA 2.1 and so far the only solution that seems adequate is mapping the discriminator column as a read only field within the Java entity, but I'm not sure if this is a supported feature.

ooozguuur
  • 3,396
  • 2
  • 24
  • 42
Grant Lay
  • 800
  • 7
  • 17

3 Answers3

12

Examples of common queries are here

Example:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery();
Root e = cq.from(Entity.class);
cq.where(cb.equal(e.type(), entityType));
Query query = em.createQuery(cq);
List<Entity> result = query.getResultList();
ooozguuur
  • 3,396
  • 2
  • 24
  • 42
5

You can use Path type method as documented here:

Predicate p = cb.equal(e.type(), cb.literal(Entity.class));
Matteo Baldi
  • 5,613
  • 10
  • 39
  • 51
1

If you just want to check the exact type of an expression, use Path.type() as described above.

But if you want the Criteria API equivalent of Java's instanceof operator, the only way to do it is by explicitly listing all of the sub-types Class objects. But this makes your code brittle against future refactoring, etc.

Here's a utility class providing a JPAUtil.instanceOf() method that solves that problem:

/**
 * JPA utility methods.
 */
public final class JPAUtil {

    private JPAUtil() {
    }

    /**
     * Build an IN predicate from a collection of possible values.
     */
    public static <T> Predicate in(CriteriaBuilder builder, Expression<T> expr, Iterable<? extends T> values) {
        final CriteriaBuilder.In<T> in = builder.in(expr);
        values.forEach(in::value);
        return in;
    }

    /**
     * Build an "instanceof" predicate.
     */
    @SuppressWarnings("unchecked")
    public static Predicate instanceOf(EntityManager entityManager, Path<?> path, Class<?> type) {
        final Set<Class<?>> subtypes = entityManager.getMetamodel().getManagedTypes().stream()
          .map(ManagedType::getJavaType)
          .filter(type::isAssignableFrom)
          .collect(Collectors.toSet());
        return JPAUtil.in(entityManager.getCriteriaBuilder(), (Expression<Class<?>>)path.type(), subtypes);
    }
}
Archie
  • 4,959
  • 1
  • 30
  • 36