使用 Criteria API 的人要么是透明地,要么是有意识地使用了ResultTransformer。ResultTransformer 是一个简单而优雅的接口,允许您转换任何 Criteria 结果元素。例如,您可以使得任何 Criteria 结果作为 java.util.Map 或非实体 Bean 返回。
Criteria 转换器
想象您有一个 StudentDTO 类
public class StudentDTO { private String studentName; private String courseDescription; public StudentDTO() { } ... }
然后您可以通过应用 ResultTransformer,使 Criteria 返回非实体类而不是标量或实体,而不是返回非实体类
List resultWithAliasedBean = s.createCriteria(Enrolment.class) .createAlias("student", "st").createAlias("course", "co") .setProjection( Projections.projectionList() .add( Projections.property("st.name"), "studentName" ) .add( Projections.property("co.description"), "courseDescription" ) ) .setResultTransformer( Transformers.aliasToBean(StudentDTO.class) ) .list(); StudentDTO dto = (StudentDTO)resultWithAliasedBean.get(0);
这是自我们引入 projection 到 Hibernate 3 的 Criteria API 以来,ResultTransformer 可用的方式。
这只是内置转换器的一个例子,如果用户愿意,他们可以提供自己的转换器。
嫉妒编程
由于我更偏向于 HQL/SQL,我一直嫉妒 Criteria 有这个特性,我看到了很多请求要求将其添加到我们所有的查询工具中。
今天,我结束了这种嫉妒,并在 Hibernate 3.2 中引入了 HQL 和 SQL 的 ResultTransformer。
HQL 转换器
在 HQL 中,我们已经有了一种通过 (select new
http://www.hibernate.org/hib_docs/v3/reference/en/html/queryhql.html#queryhql-select) 语法实现的“类型”结果转换器,但它仅通过构造函数提供了这些 Bean 的值注入。因此,如果您在许多不同的场景中使用了相同的 DTO,您可能会在 DTO 上创建许多仅为了允许 select new
功能工作的构造函数。
现在您可以通过属性方法或字段获取值注入,从而不需要显式构造函数。
List resultWithAliasedBean = s.createQuery( "select e.student.name as studentName," + " e.course.description as courseDescription" + "from Enrolment as e") .setResultTransformer( Transformers.aliasToBean(StudentDTO.class)) .list(); StudentDTO dto = (StudentDTO) resultWithAliasedBean.get(0);
SQL 转换器
使用原生 SQL 返回非实体 Bean 或 Map 的功能通常比基本Object[]更实用。有了结果转换器,现在就可以实现这一点。
List resultWithAliasedBean = s.createSQLQuery( "SELECT st.name as studentName, co.description as courseDescription " + "FROM Enrolment e " + "INNER JOIN Student st on e.studentId=st.studentId " + "INNER JOIN Course co on e.courseCode=co.courseCode") .addScalar("studentName") .addScalar("courseDescription") .setResultTransformer( Transformers.aliasToBean(StudentDTO.class)) .list(); StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);
提示:在HSQLDB上调用addScalar()是必需的,以便使其与属性名称匹配,因为HSQLDB返回所有大写的列名(例如,STUDENTNAME
)。这也可以通过一个自定义转换器来解决,该转换器搜索属性名称而不是使用精确匹配——也许我们应该提供一个fuzzyAliasToBean()方法;)
映射versusObject[]
由于您还可以使用返回别名到值/实体的映射的转换器(例如,Transformers.ALIASTOMAP),因此在处理结果时不再需要与基于索引的对象数组纠缠。
List iter = s.createQuery( "select e.student.name as studentName," + " e.course.description as courseDescription" + "from Enrolment as e") .setResultTransformer( Transformers.ALIAS_TO_MAP ) .iterate(); String name = (Map)(iter.next()).get("studentName");
同样,这对于Criteria、HQL和原生SQL都同样有效。
达到原生SQL的极乐境界
我们仍然缺少一些东西,但通过与SQL的ResultTranformer支持以及其他最近添加到Hibernate原生SQL功能中的其他添加功能,我们接近达到原生SQL支持的极乐境界。
与StatelessSession结合使用,您现在实际上拥有一个非常灵活且功能强大的sql执行器
,它可以透明地将对象映射到原生SQL,并且没有ORM开销。
...当您厌倦了手动管理对象的sql、对象状态、生命周期、缓存等,并希望利用ORM的力量时,这些功能都可以轻松获得;)