论坛首页 Java企业应用论坛

解惑 spring 嵌套事务

浏览 159962 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-11-27  
很好的分析。
终于弄清楚nested和required_new的区别了
0 请登录后投票
   发表时间:2006-11-27  
分析的很好,现在市面上的书对此都不是讲的很透彻!
0 请登录后投票
   发表时间:2006-11-27  
together 写道
Feiing 写道
当然, 就算所有嵌套事务都已经成功, 外部事务还是可能因为嵌套事务的执行结果而导致失败,  此时整个事务都要 roll back ,这也是嵌套事务的重要特性之一, 即外部事务和嵌套事务互相影响

反过来说,所有嵌套事务都执行成功,外部事务也执行成功。那么发部事务要发送commit请求给各个嵌套事务,以同时提交,是这个意思吗?


按照定义是这样的

together 写道

那么对于数据库来说,它相当于在瞬间处理多个事务的commit,在中间的任何一步也是有可能出错的。那这个时候整个事务的回滚如何进行呢?


因为嵌套事务都有自己的 savepoint, 所以整个事务都可以回滚, 至于 savepoint 的具体实现, 那就是数据库厂商的事了.

但是嵌套事务可能会引起效率问题, 如果没有特殊的需求, PROPAGATION_REQUIRED 已经足够了
0 请登录后投票
   发表时间:2006-11-28  
个人理解只有需要根据子事务的执行情况进行分支处理的情况下才是nested的用武之地
savepoint是嵌套事务回滚的实现方式 需要注意的是使用它的限制条件

而required_new就更少用到了 一般情况下可以将此部分代码放在事务之外执行 实在剥离不开才会用到
0 请登录后投票
   发表时间:2006-11-28  
大家都不明白嵌套事务,可见这个东西很少用。

做到现在还没有碰到过需要使用嵌套事务的地方。
0 请登录后投票
   发表时间:2006-11-28  
savepoint支持是JDBC 3.0规定必须支持的特性么?不知道我们常用的数据库哪个版本配合哪个驱动支持?
还有,看AbstractPlatformTransactionManager代码,嵌套事务还可以通过JTA的嵌套事务支持,这种用法哪个应用程序服务器支持呢?
看完这篇文章概念上感觉明白了,就是不知道背后支持怎么样:D
手里项目还没有遇到过需要嵌套事务的情况,有机会尝试一下。
0 请登录后投票
   发表时间:2006-11-28  
大家请注意 Juergen Hoeller 的原文

引用

PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. What will happen is that a savepoint will be taken at the start of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. The nested transaction is part of of the outer transaction, so it will only be committed at the end of of the outer transaction.

Nested transactions essentially allow to try some execution subpaths as subtransactions: rolling back to the state at the beginning of the failed subpath, continuing with another subpath or with the main execution path there - all within one isolated transaction, and not losing any previous work done within the outer transaction.

For example, consider parsing a very large input file consisting of account transfer blocks: The entire file should essentially be parsed within one transaction, with one single commit at the end. But if a block fails, its transfers need to be rolled back, writing a failure marker somewhere. You could either start over the entire transaction every time a block fails, remembering which blocks to skip - or you mark each block as a nested transaction, only rolling back that specific set of operations, keeping the previous work of the outer transaction. The latter is of course much more efficient, in particular when a block at the end of the file fails.

Rolling back the entire transaction is the choice of the demarcation code/config that started the outer transaction.

So if an inner transaction throws an exception and is supposed to be rolled back (according to the rollback rules), the transaction will get rolled back to the savepoint taken at the start of the inner transaction. The immediate calling code can then decide to catch the exception and proceed down some other path within the outer transaction.

If the code that called the inner transaction lets the exception propagate up the call chain, the exception will eventually reach the demarcation code of the outer transaction. At that point, the rollback rules of the outer transaction decide whether to trigger a rollback. That would be a rollback of the entire outer transaction then.

