多種方法實現異常捕獲和事務一致性

SQL Server】多種方法實現異常捕獲和事務一致性

“事務是單個的工作單元。如果某一事務成功,則在該事務中進行的所有數據修改均會提交,成爲數據庫中的永久組成部分。如果事務遇到錯誤且必須取消或回滾,則所有數據修改均被清除。”

因爲事務的這個特性,在應用開發實現業務邏輯的時候,我們通常會事務來控制多個操作的一致性。例如:我們在訂單表插入一條銷售記錄,就必須修改產品的庫存。要麼都成功,要麼都失敗,否則就會導致數據庫中的庫存和實際庫存不一致。

應用程序訪問數據庫,可以直接執行語句或者使用存儲過程。下文我們將介紹通過應用程序或者存儲過程實現事務,以及在SQL Server中的多種異常捕獲的方法。

方法一:在.Net應用程序中實現事務

.Net應用程序中使用SqlTransaction類實例化一個事務,還可以指定事務的隔離級別。快照隔離是SQL Server 2005新增的一種隔離級別。可以允許在修改數據的同時,允許其他事務進行查詢。

通過Try…Catch…判斷異常,在Catch中捕獲到異常之後進行回滾。

 
/*===========================================================================
在程序中控制事務,以.Net 爲例
===========================================================================*/

string connString =    
"Data souce=.;user id=sa;password=password;database=NorhtwindCS";
SqlConnection conn = new SqlConnection(connString);
string sql1 = "Insert 訂單明細Values(@訂單ID,@產品ID,@單價,@數量,0)";
string sql2 = "Update 產品Set 庫存量=庫存量-@數量Where 產品Id = @產品Id";                
SqlCommand cmd1 = new SqlCommand(sql1, conn);
SqlCommand cmd2 = new SqlCommand(sql2, conn);
cmd1.Parameters.Add("@訂單ID", SqlDbType.Int).Value = Int32.Parse(TextBox1.Text);
cmd1.Parameters.Add("@產品ID", SqlDbType.Int).Value = Int32.Parse(TextBox2.Text);
cmd1.Parameters.Add("@單價", SqlDbType.Decimal).Value =    
Convert.ToDecimal(TextBox3.Text);
cmd1.Parameters.Add("@數量", SqlDbType.Int).Value = Int32.Parse(TextBox4.Text);
cmd2.Parameters.Add("@產品Id", SqlDbType.Int).Value = Int32.Parse(TextBox2.Text);
cmd2.Parameters.Add("@數量", SqlDbType.Int).Value = Int32.Parse(TextBox4.Text);
                
conn.Open();
SqlTransaction tran = conn.BeginTransaction(IsolationLevel.Snapshot);
cmd1.Transaction = tran;
cmd2.Transaction = tran;
try
{
             cmd1.ExecuteNonQuery();
             cmd2.ExecuteNonQuery();
             tran.Commit();
             Response.Write("添加訂單明細並修改庫存成功!");
}
catch (Exception ex)
{
             tran.Rollback();
             Response.Write("事務出現了異常,自動回滾");
             Response.Write("<br>");
             Response.Write(ex.Message.ToString());
}
finally
{
             conn.Close();
}
 

方法二:在存儲過程中實現事務

可以把事務的開啓、判斷和回滾,數據的操作等整個業務邏輯都放在存儲過程中,應用程序只需要傳遞參數即可。需要的話也可以把產生的異常信息傳遞到應用程序。
Begin Tran
  Update 產品 Set 庫存量=庫存量-@Quantity    Where 產品Id = @ProductId
  Insert 訂單(客戶Id,僱員Id,訂購日期)    Values (@CustId,@EmpId,Getdate())
  Insert 訂單明細 Values (@@Identity,@ProductId,@Unitprice ,@Quantity,0)
Commit
 
上面的這三句話一起執行,也不能保證三筆操作要麼都成功,要麼都失敗。因爲其中一條語句在執行出錯的時候,只會回滾該語句,而不是整個事務,因此還需要增加異常判斷並回滾事務的代碼。

在存儲過程中實現異常的捕獲和事務的回滾也有多種做法。

1)使用@@Error判斷異常

      @@Error是一個系統變量,但是隻能返回當前會話最後一句話的執行狀況。 0表示成功,非0表示失敗。所以我們只能在事務中的每一句話執行完成之後,進行一個判斷。

      下面的代碼中還使用了goto語句進行跳轉。

 
Create Proc usp_NewOrder
  @CustId nchar(5),@EmpId int,@ProductId int,@Quantity int,@Unitprice money
AS
Begin Tran
  Update 產品 Set 庫存量=庫存量-@Quantity Where 產品Id = @ProductId
  If @@error <> 0    goto rb
  Insert 訂單(客戶Id,僱員Id,訂購日期) Values (@CustId,@EmpId,Getdate())
  If @@error <> 0    goto rb
  Insert 訂單明細 Values(@@Identity,@ProductId,@Unitprice,@Quantity,0)
  If @@error <> 0    goto rb
Commit
goto endc
rb:
        rollback
endc:
Go

 

2)使用XAct_Abort開關實現自動回滾異常事務

      XAct_Abort是一個會話開關,可以指定當 Transact-SQL 語句出現運行時錯誤時,SQL Server 是否自動回滾到當前事務。

SET XACT_ABORT ON 時,如果執行 Transact-SQL 語句產生運行時錯誤,則整個事務將終止並回滾。

SET XACT_ABORT OFF 時,有時只回滾產生錯誤的 Transact-SQL 語句,而事務將繼續進行處理。如果錯誤很嚴重,那麼即使 SET XACT_ABORT OFF,也可能回滾整個事務。OFF 是默認設置。

 
Create Proc usp_NewOrder
  @CustId nchar(5),@EmpId int,@ProductId int,@Quantity int,@Unitprice money
AS
Set XAct_Abort ON
Begin Tran
  Update 產品 Set 庫存量=庫存量-@Quantity Where 產品Id = @ProductId
  Insert 訂單(客戶Id,僱員Id,訂購日期) Values (@CustId,@EmpId,Getdate())
  Insert 訂單明細 Values    (@@Identity,@ProductId,@Unitprice,@Quantity,0)
Commit
Go
 
 

3)使用SQL 2005新增異常捕獲方法

      SQL Server 2005及以後版本的SQL Server新增了一種Begin Try..End TryBegin Catch…End Catch 的異常處理語法。在Begin Catch…End Catch中還可以使用Error_Number()函數和Error_Message()函數返回錯誤號和錯誤消息,類似於應用程序中的Try … Catch語法

      這種異常處理的方法可以用來事務的異常捕獲和回滾,而且也能保證代碼即使在出現異常的時候,不會出錯。

 
Create Proc usp_NewOrder
  @CustId nchar(5),@EmpId int,@ProductId int,@Quantity int,@Unitprice money,
  @Error nvarchar(2000) OUTPUT
AS
Begin Try
        Begin Tran
        Update 產品 Set 庫存量=庫存量-@Quantity    Where 產品Id = @ProductId
        Insert 訂單(客戶Id,僱員Id,訂購日期)    Values (@CustId,@EmpId,Getdate())
        Insert 訂單明細 Values (@@Identity,@ProductId,@Unitprice ,@Quantity,0)
        Commit
End Try
Begin Catch
        rollback
        Set @Error=Convert(nvarchar,Error_Number()) +'--' + Error_Message()
End Catch
Go
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章