找回密码
 立即注册
首页 业界区 业界 NHibernate之旅(17):探索NHibernate中使用存储过程(下) ...

NHibernate之旅(17):探索NHibernate中使用存储过程(下)

榕闹 2025-5-30 01:22:59
本节内容
           
  • 引入       
  • 实例分析       
  • 拾遗       
  • 结语
引入

上两篇,介绍使用MyGeneration提供的模板创建存储过程和删除对象、创建对象、更新对象整个详细过程,这篇介绍如何利用做更多的事,在程序开发中,我们不仅仅只利用存储过程增删查改对象,我们还可以想执行任意的存储过程,这不局限于某个对象,某个CURD操作,怎么做呢?注意:本篇并非官方权威的资料,所以敬请参考。如果你还没有学习NHibernate,请快速链接到NHibernate之旅系列文章导航。
实例分析

下面我用几个例子来分析使用来执行存储过程。
1.返回标量

Step1:存储过程
  1. CREATE PROCEDURE scalarSProcs
  2.     @number int
  3. AS
  4. BEGIN
  5.     SELECT @number as value, 'YJingLee' as name
  6. END
复制代码
这里模拟验证键/值对,按键查询名称。这里返回YJingLee。
Step2:映射文件
在映射文件中使用并定义查询的名称,使用元素来指定返回的标量值,并指定字段的别名和类型。调用存储过程时,需要一个参数,这里用命名参数表示,这里打开Customer.hbm.xml在Class元素上编写如下代码:
  1. <sql-query name="ScalarSProcs">
  2.   <return-scalar column="value" type="int"/>
  3.   <return-scalar column="name" type="string"/>
  4.   exec scalarSProcs :number
  5. </sql-query>
复制代码
Step3:数据访问方法
在数据访问层中,使用ISession接口提供的GetNamedQuery方法来调用带命名的存储过程,并传递一个整形参数。代码如下:
  1. public IList ScalarStoredProcedure()
  2. {
  3.     return _session.GetNamedQuery("ScalarSProcs")
  4.         .SetInt32("number", 22).List();
  5. }
复制代码
Step4:测试方法
测试上面的方法,验证其标量返回的结果是否与预期的一致。
  1. [Test]
  2. public void ScalarStoredProcedureTest()
  3. {
  4.     IList list = _sprocs.ScalarStoredProcedure();
  5.     object[] o = (object[])list[0];
  6.     Assert.AreEqual(o[0], 22);
  7.     Assert.AreEqual(o[1], "YJingLee");
  8. }
复制代码
OK,测试成功,NHibernate生成SQL语句如下:
  1. exec scalarSProcs @p0; @p0 = '22'
复制代码
2.设置参数

Step1:存储过程
  1. CREATE PROCEDURE paramSProcs
  2.      @i int,
  3.      @j int
  4. AS
  5. BEGIN
  6.     SELECT @i as value, @j as value2
  7. END
复制代码
这里模拟一个存储过程,学习如何运用参数。
Step2:映射文件
带参数的存储过程,参数有两种写法,一种是使用“?”参数来表示:
  1. <sql-query name="ParamSProcs">
  2.   <return-scalar column="value" type="long"/>
  3.   <return-scalar column="value2" type="long"/>
  4.   exec paramSProcs ?, ?
  5. </sql-query>
复制代码
另外一种是混合方式,使用“?”参数和命名参数表示:
  1. <sql-query name="ParamSProcs">
  2.   <return-scalar column="value" type="long" />
  3.   <return-scalar column="value2" type="long" />
  4.   exec paramSProcs ?, :second
  5. </sql-query>
复制代码
Step3:数据访问方法
在数据访问层中,我们同样使用GetNamedQuery方法来调用命名的存储过程,需要传递两个参数,这里支持位置参数和命名参数,其方法如下所示:
  1. public IList ParamStoredProcedure()
  2. {
  3.     return _session.GetNamedQuery("ParamSProcs")
  4.         .SetInt64(0, 10L)
  5.         .SetInt64(1, 20L)
  6.         //或者.SetInt64("second", 20L)
  7.         .List();
  8. }
