Featured Image with Sidebar

Hibernate’s ResultTransformer in Hibernate 4, 5 & 6

By Thorben Janssen


Hibernate implements JPA’s standardized constructor expressions and @SqlResultSetMappings to map the results of your queries. And it also supports proprietary ResultTransformers. They provide a powerful and flexible way to map the result of your JPQL, Criteria, and native SQL query to a specific object structure. This can be entity or DTO objects, java.util.List or java.util.Map representations of each record, or a custom data structure.

ResultTransformers were especially popular with Hibernate 4 but got deprecated in Hibernate 5. Unfortunately, Hibernate 5 doesn’t provide an alternative for them. Due to that, it might look like ResultTransformer would be removed in Hibernate 6. But that’s not the case!

Hibernate 6 will provide an improved version of ResultTranformers based on 2 functional interfaces. Based on the current code in the Hibernate 6 repository, migrating your existing ResultTransformer shouldn’t be a big deal.

ResultTransformer in Hibernate 4 and 5

Hibernate 4 and 5 include several build-in ResultTransformer. In addition, you can provide your own implementations. But before we talk about the different ResultTransformers, let me show you how to apply them to your query.

You only need to provide an instance of your ResultTransformer to the setResultTransformer method of Hibernate’s Query interface. If you’re using JPA’s EntityManager and Query interface, you need to unwrap them. If you unwrap the EntityManager, you get the associated Hibernate Session. If you unwrap JPA’s Query interface, you get Hibernate’s Query interface. I explained that in more detail in the Hibernate Tip: How to access Hibernate APIs from JPA.

In the following example, I tell Hibernate to apply the AliasToBeanResultTransformer to each record in the result set.

Query query = session.createQuery("select p.id as personId,p.firstName as firstName, p.lastName as lastName from Person p")
                .setResultTransformer(new AliasToBeanResultTransformer(PersonDTO.class));
        List<PersonDTO> personDTOS = query.list();

OK, let’s take a look at some of the commonly used ResultTransformers in Hibernate 4 and 5.

AliasToBeanResultTransformer

JPA enables you to map each record in your query result to an unmanaged DTO object. You can define these mappings using a constructor expression in JPQL or a @ConstructorResult for native queries. Both of these options require you to add a constructor to your DTO class that sets all attributes. This can be problematic if your DTO class has a huge number of attributes.

Hibernate’s AliasToBeanResultTransformer provides another way based on the bean specification. It uses the default constructor of the DTO class to instantiate a new object. In the next step, Hibernate uses reflection to call a setter method for each aliased value in the query. That makes it a great fit for DTOs that are implemented as a standard Java class but not as a Java record.

Query query = session.createQuery("select p.id as personId,p.firstName as firstName, p.lastName as lastName from Person p")
	.setResultTransformer(new AliasToBeanResultTransformer(PersonDTO.class));
List<PersonDTO> personDTOS = query.list();

In this example, the AliasToBeanResultTransformer uses the default constructor to instantiate a new PersonDTO object for each record returned by the query. In the next step, Hibernate calls the methods setPersonId, setFirstName, and setLastName with the values returned by the query.

ToListResultTransformer and AliasToEntityMapResultTransformer

If you don’t want to change the selected data and don’t have a matching DTO class, you can use Hibernate’s ToListResultTransformer or AliasToEntityMapResultTransformer. The ToListResultTransformer maps the Object[] returned by your query with all its elements to a java.util.List. The AliasToEntityMapResultTransformer transforms the query result to a java.util.Map that contains all aliased values of the result set. The alias of each value is used as the key of the Map.

Here you can see an example of the AliasToEntityMapResultTransformer. You can use the ToListResultTransformer in the same way.

Query selectPerson = session.createQuery(
	"Select p.id as id, " +
	"p.firstName as firstName, " +
	"p.lastName as lastName " +
	"from Person p")
	.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map> list = selectPerson.list();

Implementing your own ResultTransformer

If you want to implement your own ResultTransformer with Hibernate 4 or 5, you need to implement Hibernate’s ResultTransformer interface. That interface defines 2 methods: the transformTuple and the transformList methods.

A common ResultTransformer implementation implements the mapping of each record in the transformTuple method. The transformList method only returns the provided list of tuples.

I use that approach in the following code snippet to implement my own ResultTransformer that maps each record to a PersonDTO object.

Query query = session.createNativeQuery("select id as personId, first_name as firstName, last_name as lastName, city from Person p")
	.setResultTransformer(new ResultTransformer(){
            @Override
            public Object transformTuple(Object[] tuples, String[] aliases) {
                PersonDTO personDTO = new PersonDTO();
                personDTO.setPersonId((int)tuples[0]);
                personDTO.setFirstName((String)tuples[1]);
                personDTO.setLastName((String)tuples[2]);
                return personDTO;
            }

            @Override
            public List transformList(List list) {
                return list;
            }
        });
List<PersonDTO> list = query.list();

ResultTransformer in Hibernate 6

When I described the implementation of a custom ResultTransformer implementation, I also mentioned one of the downsides of the current ResultTransformer interface. It defines the transformTuple and transformList methods that both need to be implemented. Most applications implement only 1 of these 2 methods in a meaningful way. But because both methods are part of the interface definition, you can’t use the ResultTransformer as a functional interface in lambda expressions.

This will change in Hibernate 6. The Hibernate team has split the ResultTransformer interface into the 2 functional interfaces: TupleTransformer and ResultListTransformer.

Based on the ResultTransformer implementations provided by Hibernate 4 and 5, this will be a small but nice improvement. We will need to wait and see how this will affect the existing ResultTransformer implementations.

Conclusion

Hibernate’s ResultTransformers provide various ways to map the result of your query to different data structures. They were commonly used in Hibernate 4, got deprecated in Hibernate 5 and will be replaced by a functional interfaces in Hibernate 6.

Commonly used ResultTransformers for Hibernate 4 and 5 are:

  • AliasToBeanResultTransformer – Instantiates and sets attributes on DTO objects based on the alias defined in the query.
  • ToListResultTransformer- Maps each record in the query result to a java.util.List.
  • AliasToEntityMapResultTransformer – Maps the aliased values of each record in the query result to a java.util.Map.

You can also implement your own ResultTransformer:

  • In Hibernate 4 and 5, you need to implement the ResultTransformer interface and handle the mapping of each result set record in the transformTuple method.
  • Hibernate 6 splits the ResultTransformer interface into the functional interfaces TupleTransformer and ResultListTransformer.

About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Repl​​​​​y

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.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}