使用Hibernate TupleTransformer的最佳方法

1,153 阅读2分钟

简介

在这篇文章中,我将解释Hibernate TupleTransformer是如何工作的,以及如何使用它来替代被废弃的ResultTransformer。

被废弃的ResultTransformer

在使用Hibernate 6之前, ResultTransformer是转换查询结果集的默认选项。

然而,从5.2开始,HibernateQuery 接口的setResultTransformer 方法被废弃了,即使当时没有提供替代方法。这个方法及其相关的ResultTransformer 接口被废弃的原因是,ResultTransformer 接口不是一个功能接口。

功能性接口提供了一个单一的抽象方法,可以在定义lambda表达式时使用。出于这个原因,传统的ResultTransformer 接口被分成了两个接口TupleTransformerResultListTransformer ,如下图所示。

TupleTransformer ResultListTransformer ResultTransformer

Hibernate TupleTransformer

TupleTransformer 定义了transformTuple 方法,如下图所示。

@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);
}

transformTuple 方法允许你在消耗一个给定的记录后,对JDBCResultSet 产生的默认Object[] 数组投影进行转换。

如何使用Hibernate TupleTransformer

例如,假设我们有以下Post 实体。

@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;
}

而我们想执行下面的JPQL查询,它可以获取一个自定义的投影

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

默认情况下,当执行上面的JPQL查询时,项目记录将被包裹在一个Object[] 阵列中。

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();

然而,使用Object[] 数组投影的操作对开发人员来说并不方便,所以我们要将查询结果映射到以下的Java记录层次上。

PostRecord and AuditRecord

为此,我们要使用一个TupleTransformer ,允许我们将默认的Object[] 阵列投影映射到PostRecord 对象上,像这样。

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();

很酷,对吗?

总结

Hibernate 6提供了很多新功能,比如[SQM(Semantic Query Model)(https://vladmihalcea.com/hibernate-sqm-semantic-query-model/)或对窗口函数的支持。

新的HibernateTupleTransformer 应该被用来替代传统的ResultTransformer 的使用,因为被废弃的ResultTransformer 肯定会在未来的Hibernate版本中被移除。