How to query by entity type using JPA Criteria API
Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.
So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!
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());
Writing JPA Criteria API queries is not very easy. The Codota IDE plugin can guide you on how to write such queries, therefore increasing your productivity.
For more details about how you can use Codota to speed up the process of writing Criteria API queries, check out this article.
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?
I'm running an online workshop on the 11th of October about High-Performance SQL.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.
