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.

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 in Hibernate 4 and 5, I also mentioned one of the downsides of the 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 need to implement both of them and can’t use the ResultTransformer as a functional interface in lambda expressions.

This changed in Hibernate 6. The Hibernate team has split the ResultTransformer interface into the 2 functional interfaces: TupleTransformer and ResultListTransformer. You can set them by calling the setTupleTransformer and setResultListTransformer methods on Hibernate’s Query interface.

The Hibernate team also converted the ResultTransformer implementations provided by Hibernate 4 and 5 to TupleTransformer or ResultListTransformer implementations in Hibernate 6. Due to that, the required changes when migrating your application to Hibernate 6 should be minimal.

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

And as you can see in the following code snippet, the implementation of a custom transformer in Hibernate 6 is much more concise.

PersonDTO person = (PersonDTO) session
        .createQuery("select id as personId, first_name as firstName, last_name as lastName, city from Person p", Object[].class)
        .setTupleTransformer((tuples, aliases) -> {
                log.info("Transform tuple");
                PersonDTO personDTO = new PersonDTO();
                personDTO.setPersonId((int)tuples[0]);
                personDTO.setFirstName((String)tuples[1]);
                personDTO.setLastName((String)tuples[2]);
                return personDTO;
        }).getSingleResult();

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 got replaced by the functional interfaces TupleTransformer and ResultListTransformer in Hibernate 6.

The following list shows the 3 most commonly used ResultTransformers in Hibernate 4 and 5. These are still available in Hibernate 6 and now implement the TupleTransformer and/or ResultListTransformer interfaces.

  • 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 transformation:

  • 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.
  • In Hibernate 6, you need to implement the functional interfaces TupleTransformer or ResultListTransformer.