NHibernate-通过SQL-Query返回DataSet之一

时间:2022-04-03 15:45:58

      最近一直在使用NHibernate做开发,发现虽然NHibernate实体查询很强大,但是还是没有SQL查询方便,而且有时候我得需要返回多张表的数据,而且必须有字段名,这样必须得返回一个DataSet或DataTable类型的对象。我不可能为了这一个简单的多表查询就去整一个实体来映射,这样也太麻烦了。我也不想就因为这样一个经常碰到的小问题就去写个存储过程,这样也闹大了点。因此我想能不能直接通过配置Sql-Query查询来得到一个DataSet呢?

      一、准备测试环境

      1、创建一解决方案,添加三项目,一个类似于实体library,一个是测试项目,另一个是NHibernate的源码项目。

      NHibernate-通过SQL-Query返回DataSet之一

 

     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&lt;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&lt;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的框架有非常好的帮助。