[Hibernate]Hibernate 3.2: Transformers for HQL and SQL

Posted on 2008-07-10 17:39 追梦人 阅读(929) 评论(0)  编辑  收藏 所属分类: Hibernate
People using the Criteria API have either transparently or knowingly used a ResultTransformer. A ResultTransformer is a nice and simple interface that allows you to transform any Criteria result element. E.g. you can make any Criteria result be returned as a java.util.Map or as a non-entity Bean.

Criteria TransformersImagine you have a StudentDTO class:
public class StudentDTO
{  
   private String studentName;  
   private String courseDescription;  
   public StudentDTO() {  }        
   ...
}
Then you can make the Criteria return non-entity classes instead of scalars or entities by applying a ResultTransformer:

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

This is how ResultTransformer have been available since we introduced projection to the Criteria API in Hibernate 3.
It is just one example of the built in transformers and users can provide their own transformers if they so please.
Jealous programming Since I am more a HQL/SQL guy I have been jealous on Criteria for having this feature and I have seen many requests for adding it to all our query facilities.
Today I put an end to this jealousy and introduced ResultTransformer for HQL and SQL in Hibernate 3.2.
HQL TransformersIn HQL we already had a "kind" of result transformers via the ("select new" http://www.hibernate.org/hib_docs/v3/reference/en/html/queryhql.html#queryhql-select) syntax, but for returning non-entity beans it only provided value injection of these beans via its constructor. Thus if you used the same DTO in many different scenarios you could end up having many constructors on this DTO purely for allowing the "select new" functionality to work.
Now you can get the value injected via property methods or fields instead, removing the need for explicit constructors.

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 TransformersWith native sql returning non-entity beans or Map's is often more useful instead of basic Object[]. With result transformers that is now possible.

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

  
Tip: the addScalar() calls were required on HSQLDB to make it match a property name since it returns column names in all uppercase (e.g. "STUDENTNAME"). This could also be solved with a custom transformer that search the property names instead of using exact match - maybe we should provide a fuzzyAliasToBean() method ;)
Map vs. Object[]Since you can also use a transformer that return a Map from alias to value/entity (e.g. Transformers.ALIAS_TO_MAP), you are no longer required to mess with index based Object arrays when working with a result.

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

  
Again, this works equally well for Criteria, HQL and native SQL.
 

使用SQLQuery
对原生SQL查询执行的控制是通过SQLQuery接口进行的,通过执行Session.createSQLQuery()获取这个接口。最简单的情况下,我们可以采用以下形式:

List cats = sess.createSQLQuery( " select * from cats " ).addEntity(Cat. class ).list();

这个查询指定了:

SQL查询字符串

查询返回的实体

这里,结果集字段名被假设为与映射文件中指明的字段名相同。对于连接了多个表的查询,这就可能造成问题,因为可能在多个表中出现同样名字的字段。下面的方法就可以避免字段名重复的问题:

List cats = sess.createSQLQuery( " select {cat.*} from cats cat " ).addEntity( " cat " , Cat. class ).list();

这个查询指定了:

SQL查询语句,它带一个占位符,可以让Hibernate使用字段的别名.

查询返回的实体,和它的SQL表的别名.

addEntity()方法将SQL表的别名和实体类联系起来,并且确定查询结果集的形态。

addJoin()方法可以被用于载入其他的实体和集合的关联.

List cats = sess.createSQLQuery(
" select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id " )
.addEntity(
" cat " , Cat. class )
.addJoin(
" kitten " , " cat.kittens " )
.list();

原生的SQL查询可能返回一个简单的标量值或者一个标量和实体的结合体。

Double max = (Double) sess.createSQLQuery( " select max(cat.weight) as maxWeight from cats cat " )
.addScalar(
" maxWeight " , Hibernate.DOUBLE);
.uniqueResult();

除此之外,你还可以在你的hbm文件中描述结果集映射信息,在查询中使用。

List cats = sess.createSQLQuery(
" select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id " )
.setResultSetMapping(
" catAndKitten " )
.list();

命名SQL查询
可以在映射文档中定义查询的名字,然后就可以象调用一个命名的HQL查询一样直接调用命名SQL查询.在这种情况下,我们不 需要调用addEntity()方法.

< sql - query name = " persons " >
< return alias = " person " class = " eg.Person " />
Select person.NAME AS {person.name},person.AGE AS {person.age},person.SEX AS {person.sex} FROM PERSON person Where person.NAME LIKE :namePattern
</ sql - query >
List people = sess.getNamedQuery( " persons " ).setString( " namePattern " , namePattern)
.setMaxResults(
50 )
.list();

只有注册用户登录后才能发表评论。


网站导航: