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'
另外,如果你不喜歡儲存過程的話,你也可以這樣寫,效果和使用儲存過程一樣。
<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之旅系列文章導航
下次繼續分享NHibernate!