复制代码
Step4:测试方法
编写一个测试用例测试上面的方法,OK!
  1. [Test]
  2. public void ParamStoredProcedureTest()
  3. {
  4.     IList list = _sprocs.ParamStoredProcedure();
  5.     object[] o = (object[])list[0];
  6.     Assert.AreEqual(o[0], 10L);
  7.     Assert.AreEqual(o[1], 20L);
  8. }
复制代码
3.实体查询

Step1:存储过程
上面的查询都是返回标量值的,也就是返回是的“裸”数据。 我需要查询Customer实体对象,怎么办呢?编写一个存储过程按CustomerId查询Customer。
  1. CREATE PROCEDURE [entitySProcs]
  2.   @CustomerId int
  3. AS
  4. BEGIN
  5.    SELECT * FROM [Customer]
  6.    WHere [CustomerId] =@CustomerId
  7. END
复制代码
Step2:映射文件
通过命名查询来映射这个存储过程,使用return返回具体的实体类,使用告诉NHibernate使用哪些属性值,允许我们来选择如何引用字段以及属性。
  1. <sql-query name="EntitySProcs">
  2.   <return class="DomainModel.Entities.Customer,DomainModel">
  3.     <return-property name="CustomerId" column="CustomerId"/>
  4.     <return-property name ="Version" column="Version"/>
  5.     <return-property name="Firstname" column="Firstname"/>
  6.     <return-property name="Lastname" column="Lastname"/>
  7.   </return>
  8.   exec entitySProcs :customerId
  9. </sql-query>
复制代码
这非常奇怪,我使用这种方式测试不通过,晚上和Gray Zhang讨论这个问题,Gray Zhang使用这个方式运行成功,在我的机器上运行不成功,始终出现“NHibernate.ADOException : could not execute query [ exec entitySProcs ? ] Name:customerId - Value:1 [SQL: exec entitySProcs ?] ”问题,我换了一下方式,去掉元素,像这样:
  1. <sql-query name="EntitySProcs">
  2.   <return class="DomainModel.Entities.Customer,DomainModel"/>
  3.   exec entitySProcs :customerId
  4. </sql-query>
复制代码
Step3:数据访问方法
  1. public Customer EntityStoredProcedure(int customerId)
  2. {
  3.     return _session.GetNamedQuery("EntitySProcs")
  4.         .SetInt32("customerId",customerId)
  5.         .UniqueResult<Customer>();
  6. }
复制代码
Step4:测试方法
  1. [Test]
  2. public void EntityStoredProcedureTest()
  3. {
  4.     Customer customer = _sprocs.EntityStoredProcedure(1);
  5.     int customerId = customer.CustomerId;
  6.     Assert.AreEqual(1, customerId);
  7. }
复制代码
Step5:返回实体属性
有个需求,我不想返回整个实体Customer,我想返回实体的Firstname属性怎么办呢?哦,反应过来了,使用返回标量的方法,我来写下:
  1. <sql-query name="SingleEntitySProcs">
  2.   <return-scalar column="Firstname" type="string" />
  3.   exec singleEntitySProcs
  4. </sql-query>
复制代码
注意存储过程当前仅仅返回标量和实体。接下来就留给大家完成吧!给个提示,先写存储过程,然后写访问方法,最后测试一下看看是否返回的Firstname列表了。
拾遗

使用存储过程查询无法使用setFirstResult()/setMaxResults()进行分页。
存储过程的参数的位置顺序是非常重要,必须和NHibernate持久化类属性顺序相同。
你可以在存储过程里设定SET NOCOUNT ON,这可能会提高效率。
我们可以在类映射里使用引用这个命名查询定制装载存储过程。
结语

好了,通过三篇文章的介绍,知道了如何在NHibernate使用存储过程来删除对象、创建对象、更新对象、查询对象等操作,还有一些零碎的东西就在于大家在平时学习中去探索了。NHibernate之旅系列中关于存储过程的内容就说到这里吧,下篇开始看看如何使用代码生成器。
本系列链接:NHibernate之旅系列文章导航
NHibernate Q&A
           
  • 欢迎加入NHibernate中文社区,一起讨论NHibernate知识!       
  • 请到NHibernate中文社区下载本系列相关源码。
下次继续分享NHibernate!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册