How to query by entity type using JPA Criteria API

(Last Updated On: August 7, 2018)

Introduction

Inspired by this Hibernate forum post, I decided to write an article to explain how you can filter by the entity type using Criteria API.

Domain Model

Let’s assume that our application uses the following entity hierarchy:

To persist this entity hierarchy, we can use JPA inheritance, and, as explained in this article, the SINGLE_TABLE inheritance strategy is a very good default choice.

As its name suggests, the SINGLE_TBALE inheritance uses a single topic table to hold both base class and subclass entities belonging to this inheritance hierarchy:

For the next queries, consider e have the following 2 entities in the database:

Post post = new Post();
post.setOwner("Vlad");
post.setTitle("Inheritance");
post.setContent("Best practices");

entityManager.persist(post);

Announcement announcement = new Announcement();
announcement.setOwner("Vlad");
announcement.setTitle("Release x.y.z.Final");
announcement.setValidUntil(
    Timestamp.valueOf(LocalDateTime.now().plusMonths(1))
);

entityManager.persist(announcement);

Polymorphic queries

One feature provided by JPA inheritance is the ability to fetch entities by their associated base class. This is called a polymorphic query, and the following query selects both the Post and
Announcement entities:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Topic> criteria = builder.createQuery(Topic.class);

Root<Topic> root = criteria.from(Topic.class);

criteria.where(
    builder.equal(root.get("owner"), "Vlad")
);

List<Topic> topics = entityManager
.createQuery(criteria)
.getResultList();

assertEquals(2, topics.size());

Subclass filtering

Now, if you want to select only the Post subclass, you can change the query Root like this:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Post> criteria = builder.createQuery(Post.class);
Root<Post> root = criteria.from(Post.class);

criteria.where(
    builder.equal(root.get("owner"), "Vlad")
);

List<Post> posts = entityManager
.createQuery(criteria)
.getResultList();

assertEquals(1, posts.size());

When running this query, Hibernate will generate the following SQL query:

SELECT t.id AS id2_0_,
       t.createdOn AS createdO3_0_,
       t.owner AS owner4_0_,
       t.title AS title5_0_,
       t.content AS content6_0_
FROM   topic t
WHERE  t.DTYPE = 'Post'
  AND  t.owner = 'Vlad'

Notice that the DTYPE column is used to filter only the Post entities.

However, if we want to modify the first query so that we can dynamically filter the Topic entities by the subclass type, we can use the type method of the Path Criteria API class for this task:

Class<? extends Topic> sublcass = Post.class;

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Topic> criteria = builder.createQuery(Topic.class);
Root<Topic> root = criteria.from(Topic.class);

criteria.where(
    builder.and(
        builder.equal(root.get("owner"), "Vlad"),
        builder.equal(root.type(), sublcass)
    )
);

List<Topic> topics = entityManager
.createQuery(criteria)
.getResultList();

assertEquals(1, topics.size());

Cool, right?

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

The Criteria API provides many path expressions which allow you to build all sorts of entity queries dynamically. The type property of a given Path expression can be used to filter entities by their associated class.

Subscribe to our Newsletter

* indicates required
10 000 readers have found this blog worth following!

If you subscribe to my newsletter, you'll get:
  • A free sample of my Video Course about running Integration tests at warp-speed using Docker and tmpfs
  • 3 chapters from my book, High-Performance Java Persistence, 
  • a 10% discount coupon for my book. 
Get the most out of your persistence layer!

Advertisements

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.