The best way to map a projection query to a DTO (Data Transfer Object) with JPA and Hibernate
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
In this article, I’m going to show you the best way to map a DTO projection when using JPA and Hibernate.
While answering questions on the Hibernate forum, I stumbled on the following question. What was different than previous questions on the same topic was that the original poster realized the shortcoming of the JPA constructor expression which can be addressed elegantly by Hibernate.
Because this has been a recurring theme on StackOverflow and the Hibernate forum, I decided to dedicate an article to the best way of mapping DTO projections using JPA and Hibernate.
Domain Model
Considering we have the following Post
entity:
As previously explained, fetching entities only makes sense if you plan to modify them. If you are only interested in a DTO projection, it’s more efficient to select only the columns that are really needed by the caller.
Assuming we want to select just the id
and the title
from our Post
entities, it would be a waste of resources to select an entire Post
entity, so let’s see how you could achieve this goal using JPA and Hibernate.
DTO projections using JPA
When using JPA or Hibernate, you can execute both entity queries via JPQL or Criteria API or native SQL queries.
DTO projection using JPA Tuple and JPQL
If you don’t want to supply a DTO class for your projection, you can use the JPA Tuple
.
So, to use Tuple
projection, your JPQL query looks as follows:
List<Tuple> postDTOs = entityManager.createQuery(""" select p.id as id, p.title as title from Post p where p.createdOn > :fromTimestamp """, Tuple.class) .setParameter( "fromTimestamp", Timestamp.from( LocalDate.of(2020, 1, 1) .atStartOfDay() .toInstant(ZoneOffset.UTC) ) ) .getResultList(); assertFalse(postDTOs.isEmpty()); Tuple postDTO = postDTOs.get(0); assertEquals( 1L, postDTO.get("id") ); assertEquals( "High-Performance Java Persistence", postDTO.get("title") );
As you can see, the
Tuple
is a convenient way of fetching DTO projections as you don’t require to specify a DTO class for every type of projection that needs to be supported.
DTO projections using a Constructor Expression and JPQL
If you don’t want to use a Tuple
because you want the DTO projection to use a specific class, you can use a Constructor Expression by specifying the NEW
keyword along with the fully-qualified name of the class representing the DTO projection and the list of attributes that will be passed as constructor arguments.
The DTO class must provide a constructor that takes all the attributes fetched by the result set projection.
So, the DTO projection must look as follows:
public class PostDTO { private Long id; private String title; public PostDTO(Number id, String title) { this.id = id.longValue(); this.title = title; } public Long getId() { return id; } public String getTitle() { return title; } }
Therefore, the constructor expression JPQL query looks as follows:
List<PostDTO> postDTOs = entityManager.createQuery(""" select new com.vladmihalcea.book.hpjp.hibernate.forum.dto.PostDTO( p.id, p.title ) from Post p where p.createdOn > :fromTimestamp """, PostDTO.class) .setParameter( "fromTimestamp", Timestamp.from( LocalDate.of(2020, 1, 1) .atStartOfDay() .toInstant(ZoneOffset.UTC) ) ) .getResultList();
You can omit the package name for the construction expression if you follow the steps provided in this article.
DTO projections using Tuple and native SQL queries
Starting from Hibernate ORM 5.2.11, because the HHH-11897 Jira issue got fixed, you can use Tuple
for native SQL queries.
List<Tuple> postDTOs = entityManager.createNativeQuery(""" SELECT p.id AS id, p.title AS title FROM Post p WHERE p.created_on > :fromTimestamp """, Tuple.class) .setParameter( "fromTimestamp", Timestamp.from( LocalDate.of(2020, 1, 1) .atStartOfDay() .toInstant(ZoneOffset.UTC) ) ) .getResultList(); assertFalse(postDTOs.isEmpty()); Tuple postDTO = postDTOs.get(0); assertEquals( 1L, postDTO.get("id") ); assertEquals( "High-Performance Java Persistence", postDTO.get("title") );
DTO projections using a ConstructorResult
For native SQL queries, you can no longer use a Constructor Expression, so you need to use a named native query and configure a given SqlResultSetMapping
so that you can populate the DTO class either via its constructor or its fields.
If we use the same PostDTO
class type introduced previously, we have to provide the following SqlResultSetMapping
:
@NamedNativeQuery( name = "PostDTO", query = """ SELECT p.id AS id, p.title AS title FROM Post p WHERE p.created_on > :fromTimestamp """, resultSetMapping = "PostDTO" ) @SqlResultSetMapping( name = "PostDTO", classes = @ConstructorResult( targetClass = PostDTO.class, columns = { @ColumnResult(name = "id"), @ColumnResult(name = "title") } ) )
Now, the SQL projection named native query is executed as follows:
List<PostDTO> postDTOs = entityManager .createNamedQuery("PostDTO") .setParameter( "fromTimestamp", Timestamp.from( LocalDateTime.of(2020, 1, 1, 0, 0, 0) .toInstant(ZoneOffset.UTC) ) ) .getResultList();
For more details about the best way to use the JPA
SqlResultSetMapping
annotation, you should read this article.
YouTube Video
I also published a YouTube video about JPA projections, so if you enjoy the topic, you are going to love this video episode:
DTO projections using Hibernate
While you can use all the JPA features with Hibernate, there are many more features Hibernate has to offer than the standard Java Persistence specification.
DTO projections using ResultTransformer and JPQL
As previously explained, the ResultTransformer
allows you to customize the result set any way you like so you can use it to transform the typical Object[]
array projection into a DTO result set.
This time, you don’t need to provide a constructor to match the entity attributes being selected by the query.
Although you don’t even have to provide setters in your DTO class, here, we need the setter because
BigInteger
might be returned for theid
database column while we need it to be cast as aLong
.Hibernate can set the appropriate fields using Reflection, so it’s more flexible than the previous JPA Constructor Expression alternative.
Considering we have the following DTO class:
public class PostDTO { private Long id; private String title; public Long getId() { return id; } public void setId(Number id) { this.id = id.longValue(); } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
We can transform the result set using the setResultTransformer
method of the Hibernate-specific org.hibernate.query.Query
interface which you can unwrap from the JPA Query
.
List<PostDTO> postDTOs = entityManager.createQuery(""" select p.id as id, p.title as title from Post p where p.createdOn > :fromTimestamp """) .setParameter( "fromTimestamp", Timestamp.from( LocalDateTime.of(2020, 1, 1, 0, 0, 0) .toInstant(ZoneOffset.UTC) ) ) .unwrap(org.hibernate.query.Query.class) .setResultTransformer(Transformers.aliasToBean(PostDTO.class)) .getResultList();
DTO projections using ResultTransformer and a Native SQL query
If you want to use a native SQL query, you don’t need to go through all the trouble of declaring a SqlResultSetMapping
since you can use the AliasToBeanResultTransformer
just like it was the case for the aforementioned JPQL example.
List<PostDTO> postDTOs = entityManager.createNativeQuery(""" select p.id as "id", p.title as "title" from Post p where p.created_on > :fromTimestamp """) .setParameter( "fromTimestamp", Timestamp.from( LocalDateTime.of(2020, 1, 1, 0, 0, 0) .toInstant(ZoneOffset.UTC) ) ) .unwrap(org.hibernate.query.NativeQuery.class) .setResultTransformer(Transformers.aliasToBean(PostDTO.class)) .getResultList();
Cool, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
As you can see, there are multiple options to generate a DTO projection with JPA and Hibernate. Although the JPA specification offers both the Constructor Expression and the Tuple
result, the ResultTransformer
can be a much more flexible alternative.

Thanks. The article is very helpfull!
Though every time i see there are 8 ways to do something which seems relatively simple, i think something is wrong with the technology.
I’m glad you liked it.
There’s a very good reason why there are so many options. It’s because they fit different scenarios.