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

    本节内容

    引入

    上一篇,怎么使用MyGeneration提供的模板创建存储过程和删除对象存储过程的使用,这篇接下来介绍在NHibernate中如何使用存储过程创建对象、更新对象整个详细过程,这些全是在实际运用中积累的经验,涉及使用的错误信息,如何修改存储过程,并且比较没有使用存储过程的不同点,并非官方比较权威的资料,所以敬请参考,这篇继续,如果你还没有来及看上一篇,那赶紧去看看吧。

    实例分析

    2.创建对象

    Step1:为了比较,我们先执行CreateCustomerTest()测试方法,没有使用存储过程下创建对象生成SQL语句如下:

    INSERT INTO Customer (Version, Firstname, Lastname) VALUES (@p0, @p1, @p2);
    select SCOPE_IDENTITY(); @p0 = '1', @p1 = 'YJing', @p2 = 'Lee'

    Step2:修改映射文件添加存储过程,打开Customer.hbm.xml映射文件,在Class元素下添加<sql-insert>节点,调用CustomerInsert存储过程,CustomerInsert 存储过程有四个参数,这里用四个问号表示:

    <sql-insert>exec CustomerInsert ?,?,?,?</sql-insert>

    Step3:执行CreateCustomerTest()测试方法,出现错误“NHibernate.Exceptions.GenericADOException : could not insert: [DomainModel.Entities.Customer][SQL: exec CustomerInsert ?,?,?,?];System.Data.SqlClient.SqlException : 参数化查询 '(@p0 int,@p1 nvarchar(3),@p2 nvarchar(7),@p3 int)exec CustomerIn' 需要参数 '@p3',但未提供该参数”,这应该是参数问题,仔细看看原来的存储过程,参数位置有些问题。

    Step4:修改CustomerInsert存储过程,去掉SET NOCOUNT ON并把参数移动位置,代码片段如下:

    ALTER PROCEDURE [dbo].[CustomerInsert]
    (
        @Version int,
        @Firstname nvarchar(50) = NULL,
        @Lastname nvarchar(50) = NULL,
        @CustomerId int = NULL OUTPUT
    )
    AS
        INSERT INTO [Customer]
        (
            [Version],
            [Firstname],
            [Lastname]
        )
        VALUES
        (
            @Version,
            @Firstname,
            @Lastname
        )
        SELECT @CustomerId = SCOPE_IDENTITY();
        RETURN @@Error

    Step4:执行CreateCustomerTest()测试方法失败,还是以上问题,是不是生成器问题?

    这里,先看看NHibernate中最常用的两个内置生成器:

    native:根据底层数据库的能力选择identity、sequence 或者hilo中的一个。

    increment:生成类型为Int64、Int16或Int32的标识符,只当没有其他进程同时往同一个表插入数据时,能够保持唯一性。

    附:

    • identity:对DB2、MySQL、SQL Server、Sybase数据库内置标识字段提供支持。数据库返回的标识符用Convert.ChangeType加以转换,因此能够支持任何整型。
    • sequence:在DB2、PostgreSQL、Oracle数据库中使用序列或者Firebird的生成器。数据库返回的标识符用Convert.ChangeType加以转换,因此能够支持任何整型。
    • hilo:使用一个高/低位算法高效地生成Int64、Int32或Int16类型的标识符。给定一个表和字段(默认分别是 hibernate_unique_key 和next_hi)作为高位值的来源。高/低位算法生成的标识符只在一个特定的数据库中是唯一的。如果是用户提供的连接,不要使用此生成器。

    测试上面方法时,使用NHibernate内置生成器类型native,它根据数据库配置自动选择了identity类型,identity类型对表的主键提供支持,可以为主键插入显式值(标识增量加一)。我们在第一步就是使用NHibernate内置的生成器类型为主键增量加一,没有任何问题,但是看看CustomerInsert存储过程,主键CustomerId使用SCOPE_IDENTITY()插入显式值(标识增量加一),我们就没有必要使用内置的生成器来插入值了,把设置IDENTITY_INSERT为OFF,NHibernate正好提供了increment类型,生成类型仅仅是整型。

    解决方法有两种:

    • 1.修改存储过程:如果你在开发,你最好修改存储过程,因为在面向对象开发中,尽量不要使用存储过程,何况现在存储过程破坏了你的设计。
    • 2.修改主键生成类型:如果你已经部署好你的数据库,你没有权限修改存储过程的话,那么只要修改程序来将就存储过程了。

    还有一点注意:如果你主键生成器类型为“native”,那么存储过程的参数必须相一致。

    Step5:修改主键生成器类型

    为了演示,这里我们修改主键生成器类型,还可以总结错误信息。使用increment类型,关闭IDENTITY_INSERT,这时执行存储过程,由存储过程来为表'Customer'中的标识列(主键)插入显式值(标识增量加一)。

    <generator class ="increment"></generator>

    Step6:执行CreateCustomerTest()测试方法成功,生成SQL如下,其中p0是Version,p3是CustomerId

    exec CustomerInsert @p0,@p1,@p2,@p3;
    @p0 = '1', @p1 = 'YJing', @p2 = 'Lee' ,@p3 = '18' 
    错误提示

    其实我在调试过程中还有一些错误,这里总结一下:

    方案1:使用主键生成器类型为"native"

    • 直接创建对象:正常
    • 存储过程创建对象:参数化查询 '(@p0 int,@p1 nvarchar(5),@p2 nvarchar(7),@p3 int)exec CustomerIn' 需要参数 '@p3',但未提供该参数。解决方法:使用increment类型

    方案2:使用主键生成器类型为"increment"

    • 直接创建对象:当IDENTITY_INSERT设置为OFF时,不能为表'Customer'中的标识列插入显式值。解决方法:使用native类型
    • 存储过程创建对象:Batch update returned unexpected row count from update; actual row count: -1; expected: 1。解决方法:去掉SET NOCOUNT ON

    另外,如果你不喜欢存储过程的话,你也可以这样写,效果和使用存储过程一样。

    <sql-insert>INSERT INTO Customer (Version, Firstname, Lastname) VALUES (?,?,?)</sql-insert>

    但是这样的话,主键生成器类型必须为"increment"。

    3.更新对象

    Step1:为了比较,我们先执行UpdateCustomerTest()测试方法,没有使用存储过程下创建对象生成SQL语句如下:

    UPDATE Customer SET Version = @p0, Firstname = @p1, Lastname = @p2 
    WHERE CustomerId = @p3 AND Version = @p4;
    @p0 = '2', @p1 = 'YJingCnBlogs', @p2 = 'Lee', @p3 = '13', @p4 = '1'

    Step2:修改映射文件添加存储过程,打开Customer.hbm.xml映射文件,在Class元素下添加<sql-update>节点,调用CustomerUpdate存储过程,CustomerUpdate存储过程有四个参数,这里用四个问号表示:

    <sql-update>exec CustomerUpdate ?,?,?,?</sql-update>

    Step3:执行UpdateCustomerTest()测试方法,出现错误“Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [DomainModel.Entities.Customer#15]”,这个错误同删除对象存储过程一样,我们修改CustomerUpdate存储过程,去掉SET NOCOUNT ON,再次执行UpdateCustomerTest()测试方法,输出结果如下:

    exec CustomerUpdate @p0,@p1,@p2,@p3; 
    @p0 = '2', @p1 = 'YJingCnBlogs', @p2 = 'Lee', @p3 = '15', @p4 = '1'

    这段根据我们写的存储过程实质SQL语句为:

    UPDATE [Customer] SET [Version] = '2', [Firstname] = 'YJingCnBlogs',
        [Lastname] = 'Lee' WHERE [CustomerId] ='1'

    虽然NHibernate知道Version列是版本控制,它自动由原来的1更新为2,但是看看上面生成的SQL语句还是不怎么舒服,其@p4参数无缘无故的在那里。

    Step4:修改CustomerUpdate存储过程,把版本控制用好,编写如下:

    ALTER PROCEDURE [dbo].[CustomerUpdate]
    (
        @Version int,
        @Firstname nvarchar(50) = NULL,
        @Lastname nvarchar(50) = NULL,
        @CustomerId int,
        @OldVersion int
    )
    AS    
        UPDATE [Customer]
        SET
            [Version] = @Version,
            [Firstname] = @Firstname,
            [Lastname] = @Lastname
        WHERE 
            [CustomerId] = @CustomerId and [Version] =@OldVersion
        RETURN @@Error

    Step4:执行UpdateCustomerTest()测试方法,出现错误“过程或函数 'CustomerUpdate' 需要参数 '@OldVersion',但未提供该参数”,Oh!映射文件中调用存储过程忘了增加一个参数,现在是五个参数了!

    Step5:修改存储过程参数数量,打开映射文件在<sql-update>中添加一个参数即添加“,?”

    <sql-update>exec CustomerUpdate ?,?,?,?,?</sql-update>

    Step6:执行UpdateCustomerTest()测试方法,NHibernate生成语句如下,这下完美了。

    exec CustomerUpdate @p0,@p1,@p2,@p3,@p4; 
    @p0 = '4', @p1 = 'YJingCnBlogsCnBlogs', @p2 = 'Lee', @p3 = '13', @p4 = '3'

    另外,如果你不喜欢存储过程的话,你也可以这样写,效果和使用存储过程一样。

    <sql-update>UPDATE [Customer] SET [Version] = ?,[Firstname] = ?,[Lastname] = ? 
                      WHERE [CustomerId] =? and [Version] =?</sql-update>

    结语

    这一篇和上一篇介绍了如何使用存储过程删除对象、创建对象、更新对象,还有一种使用<sql-query>来调用存储过程,非常方便,下篇继续介绍。

    NHibernate Q&A

    下次继续分享NHibernate!

  • 相关阅读:
    SecureCRT远程控制ubuntu
    zedboard启动过程分析
    zedboard之ubuntu环境变量设置
    理解 pkg-config 工具
    linux下 tar解压 gz解压 bz2等各种解压文件使用方法
    zedboard搭建交叉编译环境
    一步一步学ZedBoard & Zynq(四):基于AXI Lite 总线的从设备IP设计
    zedboard 中SDK 修改串口设置(波特率。。。。)
    VC 2010下安装OpenCV2.4.4
    VS2010恢复默认编辑环境的设置
  • 原文地址:https://www.cnblogs.com/a282421083/p/13444739.html
Copyright © 2011-2022 走看看