在EJB和Spring中何为事务传播

在任意复杂度程序结构中,如果把视角仅仅局限于一个线程的话,你会发现程序总是一个方法调用一个另外一个方法,层层次次嵌套,嵌套层次的上限受栈容量限制。这是大家所熟知的栈模型,在这里为了便于间接,把调用方称呼为外层方法,而把被调用方称为内层调用。如果(1)仅考虑应用程序代码而不考虑操作系统内部代码的话(2)不考虑C++语言的全局/静态对象的构造和析构函数。毫无疑问,所有的编程语言中,最外层的方法总是main方法,最内层的方法总是当前调试断点所定位的方法。这个模型不难理解。
为什么强调把视角仅仅局限于一个线程呢?那是因为对于多线程程序而言,每一个线程都有一个独立的栈,不同线程各自的栈是彼此无关的,每个线程的方法调用嵌套关系也是无关的。所以,外层代码和内层代码这两个概念本身仅仅对一个线程内部有意义。
设想,如果外层方法开始后打开了一个数据库连接,结束前关闭了这个数据库连接,在这个过程之中,调用了另外一个方法

private Connection con;
public void outer() {
    Connection oldCon = this.con; //记住成员变量的旧值
    try (Connection con = this.dataSource.getConnection() ) {
        this.con = con; //让成员变量记住连接
        调用内层方法之前操作数据库
        this.inner(); //调用内存方法
        调用内层方法后前操作数据库
} finallay {
    this.con = oldCon; //恢复成员变量
}
}
private void inner() {
     当我被调用的时候,其实外层方法已经打开了一个数据库连接,
     并且外层方法还特意在这个时间段内用this.con成员变量记忆了这个连接
     所以外层方法的连接我是拿得到的

   但是!我好纠结啊,我面临如下的选择。

(1) 我该直接借用外层方法的连接操作数据库呢?
try (Statement stmt = this.con.create…) {
        …
}
this.innerInner(); //调用更内层的方法2) 还是我不管它,我坚持自己新开一个连接操作数据库呢(如下代码)?
Connection oldCon = this.con; //记住成员变量的旧值
            try (Connection con = this.dataSource.getConnection() ) {
                this.con = con; //让成员变量记住连接
                //操作数据库
        try (Statement stmt = …) {
            …
}
        this.innerInner(); //调用更内层的方法
                //调用内层方法后前操作数据库
} finallay {
            this.con = oldCon; //恢复成员变量
}
}

我们看到内层方法存在一个纠结,必须做出一个选择。这个选择就叫事务传播。
当然,我承认,这个例子很无聊,因为外层和内层都是同一个对象的方法;更普遍的情况是,外层和内层是不同对象的方法。但是,使用同一个类的多个方法来形成内外层嵌套可以用最简单的语法展现我想表达的意思。如果是不同模块之间的嵌套调用,想让外层的连接对象对内层可见的话,语法可就没这么简单了,不好讲解了。
最后我再说明一点,这一节的标题叫“在EJB和Spring中何为事务传播”而不是“何为事务传播”,原因是COM+的事务传播的概念不是相对于函数调用链的,而是相对于COM+组件创建链的(D组件是被C组件创建的,C组件是被B组件创建出来的,B组件是被A组件创建出来的。。。),这是一个很不自然的,很晦涩的传播机制,毕竟,作为第一个吃螃蟹的人,COM+难以一次做到最完美的设计。但是,在EJB和Spring中事务传播机制是相对方法调用链而言的,这条链明显好理解得多,这就是为什么EJB和Spring要做出这个改进的原因。
Spring事务传播机制
REQUIRED
【我要用事务,如果外层的已有事务,我会重用之】
(1) 如果外层已经具备一个连接,且该连接开启了事务,那么借用它
(2) 否则自己创建一个连接并开启事务
REQUIRES_NEW
【我要用事务,但我决不和外层事务有瓜葛,我要自己创建绝对为自己所独享的事务】
不分青红皂白,总是自己建立一个连接并开启事务
SUPPORTS
【我是一个破罐子破摔的懒虫,懒到连用不用事务对我都不关心的地步了,只要外层有连接,不管它有没有事务我都将就着借来用】
(1) 如果外层已经具备一个连接,无论该连接是否开启了事务,都借用它
(2) 否则自己创建一个连接但不开启事务
NOT_SUPPORTED
【我不用事务,如果外层的已有连接但没开事务,我会重用之】
(1) 如果外层已经具备一个连接,但该连接并未开启事务,那么借用它
(2) 否则,自己创建一个连接但不开启事务
MANDATORY
【我要用事务但绝不自己动手,必须外层弄好拿给我用;否则,我要闹事】
(1) 如果外层不具备连接,抛出异常
(2) 如果外层具备一个连接,但该连接并未开启事务,抛出异常
(3) 总是借用外层开启了事务的连接
NEVER
【我不用事务,并且我还讨厌外层使用事务,如果外层有事务,我会抗议】
(1) 如果外层具备一个连接,且该连接开启了事务,抛出异常
(2) 如果外层具备一个连接,但该连接并未开启事务,那么借用它
(3) 如果外层不具备连接,自己创建一个连接但不开启事务
NESTED
【我和REQUIRES_NEW很像,但我比它聪明点,对数据库也有点挑三捡四】
(1) 如果外层不具备一个连接,那么自己创建一个连接并开启事务
(2) 如果外层具备一个连接,但该连接并未开启事务,那么自己创建一个连接并开启事务
(3) 如果外层具备一个连接,且该连接开启了事务,那么借用此连接开启一个嵌套子事务,此子事务的提交/回滚和父事务无关。注意:嵌套这个功能仅仅被少量数据库产品支持,大多数数据库仅仅支持平坦事务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值