程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> NHibernate之旅(16):探索NHibernate中使用存儲過程(中)

NHibernate之旅(16):探索NHibernate中使用存儲過程(中)

編輯:關於.NET

本節內容

引入

實例分析

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>來調用存儲過程,非常方便,下篇繼續介紹。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved