嵌套事务的问题

本文介绍了Microsoft SQL Server中嵌套事务的特征,如忽略提交内部事务,根据外部事务操作决定内层事务提交或回滚;还说明了COMMIT和ROLLBACK语句的应用规则,以及@@TRANCOUNT函数记录事务嵌套级的作用,并提及解决嵌套显式事务问题的例子。

嵌套事务有几个特征, 帮助中这么说;

Microsoft® SQL Server™ 忽略提交内部事务。根据最外部事务结束时采取的操作,将提交或者回滚事务。如果提交外部事务,则内层嵌套的事务也会提交。如果回滚外部事务,则不论此前是否单独提交过内层事务,所有内层事务都将回滚。

对 COMMIT TRANSACTION 或 COMMIT WORK 的每个调用都应用于最后执行的 BEGIN TRANSACTION。如果嵌套 BEGIN TRANSACTION 语句,那么 COMMIT 语句只应用于最后一个嵌套的事务,也就是在最内层的事务。即使嵌套事务内部的 COMMIT TRANSACTION transaction_name 语句引用外部事务的事务名,该提交也只应用于最内层的事务。

ROLLBACK TRANSACTION 语句的 transaction_name 参数引用一组命名的嵌套事务的内层事务是非法的,transaction_name 只能引用最外部事务的事务名。如果在一组嵌套事务的任意级别执行使用外部事务名称的 ROLLBACK TRANSACTION transaction_name 语句,那么所有的嵌套事务都将回滚。如果在一组嵌套事务的任意级别执行没有 transaction_name 参数的 ROLLBACK WORK 或 ROLLBACK TRANSACTION 语句,那么它将回滚所有嵌套事务,包括最外部事务。

@@TRANCOUNT 函数记录当前事务的嵌套级。每个 BEGIN TRANSACTION 语句使 @@TRANCOUNT 加 1。每个 COMMIT TRANSACTION 或 COMMIT WORK 语句使 @@TRANCOUNT 减 1。没有事务名的 ROLLBACK WORK 或 ROLLBACK TRANSACTION 语句将回滚所有嵌套事务,并使 @@TRANCOUNT 减小到 0。使用一组嵌套事务中最外部事务的事务名称的 ROLLBACK TRANSACTION 将回滚所有嵌套事务,并使 @@TRANCOUNT 减到 0。在无法确定是否已经在事务中时,可以用 SELECT @@TRANCOUNT 语句确定 @@TRANCOUNT 是 1 还是更大。如果 @@TRANCOUNT 是 0,则表明不在事务中。

如何解决嵌套显式事务的问题,我举了一个例子:


--  create a table  for  testing

Create Table T1(v 
int  check (v < 100 ))      --  v必须小于100

-- create first proc  to  insert data