So essentially, it depends on your exception handling. If you catch the exception thrown by the inner transaction, you can proceed down some other path within the outer transaction. If you let the exception propagate up the call chain, it's eventually gonna cause a rollback of the entire outer transaction.



PROPAGATION_NESTED 默认情况下只是回滚到子事务开始的地方(savepoint) 父事务不回滚

如果想使用PROPAGATION_NESTED 又想回滚父事务 该怎么办呢?

ServiceA {   
       
    /**  
     * 事务属性配置为 PROPAGATION_NESTED  
     */  
    void methodA() {   
        // do A ...

        ServiceB.methodB(); 
  
        // do A ...
    }   
  
}


OR

ServiceA {   
       
    /**  
     * 事务属性配置为 PROPAGATION_NESTED  
     */  
    void methodA() {   
        // do A ...
        try {   
            ServiceB.methodB();   
        } catch (Exception ex) {   
            throw ex;
        }   
        // do A ...
    }   
  
}



比如有两套系统 一套使用ibatis 一套使用hibernate

当两套系统进行集成整合的时候就会遇到事务嵌套的问题

比如想在 DataSourceTransactionManager 中调用hibernate操作 这时候麻烦就来了

有一种做法是将两套系统都交由 HibernateTransactionManager

因为 HibernateTransactionManager 是可以管理jdbc操作的

不知道将两套系统都交由 DataSourceTransactionManager 会怎么样?

或者两个事务管理器并存 用PROPAGATION_NESTED? 回头试一试...
0 请登录后投票
   发表时间:2006-11-28  
看了两遍,真是有收获,虽然暂时还没遇到需要嵌套事务的地方
0 请登录后投票
   发表时间:2006-11-29  
好文呀
0 请登录后投票
   发表时间:2006-11-29  
引用

在我所见过的误解中, 最常见的是下面这种:


引用

假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下

/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
// 调用 ServiceB 的方法
ServiceB.methodB();
}

那么如果 ServiceB 的 methodB 如果配置了事务, 就必须配置为 PROPAGATION_NESTED



这种想法可能害了不少人, 认为 Service 之间应该避免互相调用, 其实根本不用担心这点,PROPAGATION_REQUIRED 已经说得很明白,
如果当前线程中已经存在事务, 方法调用会加入此事务, 果当前没有事务,就新建一个事务, 所以 ServiceB#methodB() 的事务只要遵循最普通的规则配置为 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我们称之为内部事务, 为下文打下基础) 抛了异常, 那么 ServiceA#methodA(我们称之为外部事务) 如果没有特殊配置此异常时事务提交 (即 +MyCheckedException的用法), 那么整个事务是一定要 rollback 的, 什么 Service 只能调 Dao 之类的言论纯属无稽之谈, spring 只负责配置了事务属性方法的拦截, 它怎么知道你这个方法是在 Service 还是 Dao 里 ?


看了这篇文章后很多人说概念上清楚了,我怎么就觉得很迷糊呢!?仔细琢磨了一下,说一下我的想法和疑惑。
我觉得上面的这种想法其实是合理的,楼主把针对持久层的事务和程序的事务是不是较在一起了呢?(当然我也怀疑是自己犯本质上的错误了)
抛开数据库来理解,如果serviceA中的methodA中有调用ServiceB methodB,如果ServiceB的methodB调用出错需要rollback,那么理所当然,该方法所做的事情都应该取消,回到调用前,即rollback本来就该是这个样子的,哪来的脏数据呢?数据库的rollback是不会有数据库脏数据生成的吧
PROPAGATION_REQUIRES_NEW 的配置当然会有脏数据问题,因为正如他的英文意思,本身就是新的事务的意思呀。
这样说来Service 调用dao的说法也有其合理性,把业务逻辑和持久层很好的分离出来从而更好的剥离业务上的事务和持久层的事务。
我现在的理解是 事务只是保证一系列操作的原子属性,按照楼主的意思我想spring是不是不能处理多数据源情况下的事务处理呢。

经验很少,还望各位不啻指教,感谢了。








0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics