Question:

He used the InheritanceType.SINGLE_TABLE to map an inheritance hierarchy to one database table. In one of his queries, he wanted to return the same DTO projection for different subentities of the hierarchy. Depending on the subentity class, he wanted to initialize different properties of the DTO object.

The question was: How do you implement a query that adapts the constructor call to different subentities?

Don’t want to read? You can watch it here!

Solution:

To be honest, the definition of the query was more complex than I expected. I first thought that I just needed to implement 2 different constructors and then use a CASE expression with JPQL‘s TYPE function to distinguish between the 2 subclasses.

But unfortunately, the combination of CASE expression, TYPE function, and constructor expression caused a strange error message with Hibernate 5.4.

In the end, I had to implement one constructor with parameters for all properties of the DTO and use a CASE expression for each property that wasn’t mapped by the superclass.

Let’s take a look at a simplified example, as I obviously can’t share any client’s code.

Entities and DTO

I created a small inheritance hierarchy consisting of a Publication as the superclass and the subclasses Book and BlogPost.

And I will use the PublicationPresentationValue class as the projection of my query. As you can see in the diagram, the constructor expects 4 parameters with the values for the id, title, numPages and url properties. Id and title are mapped by the Publication entity and will be set for all PublicationPresentationValue objects. The numPages property is specific for Book entities and will be null for all BlogPosts. The url property will only be set for publications of type BlogPost.

Subentity-specific constructor calls in JPQL

As I explained at the beginning of this Hibernate Tip, the combination of CASE expression, TYPE function, and constructor expression didn’t work with Hibernate 5.4. I had to use a constructor with parameters for all properties of the DTO instead. You can see the query here.

The query starts with a simple constructor expression that tells Hibernate to instantiate a PublicationPresentationValue object for each record. The interesting parts are the following lines of the query.

Line 2 starts with a CASE expression that I use to determine the 3rd constructor parameter. A CASE expression is similar to an if clause in Java. It evaluates a when_clause, which in this query is TYPE(p). The TYPE function returns the type of the selected entity. In this example, that’s either a Book or a BlogPost. If it’s a Book, I call the TREAT function to cast p to a Book entity and reference the numPages attribute. BlogPost entities don’t have a numPages attribute, and I return null instead.

Line 3 is very similar to the previous one. This time, I either want to return the url attribute of a BlogPost entity or null, if it’s a Book entity. So, I again use the TYPE function to get the class of the current entity and the TREAT function to cast it to a BlogPost entity.

As you can see, even so, JPQL is not as powerful as SQL, it still enables you to create quite complex queries. If you run this query and activate the SQL statement logging, you can see that Hibernate generates the following SQL statement.