最近一直在使用NHibernate做開發,發現雖然NHibernate實體查詢很強大,但是還是沒有SQL查詢方便,而且有時候我得需要返回多張表的數據,而且必須有字段名,這樣必須得返回一個DataSet或DataTable類型的對象。我不可能為了這一個簡單的多表查詢就去整一個實體來映射,這樣也太麻煩了。我也不想就因為這樣一個經常碰到的小問題就去寫個存儲過程,這樣也鬧大了點。因此我想能不能直接通過配置Sql-Query查詢來得到一個DataSet呢?
一、准備測試環境
1、創建一解決方案,添加三項目,一個類似於實體library,一個是測試項目,另一個是NHibernate的源碼項目。
TestLb中主要是一個實體DriverInfo.cs及其映射文件。MyTest項目中主要是UnitTest1.cs文件以及其它一些應用類和配置文件。TestLb和MyTest項目中包含一個對NHibernate源碼項目的引用,因文件太多,不提供下載,可到NHibernate官網下載:
https://github.com/nhibernate/nhibernate-core/tree/master/src
下面是上面TestLb和MyTest兩項目下載地址,僅供參考:
http://files.cnblogs.com/wuwei_chen/src.zip
我的數據庫環境是Oracle,所以請根據自己的情況配置實體映射文件以及測試項目中hibernate.cfg.xml文件。解決方案中添加好三項目后,注意給實體項目和測試項目添加NHibernate的 Reference。
2、簡單了解下sql-query查詢
DriverInfo.hbm.xml映射文件下如下代碼:
<sql-query name="GetDataSetTest"> <!--<return class="System.Data.DataSet,System.Data"> </return>--> <!--<return class="DataSet"> </return>--> select * from Fleet_DriverInfo where rownum<2 </sql-query>
MyTest項目中UnitTest1下測試方法:
[TestMethod] public void TestReaderWay() { ISessionFactory factory=SessionFactory.SessionFac; ISession session = factory.OpenSession(); IQuery query = session.GetNamedQuery("GetDataSetTest"); var res0 = query.List(); //var res = query.QueryDataSet(); }
至於上述代碼中SessionFactory只是一個實現了單鍵模式的用於返回一個ISessionFactory類型的簡單類。一個簡單的sql-query查詢測試就這樣了。
3、開始摸索
運行上述測試代碼,返回來的對象是一個IEnurable的object[]數組,顯然不是我們需要的。我們查看IQuery接口代碼,發現其有一個返回泛型list的方法,那能否用泛型返回DataSet呢?
修改上面測試方法中一句代碼:
var res0 = query.List<DataSet>()
執行報如下錯誤 :
System.Object[]" is not of type "System.Data.DataSet" and cannot be used in this generic collection
那如果將映射文件中的Sql-query中的配置改成如下呢:
<sql-query name="GetDataSetTest"> <return class="System.Data.DataSet,System.Data"> </return> <!-- or <return class="DataSet"> </return>--> select * from Fleet_DriverInfo where rownum<2 </sql-query>
如果改成上面兩種設置返回類型,更加不行了,在BuildSessionFactory階段就會報錯。
No persister for :System.Data.DataSet......
看來是無法這樣返回DataSet了,那只好考慮是否能基於NHibernate的源碼做擴展了。
二、了解NHibernate的sql-query查詢機制
既然是了解NHibernate的查詢機制,肯定得調試NHibernate的源碼了。為了節約大家按F5、F9、F11、F12的次數,我直接先列出一些重要的斷點位置。
SqlQueryImpl.cs NHibernate=>Impl=>SqlQueryImpl.cs
斷點位置:127 line
public override IList List() { ...... return Session.List(spec, qp); ...... }
SessionImpl.cs NHibernate=>Impl=>SessionImpl.cs
斷點位置:2163 line
public override void ListCustomQuery(ICustomQuery customQuery, QueryParameters queryParameters, IList results) { ......
ArrayHelper.AddAll(results, loader.List(this, queryParameters)); ...... }
由於需要設置斷點的位置太多,下面我就不一一貼出設置斷點的全部方法了,只貼出設置斷點的代碼。
CustomLoader.cs NHibernate=>Loader=>Custom=>CustomLoader.cs
斷點位置:276 line
public IList List(ISessionImplementor session, QueryParameters queryParameters) { return List(session, queryParameters, querySpaces, resultTypes); }
Loader.cs NHibernate=>Loader=>Loader.cs
斷點位置:1495 line
斷點位置:1532 line
斷點位置:1624 line
斷點位置:249 line
斷點位置:417 line
protected IList List(ISessionImplementor session, QueryParameters queryParameters, ISet<string> querySpaces, IType[] resultTypes) { ...... return ListUsingQueryCache(session, queryParameters, querySpaces, resultTypes); ...... } private IList ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters) { return GetResultList(DoList(session, queryParameters), queryParameters.ResultTransformer); } protected IList DoList(ISessionImplementor session, QueryParameters queryParameters) { ...... result = DoQueryAndInitializeNonLazyCollections(session, queryParameters, true); ...... } private IList DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, bool returnProxies) { ...... result = DoQuery(session, queryParameters, returnProxies); ...... } private IList DoQuery(ISessionImplementor session, QueryParameters queryParameters, bool returnProxies) { ...... IDbCommand st = PrepareQueryCommand(queryParameters, false, session); IDataReader rs = GetResultSet(st, queryParameters.HasAutoDiscoverScalarTypes, queryParameters.Callable, selection, session); ...... }
在此,一些重要的斷點位置設置好了,開始調試了。因為這篇隨筆太長了,所以只好另起一篇了。下一篇主要介紹如何擴展NHibernate的 sql-query來返回DataSet。
有興趣的同仁可以先按照我上面介紹的設置好的一些斷點,跟蹤調試幾次,對理解NHibernate的框架有非常好的幫助。