Create Proc InnerTrans
AS
Begin
    begin tran tran1
    insert into T1 values(
1 --  this should be ok
    
if  @@ error <> 0
        begin
            rollback tran tran1
            return 
- 1
        
end
    
    insert into T1 values(
200 --  违反了约束
    
if  @@ error <> 0
        begin
            rollback tran tran1
            return 
- 1
        
end
    commit tran tran1
    return 
1
    
-- ok here
End

Create Proc OuterTran
AS
Begin
    begin tran tran2
    insert into T1 values(
2 )         --  ok  now
    declare  @invokecode 
int
    exec @invokecode
= innerTrans
    
if (@invokecode =- 1 )        
        rollback tran tran2
    
else
        commit tran tran2
    
End

select   *  from T1
exec innerTrans    
--  worked ok
exec OuterTran    
-- does  not  work

--  How  to  Modify?


alter Proc InnerTrans
AS
Begin
    begin tran tran1
    save tran ttt    
-- savepoint here
    insert into T1 values(
1 --  this should be ok
    
if  @@ error <> 0
        begin
            rollback tran ttt
            commit tran tran1
             return 
- 1
        
end
    
    insert into T1 values(
200 --  违反了约束
    
if  @@ error <> 0
        begin
            rollback tran ttt
            commit tran tran1
            return 
- 1
        
end
    commit tran tran1
    return 
1
    
-- ok here
End
posted on 2005-03-12 15:00 montaque 阅读(1777) 评论(2)   编辑  收藏
href="http://www.cnblogs.com/montaque/Services/Pingback.aspx" rel="pingback"/>

评论

#  re: 嵌套事务的问题 2005-03-12 15:42 lostinet
我习惯判断@@TRANCOUNT然后选择是BEGIN TRANSACTION还是用SAVE TRANSACTION

例如:

--BEGIN TRANSACTION

SELECT @@TRANCOUNT

DECLARE @ROLLBACKIT BIT
DECLARE @SAVEPOINT NVARCHAR(36)
IF @@TRANCOUNT = 0 BEGIN TRANSACTION ELSE BEGIN SET @SAVEPOINT=NEWID() SAVE TRANSACTION @SAVEPOINT END

SELECT @SAVEPOINT
--DO SOME THING

IF @ROLLBACKIT=1
BEGIN
IF @SAVEPOINT IS NULL ROLLBACK TRANSACTION ELSE ROLLBACK TRANSACTION @SAVEPOINT
END
ELSE
BEGIN
IF @SAVEPOINT IS NULL COMMIT TRANSACTION
END

SELECT @@TRANCOUNT

--ROLLBACK TRANSACTION


如果在同一个命名范围中需要嵌套,那么就要是 @SAVEPOINT1,@SAVEPOINT2...了.
  
#  re: 嵌套事务的问题 2005-03-12 15:42 lostinet
我习惯判断@@TRANCOUNT然后选择是BEGIN TRANSACTION还是用SAVE TRANSACTION

例如:

--BEGIN TRANSACTION

SELECT @@TRANCOUNT

DECLARE @ROLLBACKIT BIT
DECLARE @SAVEPOINT NVARCHAR(36)
IF @@TRANCOUNT = 0 BEGIN TRANSACTION ELSE BEGIN SET @SAVEPOINT=NEWID() SAVE TRANSACTION @SAVEPOINT END

SELECT @SAVEPOINT
--DO SOME THING

IF @ROLLBACKIT=1
BEGIN
IF @SAVEPOINT IS NULL ROLLBACK TRANSACTION ELSE ROLLBACK TRANSACTION @SAVEPOINT
END
ELSE
BEGIN
IF @SAVEPOINT IS NULL COMMIT TRANSACTION
END

SELECT @@TRANCOUNT

--ROLLBACK TRANSACTION


如果在同一个命名范围中需要嵌套,那么就要是 @SAVEPOINT1,@SAVEPOINT2...了.
  
### Spring Boot 事务管理中的嵌套事务问题及解决方案 #### 嵌套事务的概念 在Spring Boot中,当一个事务方法内部调用了另一个同样带有`@Transactional`注解的方法时,默认情况下这不会创建一个新的事务上下文而是加入当前已存在的事务。这种行为被称为传播特性(propagation behavior),默认遵循的是`REQUIRED`模式[^1]。 #### 默认情况下的局限性 如果内层方法抛出了异常而外层未捕获,则整个事务会回滚;但如果期望即使外部失败也能让部分操作成功提交(即形成真正的子父关系),那么就需要特别处理因为按照默认配置这是不可能达成的效果[^2]。 #### 实现真正意义上的嵌套事务 为了达到上述目的——使两个不同级别的业务逻辑能够各自独立地决定是否要继续还是撤销其更改而不影响对方的状态——可以采取如下措施之一: ##### 方法一:调整传播属性 通过改变被标注函数上的`propagation`参数值为`REQUIRES_NEW`可以让每次进入该服务都开启全新的隔离环境来进行数据库交互活动,从而实现了所谓的“真·嵌套”。这种方式简单易行,在大多数场合下都能很好地解决问题[^3]。 ```java @Service public class OuterService { @Autowired private InnerService innerService; @Transactional public void outerMethod() throws Exception{ // 外部事务逻辑... try { innerService.innerMethod(); } catch (Exception e){ // 即便这里捕捉到了异常也不会阻止innerMethod所做的一切生效 } // 更多的外部事务逻辑... } } @Component class InnerService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void innerMethod(){ // 内部事务逻辑... } } ``` ##### 方法二:利用AOP切面编程绕过JDK动态代理限制 由于Java反射机制的原因,直接在一个类里边互相调用有@Transactional的方法并不会触发代理对象的功能,也就谈不上什么新的transaction context了。因此可以把涉及具体工作的那部分内容拆分出来放到其他bean里面去执行,这样就能保证每次都是经过代理后的实例参与进来,进而获得想要的结果。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值