1. 程式人生 > >在hibernate框架下使用Transformers呼叫sql

在hibernate框架下使用Transformers呼叫sql

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:
[color=red]public class StudentDTO
{
private String studentName;
private String courseDescription;
public StudentDTO() { }
...
} [/color]Then you can make the Criteria return non-entity classes instead of scalars or entities by applying a ResultTransformer:

[color=red]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); [/color]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.
[color=red]
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); [/color]  
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.

[color=red]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); [/color]
  
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.
[color=red]
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.
[/color]
使用SQLQuery
對原生SQL查詢執行的控制是通過SQLQuery介面進行的,通過執行Session.createSQLQuery()獲取這個介面。最簡單的情況下,我們可以採用以下形式:

[color=blue]List cats = sess.createSQLQuery( " select * from cats " ).addEntity(Cat. class ).list();[/color]這個查詢指定了:

SQL查詢字串

查詢返回的實體

這裡,結果集欄位名被假設為與對映檔案中指明的欄位名相同。對於連線了多個表的查詢,這就可能造成問題,因為可能在多個表中出現同樣名字的欄位。下面的方法就可以避免欄位名重複的問題:
[color=blue]
List cats = sess.createSQLQuery( " select {cat.*} from cats cat " ).addEntity( " cat " , Cat. class ).list();[/color]這個查詢指定了:

SQL查詢語句,它帶一個佔位符,可以讓Hibernate使用欄位的別名.

查詢返回的實體,和它的SQL表的別名.

addEntity()方法將SQL表的別名和實體類聯絡起來,並且確定查詢結果集的形態。

addJoin()方法可以被用於載入其他的實體和集合的關聯.

[color=blue]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();[/color]原生的SQL查詢可能返回一個簡單的標量值或者一個標量和實體的結合體。

[color=blue]Double max = (Double) sess.createSQLQuery( " select max(cat.weight) as maxWeight from cats cat " )
.addScalar( " maxWeight " , Hibernate.DOUBLE);
.uniqueResult();[/color]除此之外,你還可以在你的hbm檔案中描述結果集對映資訊,在查詢中使用。

[color=blue]List cats = sess.createSQLQuery(
" select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id " )
.setResultSetMapping( " catAndKitten " )
.list();[/color]命名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();