zoukankan      html  css  js  c++  java
  • NHibernate之旅(17):探索NHibernate中使用存储过程(下)

    本节内容

    • 引入
    • 实例分析
    • 拾遗
    • 结语

    引入

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

    实例分析

    下面我用几个例子来分析使用<sql-query>来执行存储过程。

    1.返回标量

    Step1:存储过程

    CREATE PROCEDURE scalarSProcs
        @number int
    AS
    BEGIN
        SELECT @number as value, 'YJingLee' as name
    END

    这里模拟验证键/值对,按键查询名称。这里返回YJingLee。

    Step2:映射文件

    在映射文件中使用<sql-query>并定义<sql-query>查询的名称,使用<return-scalar>元素来指定返回的标量值,并指定字段的别名和类型。调用存储过程时,需要一个参数,这里用命名参数表示,这里打开Customer.hbm.xml在Class元素上编写如下代码:

    <sql-query name="ScalarSProcs">
      <return-scalar column="value" type="int"/>
      <return-scalar column="name" type="string"/>
      exec scalarSProcs :number
    </sql-query>

    Step3:数据访问方法

    在数据访问层中,使用ISession接口提供的GetNamedQuery方法来调用带命名的存储过程,并传递一个整形参数。代码如下:

    public IList ScalarStoredProcedure()
    {
        return _session.GetNamedQuery("ScalarSProcs")
            .SetInt32("number", 22).List();
    }

    Step4:测试方法

    测试上面的方法,验证其标量返回的结果是否与预期的一致。

    [Test]
    public void ScalarStoredProcedureTest()
    {
        IList list = _sprocs.ScalarStoredProcedure();
        object[] o = (object[])list[0];
        Assert.AreEqual(o[0], 22);
        Assert.AreEqual(o[1], "YJingLee");
    }

    OK,测试成功,NHibernate生成SQL语句如下:

    exec scalarSProcs @p0; @p0 = '22'

    2.设置参数

    Step1:存储过程

    CREATE PROCEDURE paramSProcs
         @i int,
         @j int
    AS
    BEGIN
        SELECT @i as value, @j as value2
    END

    这里模拟一个存储过程,学习如何运用参数。

    Step2:映射文件

    带参数的存储过程,参数有两种写法,一种是使用“?”参数来表示:

    <sql-query name="ParamSProcs">
      <return-scalar column="value" type="long"/>
      <return-scalar column="value2" type="long"/>
      exec paramSProcs ?, ?
    </sql-query>

    另外一种是混合方式,使用“?”参数和命名参数表示:

    <sql-query name="ParamSProcs">
      <return-scalar column="value" type="long" />
      <return-scalar column="value2" type="long" />
      exec paramSProcs ?, :second
    </sql-query>

    Step3:数据访问方法

    在数据访问层中,我们同样使用GetNamedQuery方法来调用命名的<sql-query>存储过程,需要传递两个参数,这里支持位置参数和命名参数,其方法如下所示:

    public IList ParamStoredProcedure()
    {
        return _session.GetNamedQuery("ParamSProcs")
            .SetInt64(0, 10L)
            .SetInt64(1, 20L)
            //或者.SetInt64("second", 20L)
            .List();
    }

    Step4:测试方法

    编写一个测试用例测试上面的方法,OK!

    [Test]
    public void ParamStoredProcedureTest()
    {
        IList list = _sprocs.ParamStoredProcedure();
        object[] o = (object[])list[0];
        Assert.AreEqual(o[0], 10L);
        Assert.AreEqual(o[1], 20L);
    }

    3.实体查询

    Step1:存储过程

    上面的查询都是返回标量值的,也就是返回是的“裸”数据。 我需要查询Customer实体对象,怎么办呢?编写一个存储过程按CustomerId查询Customer。

    CREATE PROCEDURE [entitySProcs]
      @CustomerId int
    AS
    BEGIN
       SELECT * FROM [Customer]
       WHere [CustomerId] =@CustomerId
    END

    Step2:映射文件

    通过命名查询来映射这个存储过程,使用return返回具体的实体类,使用<return-property>告诉NHibernate使用哪些属性值,允许我们来选择如何引用字段以及属性。

    <sql-query name="EntitySProcs">
      <return class="DomainModel.Entities.Customer,DomainModel">
        <return-property name="CustomerId" column="CustomerId"/>
        <return-property name ="Version" column="Version"/>
        <return-property name="Firstname" column="Firstname"/>
        <return-property name="Lastname" column="Lastname"/>
      </return>
      exec entitySProcs :customerId
    </sql-query>

    这非常奇怪,我使用这种方式测试不通过,晚上和Gray Zhang讨论这个问题,Gray Zhang使用这个方式运行成功,在我的机器上运行不成功,始终出现“NHibernate.ADOException : could not execute query [ exec entitySProcs ? ] Name:customerId - Value:1 [SQL: exec entitySProcs ?] ”问题,我换了一下方式,去掉<return-property>元素,像这样:

    <sql-query name="EntitySProcs">
      <return class="DomainModel.Entities.Customer,DomainModel"/>
      exec entitySProcs :customerId
    </sql-query>

    Step3:数据访问方法

    public Customer EntityStoredProcedure(int customerId)
    {
        return _session.GetNamedQuery("EntitySProcs")
            .SetInt32("customerId",customerId)
            .UniqueResult<Customer>();
    }

    Step4:测试方法

    [Test]
    public void EntityStoredProcedureTest()
    {
        Customer customer = _sprocs.EntityStoredProcedure(1);
        int customerId = customer.CustomerId;
        Assert.AreEqual(1, customerId);
    }

    Step5:返回实体属性

    有个需求,我不想返回整个实体Customer,我想返回实体的Firstname属性怎么办呢?哦,反应过来了,使用返回标量的方法,我来写下<sql-query>:

    <sql-query name="SingleEntitySProcs">
      <return-scalar column="Firstname" type="string" />
      exec singleEntitySProcs
    </sql-query>

    注意存储过程当前仅仅返回标量和实体。接下来就留给大家完成吧!给个提示,先写存储过程,然后写访问方法,最后测试一下看看是否返回的Firstname列表了。

    拾遗

    使用存储过程查询无法使用setFirstResult()/setMaxResults()进行分页。

    存储过程的参数的位置顺序是非常重要,必须和NHibernate持久化类属性顺序相同。

    你可以在存储过程里设定SET NOCOUNT ON,这可能会提高效率。

    我们可以在类映射里使用<loader query-ref="EntitySProcs"/>引用这个命名查询定制装载存储过程。

    结语

    好了,通过三篇文章的介绍,知道了如何在NHibernate使用存储过程来删除对象、创建对象、更新对象、查询对象等操作,还有一些零碎的东西就在于大家在平时学习中去探索了。NHibernate之旅系列中关于存储过程的内容就说到这里吧,下篇开始看看如何使用代码生成器。

    本系列链接:NHibernate之旅系列文章导航

    NHibernate Q&A

    下次继续分享NHibernate!

  • 相关阅读:
    JeePlus:代码生成器
    JeePlus:API工具
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
  • 原文地址:https://www.cnblogs.com/lyj/p/1328782.html
Copyright © 2011-2022 走看看