最近一直在使用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的框架有非常好的帮助。