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

本节内容

  • 引入
  • 实例分析
    • 2.创建对象
    • 3.更新对象
  • 结语

引入

上一篇,怎么使用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>来调用存储过程,非常方便,下篇继续介绍。

版权声明:本文为博主http://www.zuiniusn.com 原创文章,未经博主允许不得转载。

时间: 2024-10-13 21:57:53

NHibernate之旅(16):探索NHibernate中使用存储过程(中)的相关文章

Sql server中根据存储过程中的部分信息查找存储过程名称的方法【视图和Function】

1.查询的语句: select a.id,b.name,a.*,b.* from syscomments a join sysobjects b on a.id=b.id where b.xtype='P' and a.text like '%usp_cm%' b.xtype='P'指定在什么类型的范围进行搜索 '%usp_cm%'就是你能记得的存储过程中的内容. 2.查找类型: select distinct xtype from sysobjects 找到数据库中所有的对象类型 P是存储过程

oracle中PLSQL存储过程中如何使用逗号分隔的集合(逗号分隔字符串转换为一个集合)

原文: https://blogs.oracle.com/aramamoo/entry/how_to_split_comma_separated_string_and_pass_to_in_clause_of_select_statement 'SMITH,ALLEN,WARD,JONES'  为4个值的集合,在存储过程中需要怎么处理才能实现 in (值1,值2,值3,值4 ) 的效果: 下面的方法值得借鉴:下面的方式将一个逗号表达式变成一列的4行的集合来实现. select regexp_su

SQLServer 中的存储过程中判断临时表是否存在,存在则删除临时表

IF OBJECT_ID('TEMPDB..#BCROSSREFERENCE ') IS NOT NULL DROP TABLE #BCROSSREFERENCE IF OBJECT_ID('TEMPDB..#SCVTMP_BCUSTOMER') IS NOT NULL DROP TABLE #SCVTMP_BCUSTOMER IF OBJECT_ID('TEMPDB..#PDS_CREATE_SCV_TEMP') IS NOT NULL DROP TABLE #PDS_CREATE_SCV_T

NHibernate之旅系列文章导航

NHibernate之旅系列文章导航 宣传语 NHibernate.NHibernate教程.NHibernate入门.NHibernate下载.NHibernate教程中文版.NHibernate实例.NHibernate2.0.NHibernate2.0教程.NHibernate之旅.NHibernate工具 我的NHibernate全部文章在http://www.cnblogs.com/lyj/category/129155.html 导游 NHibernate是把Java的Hiberna

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

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

NHibernate之旅(4):探索查询之条件查询(Criteria Query)

本节内容 NHibernate中的查询方法 条件查询(Criteria Query) 1.创建ICriteria实例 2.结果集限制 3.结果集排序 4.一些说明 根据示例查询(Query By Example) 实例分析 结语 上一节,我们介绍了NHibernate查询语言的一种:NHibernate查询语言(HQL,NHibernate Query Language),这一节介绍一下条件查询(Criteria API). NHibernate中的查询方法 在NHibernate中提供了三种查

[转]NHibernate之旅(10):探索父子(一对多)关联查询

本节内容 关联查询引入 一对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 关联查询引入 在NHibernate中提供了三种查询方式给我们选择:NHibernate查询语言(HQL,NHibernate Query Language).条件查询(Criteria API,Query By Example(QBE)是Criteria API的一种特殊情况).原生SQL(Literal SQL,T-SQL.PL/SQL).这一节分别使用这三种方式来

NHibernate之旅(21):探索对象状态

本节内容 引入 对象状态 对象状态转换 结语 引入 在程序运行过程中使用对象的方式对数据库进行操作,这必然会产生一系列的持久化类的实例对象.这些对象可能是刚刚创建并准备存储的,也可能是从数据库中查询的,为了区分这些对象,根据对象和当前会话的关联状态,我们可以把对象分为三种: 瞬时对象:对象刚刚建立.该对象在数据库中没有记录,也不在ISession缓存中.如果该对象是自动生成主键,则该对象的对象标识符为空. 持久化对象:对象已经通过NHibernate进行了持久化,数据库中已经存在对应的记录.如果

NHibernate之旅(18):初探代码生成工具使用

本节内容 引入 代码生成工具 结语 引入 我们花了大量的篇幅介绍了相关NHibernate的知识.一直都是带着大家手动编写代码,首先创建数据库架构.然后编写持久化类和映射文件,最后编写数据操作方法.測试方法. 这是典型的数据库驱动开发(DbDD,Database-Driven Developent)技术.可是自己不是这样做的,我先编写持久化类和映射文件,然后偷偷的使用SchemaExport工具把数据库生成了.按上面的步骤写文章的,关于SchemaExport工具就是下一篇的事情了,这篇说说利用