The best way to use the Hibernate TupleTransformer

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 explain how the Hibernate TupleTransformer works and how to use it to replace the deprecated ResultTransformer.

The deprecated ResultTransformer

Prior to using Hibernate 6, the ResultTransformer was the default option for transforming a query result set.

However, since 5.2, the setResultTransformer method of the Hibernate Query interface was deprecated even if no alternative was provided at the time. The reason why this method and its associated ResultTransformer interface were deprecated was that the ResultTransformer interface was not a functional interface.

A functional interface provides a single abstract method and can be used when defining a lambda expression. For this reason, the legacy ResultTransformer interface was split into two interfaces TupleTransformer and ResultListTransformer, as illustrated in the following diagram:

TupleTransformer ResultListTransformer ResultTransformer

Hibernate TupleTransformer

The TupleTransformer defines the transformTuple method, as follows:

@Incubating
@FunctionalInterface
public interface TupleTransformer<T> {
    /**
     * Tuples are the elements making up each "row" of the query result.
     * The contract here is to transform these elements into the final
     * row shape.
     *
     * @param tuple The result elements
     * @param aliases The result aliases ("parallel" array to tuple)
     *
     * @return The transformed row.
     */
    T transformTuple(Object[] tuple, String[] aliases);
}

The transformTuple method allows you to transform the default Object[] array projection resulting from the JDBC ResultSet after consuming a given record.

How to use the Hibernate TupleTransformer

For instance, let’s say we have the following Post entity:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    @Column(name = "created_on")
    private LocalDateTime createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @Column(name = "updated_on")
    private LocalDateTime updatedOn;

    @Column(name = "updated_by")
    private String updatedBy;

    @Version
    private Integer version;
}

And we want to execute the following JPQL query, which fetches a custom projection:

select 
    p.id,
    p.title,
    p.createdOn,
    p.createdBy,
    p.updatedOn,
    p.updatedBy
from Post p
order by p.id

By default, when executing the JPQL query above, the project records are going to be wrapped in an Object[] array:

List<Object[]> postRecords = entityManager.createQuery("""
    select 
        p.id,
        p.title,
        p.createdOn,
        p.createdBy,
        p.updatedOn,
        p.updatedBy
    from Post p
    order by p.id
    """)
.getResultList();

However, operating with Object[] array projections is not developer-friendly, so we want to map the query result on the following Java Record hierarchy:

PostRecord and AuditRecord

For this purpose, we are going to use a TupleTransformer that allows us to map the default Object[] array projection to the PostRecord object, like this:

List<PostRecord> postRecords = entityManager.createQuery("""
    select 
        p.id,
        p.title,
        p.createdOn,
        p.createdBy,
        p.updatedOn,
        p.updatedBy
    from Post p
    order by p.id
    """)
.unwrap(org.hibernate.query.Query.class)
.setTupleTransformer(
    (tuple, aliases) -> {
        int i =0;
        return new PostRecord(
            longValue(tuple[i++]),
            stringValue(tuple[i++]),
            new AuditRecord(
                localDateTimeValue(tuple[i++]),
                stringValue(tuple[i++]),
                localDateTimeValue(tuple[i++]),
                stringValue(tuple[i++])
            )
        );
    }
)
.getResultList();

Cool, right?

I'm running an online workshop on the 20-21 and 23-24 of November about High-Performance Java Persistence.

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

Hibernate 6 provides a lot of new features, like SQM (Semantic Query Model) or support for Window Functions.

The new Hibernate TupleTransformer should be used to replace the legacy ResultTransformer usages, as the deprecated ResultTransformer will surely be removed in a future version of Hibernate.

Transactions and Concurrency Control eBook

Leave a Reply

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.