网上有很多这样的问题分析,无非以下两种情况:
1、begin tran 后没有rollback tran 或commit tran
2、应用程序数据库连接事务不在一个会话中;
但以后我要说的情况有点特别,先说一下场景:
1、.net 开发环境
2、使用sqlconnection对象发起事务
3、事务内有多个存储过程
4、各存储过程内有自已的事务
总之就是sqlconnection事务嵌套存储过程事务,以下是演示代码,先看看这样做会有什么问题
以下是VB.NET代码,什么语言不重要,只要是用.net 环境和SqlConnection对象就符合条件
'事务嵌套测试
Sub testtrans()
Dim mycn As SqlConnection
Dim tran As SqlTransaction
Dim inputs As New Dictionary(Of String, Object)
Dim outputs As New Dictionary(Of String, SqlParameter)
mycn = obj.GetSqlConnection
obj.openSqlConnection(mycn)
Dim errMsg As String
Dim cmd As SqlCommand
Try
tran = mycn.BeginTransaction
'存储过程输入参数
inputs.Add("@test", 1)
'存储过程输出参数
outputs.Add("@result", New SqlParameter("@result", SqlDbType.VarChar, 10))
'执行这事出错,提示EXECUTE 后的事务计数指示 BEGIN 和 COMMIT
' 语句的数目不匹配。上一计数 = 1,当前计数 = 0。
obj.execProc("test_pro1", inputs, outputs, errMsg, tran)<span style="white-space:pre"> </span>'自定义的执行存储过程的方法
If tran.Connection IsNot Nothing Then
tran.Commit()
End If
Catch ex As Exception
If tran.Connection IsNot Nothing Then
tran.Rollback()
End If
Me.callBack(False, ex.Message)
Exit Sub
Finally
obj.closeSqlConnection(mycn)
End Try
以下是存储过程
ALTER PROCEDURE [dbo].[test_pro1]
@test INT,
@result VARCHAR(10) OUTPUT
AS
BEGIN TRANSACTION
SET @result='A'
DECLARE @aa INT
set @aa=5/0
ROLLBACK TRANSACTION
通过.net事务调用含事务的存储过程,只要存储过程触发rollback tranaction,会马上触发.net事务异常,即
---------------------
EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配。上一计数 = 1,当前计数 = 0。
---------------------
这样的异常,但实际上存储过程是按要求执行的,返回参数也返回预期值,也正确回滚了;
如何处理这种问题呢?比较简单,只要在catch对错误码准确判断即可,如下代码:
cmd.ExecuteNonQuery()
Catch ex As SqlException
If ex.Number <> 266 Then
errMsg = "执行" & procName & "出错,错误码:" & errcode
WriteLog(errMsg & ",详情:" & Chr(13) & ex.Message & Chr(13) & ex.StackTrace)
End If
266是事务计数器异常代码,是这个代码直接当正常即可,因为存储过程参数返回到上一级方法后,再判断参数值是否正确,若正常就继续,不正常就退出方法,当然最上一级sqlconnection要对事务做一下判断
Catch ex As Exception
If _tran IsNot Nothing Then
If _tran.Connection IsNot Nothing Then
_tran.Rollback()
End If
End If
obj.WriteLog("XX,错误代码:" & errnum & Chr(13) & ex.StackTrace)
Return obj.CallBack(False, "XX,错误代码:" & errnum)
Finally
obj.closeSqlConnection(cn)
End Try
注:sqlconnection的事务对象sqlTransaction,要用引用传值方式给内部执行存储过程的sqlCommand对象的transaction属性;
另外,我认为.net完全没有必要触发异常,.net的事务是要提交还是回滚,.net本来就提供了判断事务是否有效的方法,如果有效且没有其他异常,手动提交,反之回滚;