获得我所有的视频课程,每月2次问答电话,每月的编码挑战,一个志同道合的开发者社区,以及定期的专家会议。
Hibernate实现了JPA的标准化构造函数表达式和@SqlResultSetMappings来映射你的查询结果。而且它还支持专有的ResultTransformers。它们提供了一种强大而灵活的方式,将你的JPQL、Criteria和本地SQL查询的结果映射到一个特定的对象结构。这可以是实体或DTO对象,java.util.List或java.util.Map对每条记录的表示,或自定义的数据结构。
ResultTransformers 在Hibernate 4中特别流行,但在Hibernate 5中被弃用。不幸的是,Hibernate 5并没有为它们提供一个替代方案。由于这个原因,看起来ResultTransformer 会在Hibernate 6中被移除。但事实并非如此!
Hibernate 6将提供一个基于2个功能接口的改进版ResultTranformers 。基于Hibernate 6资源库中的当前代码,迁移你现有的ResultTransformer 应该不是什么大问题。
内容
- 1Hibernate 4和5中的ResultTransformer
- 1.1AliasToBeanResultTransformer
- 1.2ToListResultTransformer 和 AliasToEntityMapResultTransformer
- 1.3实现你自己的ResultTransformer
- 2Hibernate 6中的ResultTransformer
- 3结论
Hibernate 4和5中的ResultTransformer
Hibernate 4和5包括几个内置的ResultTransformer。此外,你也可以提供你自己的实现。但在我们谈论不同的ResultTransformers之前,让我告诉你如何将它们应用于你的查询。
你只需要向Hibernate查询接口的setResultTransformer方法提供一个你的ResultTransformer 实例。如果你使用JPA的EntityManager 和查询 接口,你需要将它们解包。如果你解开EntityManager,你会得到相关的HibernateSession。如果你解开JPA的查询 接口,你会得到Hibernate的查询接口。我在《Hibernate Tip: How to access Hibernate APIs from JPA》中详细解释了这一点。
好了,让我们来看看Hibernate 4和5中一些常用的ResultTransformers 。
AliasToBeanResultTransformer
JPA使你能够将查询结果中的每条记录映射到一个非管理的DTO对象。你可以使用JPQL中的构造函数表达式或本地查询的@ConstructorResult来定义这些映射。这两个选项都要求你在你的DTO类中添加一个构造函数,以设置所有的属性。如果你的DTO类有大量的属性,这可能是个问题。
Hibernate的AliasToBeanResultTransformer 提供了另一种基于bean规范的方法。它使用DTO类的默认构造函数来实例化一个新对象。在下一步,Hibernate使用反射来为查询中的每个别名值调用一个setter方法。这使得它非常适用于作为标准Java类但不作为Java记录实现的DTOs。
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();
在这个例子中,AliasToBeanResultTransformer 使用默认构造函数,为查询返回的每条记录实例化一个新的PersonDTO 对象。在下一步,Hibernate用查询返回的值调用setPersonId、setFirstName 和setLastName 方法。
ToListResultTransformer 和 AliasToEntityMapResultTransformer
如果你不想改变选中的数据,又没有匹配的DTO类,你可以使用Hibernate的ToListResultTransformer 或AliasToEntityMapResultTransformer。ToListResultTransformer 将查询返回的Object[]及其所有元素映射为java.util.List。AliasToEntityMapResultTransformer 将查询结果转换为一个java.util.Map ,其中包含结果集的所有别名值。每个值的别名被用作Map的键。
这里你可以看到一个AliasToEntityMapResultTransformer的例子。你可以以同样的方式使用ToListResultTransformer 。
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();
实现你自己的 ResultTransformer
如果你想用Hibernate 4或5实现自己的ResultTransformer ,你需要实现Hibernate的ResultTransformer 接口。该接口定义了2个方法:transformTuple 和transformList方法。
一个普通的ResultTransformer 实现在transformTuple 方法中实现每个记录的映射。transformList 方法只返回提供的图元列表。
我在下面的代码片段中使用了这种方法来实现我自己的ResultTransformer ,将每条记录映射到一个PersonDTO 对象。
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();
Hibernate 6中的ResultTransformer
当我描述Hibernate 4和5中自定义ResultTransformer 的实现时,我也提到了ResultTransformer 接口的一个缺点。它定义了transformTuple 和transformList 方法,这两个方法都需要被实现。大多数应用程序只以有意义的方式实现这两个方法中的一个。但是因为这两个方法都是接口定义的一部分,所以你需要实现这两个方法,并且不能在lambda表达式中把ResultTransformer 作为一个功能接口。
这在Hibernate 6中有所改变。Hibernate团队已经将ResultTransformer 接口拆分为2个功能接口。TupleTransformer 和ResultListTransformer。你可以通过调用Hibernate的查询 接口上的setTupleTransformer和setResultListTransformer 方法来设置它们。
Hibernate团队还将Hibernate 4和5提供的ResultTransformer 实现转换为Hibernate 6中的TupleTransformer或ResultListTransformer实现。因此,在将你的应用程序迁移到Hibernate 6时,所需的改变应该是最小的。
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();
正如你在下面的代码片段中所看到的,Hibernate 6中的自定义转换器的实现要简洁得多。
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();
总结
Hibernate的ResultTransformers 提供了各种方法来将查询结果映射到不同的数据结构。它们在Hibernate 4中被普遍使用,在Hibernate 5中被废弃,在Hibernate 6中被功能接口TupleTransformer和ResultListTransformer取代。
下面的列表显示了Hibernate 4和5中最常用的3种ResultTransformers。这些在Hibernate 6中仍然可用,现在实现了TupleTransformer和/或ResultListTransformer 接口。
- AliasToBeanResultTransformer- 基于查询中定义的别名,对DTO对象进行实例化和设置属性。
- ToListResultTransformer--将查询结果中的每条记录映射到java.util.List中。
- AliasToEntityMapResultTransformer--将查询结果中每条记录的别名值映射到一个java.util.Map。
你也可以实现你自己的转换。