什么是事务的传播行为?
当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行.
事务的传播行为可以由传播属性指定. Spring 定义了 7 种类传播行为.
这里比较重要的是前两个。
(本文出自:http://my.oschina.net/happyBKs/blog/513390)
REQUIRED 传播行为
当 bookService 的 purchase() 方法被另一个事务方法 checkout() 调用时, 它默认会在现有的事务内运行. 这个默认的传播行为就是 REQUIRED. 因此在 checkout() 方法的开始和终止边界内只有一个事务. 这个事务只在 checkout() 方法结束的时候被提交, 结果用户一本书都买不了
事务传播属性可以在 @Transactional 注解的 propagation 属性中定义
REQUIRES_NEW 传播行为
另一种常见的传播行为是 REQUIRES_NEW. 它表示该方法必须启动一个新事务, 并在自己的事务内运行. 如果有事务在运行, 就应该先挂起它.
注意:以上传播行为的类别是在@Transactional注解上配置propagation来完成的,如果没有配置默认情况是REQUIRED。
HappyBKs的打比方: :)
是不是还是有点晕?我打个比方吧,好比,你和一个哥们去饭前吃饭(一个事务),发现另一个哥们正在饭店吃饭(另一个事务),这时候,你过去有两种选择:一,我们俩加入那个正在吃饭的哥们的饭桌大家一起吃,这样,我们事务就被融入了调用我们的事务,这就是REQUIRED传播行为;二,我们不打扰他,只是打个招呼,我们自己开一桌吃饭,这样我们自己有个独立的事务,这种方式就是REQUIRES_NEW 传播行为。
我们还是用例子来说明什么是事务的传播行为吧。
假如我们在上面的例子中加入一个新的需求:一个用户一次买多本,如果买多本的操作也是事务,那么则存在事务中调用事务的情况。这样的事务该如何管理。
新定义 Cashier 接口: 表示客户的结账操作
修改数据表信息如下, 目的是用户 Tom 在结账时, 余额只能支付第一本书, 不够支付第二本书:
代码编写如下:
package com.happBKs.spring.tx; import java.util.List; public interface Cashier { public void checkout(String username, List<String> isbns); }
package com.happBKs.spring.tx; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("cashier") public class CashierImpl implements Cashier { @Autowired private BookShopService bookShopService; @Transactional public void checkout(String username, List<String> isbns) { // TODO Auto-generated method stub for(String isbn:isbns){ bookShopService.purchase(username, isbn); } } }
测试程序:
package com.happBKs.spring.tx; import java.util.Arrays; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TransactionTest { private ApplicationContext ctx=null; private BookShopDao bookShopDao; private BookShopService bookShopService; private Cashier cashier=null; { ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); bookShopDao=ctx.getBean(BookShopDao.class); bookShopService=ctx.getBean(BookShopService.class); cashier=ctx.getBean(Cashier.class); } @Test public void test5(){ cashier.checkout("HappyBks", Arrays.asList("1001","1002")); } }
好,我们先把数据库中的数据填好:
运行之后,一切正常:
这个方法没问题。那么现在如果我再买一次,会怎么样?HappBKs只有120元了,第一本可以买,第二本金额不足了。那么整个checkout方法作为一个事务会如何管理呢,我第一本的购买事务会不会完成?还是回滚?
好,在调用一次test5试试吧。
看到了吧,由于默认事务的传播行为是REQUIRED ,所以被调用的事务(即两个买一本书的事务)被融入到了调用它们的事务(即checkout方法)中,作为一个事务,那么当买第二本书出现余额不足的异常,整个事务将被回滚。
这里需要注意:在指定传播行为的时候,需要指定事务传播行为类型的时候应该在被调用事务的方法上的事务注解上进行设置。所以这里的例子中应该在如下方法上进行修改,比如这里我们将默认改为REQUIRED 。(其实默认就是REQUIRED ,写不写都一样)。完整的例子请见前一篇博客文章。
package com.happBKs.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事务注解 //使用propagation指定事务的传播行为,即当前事务的方法被另一个事务方法调用时,如何使用是事务。 //使用另外一个方法的事务(REQUIRED)?还是另开一个新事物?(REQUIRES_NEW) //默认REQUIRED @Transactional(propagation=Propagation.REQUIRED) public void purchase(String username, String isbn) { // TODO Auto-generated method stub //1. 获取书的单价 int price=bookShopDao.findBookPriceIsdn(isbn); //2. 更新书的库存 bookShopDao.updateBookStock(isbn); //3. 更新用户余额 bookShopDao.updateUserAccount(username, price); } }
好,如果我们现在将其修改为REQUIRES_NEW 传播行为。
package com.happBKs.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事务注解 //使用propagation指定事务的传播行为,即当前事务的方法被另一个事务方法调用时,如何使用是事务。 //使用另外一个方法的事务(REQUIRED)?还是另开一个新事物?(REQUIRES_NEW) //REQUIRES_NEW:事务自己的事务,调用该事物方法的事务被挂起 @Transactional(propagation=Propagation.REQUIRES_NEW) public void purchase(String username, String isbn) { // TODO Auto-generated method stub //1. 获取书的单价 int price=bookShopDao.findBookPriceIsdn(isbn); //2. 更新书的库存 bookShopDao.updateBookStock(isbn); //3. 更新用户余额 bookShopDao.updateUserAccount(username, price); } }
我们再来运行一次:
看到了吧,当被调用事务的传播行为改为REQUIRES_NEW,被调用事务建立一个自己的新事务,该事物执行期间,被调用事务是挂起的,执行完成之后,才返回被调用事务checkout。
原文:http://my.oschina.net/happyBKs/blog/513390