轉載 http://www.iflym.com/index.php/code/resolve-hibernate-result-transformer-by-query.html
任何包裝jdbc的框架,都離不開將最終的數據封裝成java對象的一個過程。在jdbc中,取得的數據被封裝在resultset中,通過迭代resultset來一次次的取得相應的字段和數據值。數據庫框架始終需要解決的問題在於將resultset中的字段名稱信息和相應的字段值對應起來,然后封裝成對象,最后將所有的對象形成一個集合,並最終返回給調用者。
任何數據庫框架都逃不過這中間的處理邏輯,只不過如何將這些邏輯分散在上下的處理中。在Hibernate中,同樣也有類似的東西,這個接口就叫做ResultTransformer。
Transformer的定義如下:
public interface ResultTransformer extends Serializable{
public Object transformTuple(Object[] tuple, String[] aliases);
public List transformList(List collection);
}
其中第一個方法transformTuple,即是如何處理從數據庫查詢出來的字段值(可能經過了二次處理)和相對應的字段名稱值,比如將字段值和字段名稱組合成一個map。字段值,即在查詢過程中查詢的字段列表,而字段名稱即是在查詢時select的名稱。
第二個方法transformList,提供了對於從數據庫返回結果,進行了封裝之后,再對封裝之后的數據列表進行最后一次處理。如進行去重等。
為了說明ResultTransformer在Hibernate中的運用,我們從Hibernate中的查詢入手,看ResultTransformer如何在其中運用的(以Hibernate3.6.3版本為例,在代碼中不顯示不必要的代碼)。
由類QueryImpl中的list入手:
public List list() throws HibernateException {return getSession().list(expandParameterLists(namedParams), getQueryParameters(namedParams));
}
注意上面代碼中的getQueryParameters調用,這里會將傳遞給query的所有參數進行封裝,包括傳遞給query的resultTransformer。如果我們使用query.setResultTransformer傳遞給query,在調用時這里就會傳遞給相應的函數,並生成一個QueryParameters對象。
接下來看sessionImpl中的實現:
public List list(String query, QueryParameters queryParameters) throws HibernateException{
HQLQueryPlan plan = getHQLQueryPlan( query, false );
results = plan.performList( queryParameters, this );
return results;
}
以下代碼將查詢語句封裝成一個查詢計划,並執行該計划,返回一個查詢結果。進入方法實現,類HQLQueryPlan的performList方法:
List combinedResults = new ArrayList();
translator_loop: for ( int i = 0; i < translators.length; i++ ) {
List tmp = translators[i].list( session, queryParametersToUse );
combinedResults.addAll( tmp );
}
return combinedResults;
}
以上代碼會調用一個叫QueryTranslator的實現,即將hql轉化為sql並進行查詢操作。進入實現類QueryTranslatorImpl類的list方法:
public List list( SessionImplementor session,QueryParameters queryParameters) throws HibernateException
{
List results = queryLoader.list( session, queryParametersToUse );
return results;
}
以上代碼會調用最終的查詢邏輯實現即queryTranslator的最終數據庫加載邏輯去查詢,進入實現類QueryLoader的list方法:
public List list(SessionImplementor session,QueryParameters queryParameters) throws HibernateException {
}
protected List list( final SessionImplementor session, final QueryParameters queryParameters, final Set querySpaces, final Type[] resultTypes) throws HibernateException {
return listIgnoreQueryCache( session, queryParameters );
}
private List listIgnoreQueryCache(SessionImplementor session, QueryParameters queryParameters) {
return getResultList( doList( session, queryParameters ), queryParameters.getResultTransformer() );
}
以上3個方法是一些內部實現邏輯,這里就不深究了。最重要的是最后的getResult方法和里面的doList方法。其中doList即是最終的數據庫查詢實現,以及初步的對象轉化(比如from 類查詢時,會將數據結果轉換成一個dom對象)。然后將結果集交到getResultList中進行處理,即到了我們最重要的resultTransformer處理了。
HolderInstantiator holderInstantiator = buildHolderInstantiator( resultTransformer );
if ( holderInstantiator.isRequired() ) {
for ( int i = 0; i < results.size(); i++ ) {
Object[] row = ( Object[] ) results.get( i );
Object result = holderInstantiator.instantiate(row);
}......
return resultTransformer.transformList(results);
}
}
在上面的地方,這里出現了一個關鍵的類HolderInstantiator,就是根據resultTransformer來處理數據。首先會將返回的數據值(默認即為object數組)進行實例化為一個對象,就是將數組轉換為對象,這里面就會調用我們transformer第一次轉換數據了,即將數據庫返回數據轉換為需要的數據:
if(transformer== null) {
return row;
} else {
return transformer.transformTuple(row, queryReturnAliases);
}
}
接下來就進行第二次處理,如果需要去重,就將list中的對象集合裝入set,再轉換回來進行去重處理。最后就是我們所需要的結果了。
在這些處理當中,並不是每次處理都傳遞了resultTransformer的。對於沒有resultTransformer的情況,Hibernate在內部已經進行了處理。如果我們需要查詢一個domain對象,Hibernate就會使用entityKey來解析數據;如果查詢的是屬性列表,即是使用默認的object數組來裝結果。但一旦設置了resultTransformer,就是將上面查詢的結果進行處理了。如將object數組轉換成其它格式,如list格式,或者map格式(這一種用得最頻繁,即經常使用的AliasToEntityMapResultTransformer).。
Hibernate使用了靜態單態的模式來封裝相應的resultTransformer實現,當需要相應的數據時,即可直接通過公共的靜態字段進行獲取和傳遞(而且也是惟一的手段)。如通過ResultTransformerImpl.INSTANCE或通過Transformers.靜態字段引用來獲取相應的resultTransformer,最終傳遞給query,criteria等,以達到獲取數據的目的。