Hibernate 3.2: HQL 和 SQL 的转换器

发布者:    |       Hibernate ORM

使用 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的力量时,这些功能都可以轻松获得;)


返回顶部