- 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务汇总运行。
- 事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为
Sping支持的事务传播行为
| 传播属性 | 描述 |
|---|---|
| REQUIRED | 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行。 |
| REQUIRED_NEW | 当前的方法必须启动新事务,并在它自己的事务内运行。如果有事务正在运行,应该将它挂起。 |
| SUPPORTS | 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中 |
| NOT_SUPPORTED | 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起 |
| MANDATORY | 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常。 |
| NEVER | 当前的方法不应该运行在事务中。如果有运行的事务,则抛出异常。 |
| NESTED | 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。否则,就启动一个新的事务,并在它自己的事务内运行。 |
REQUIRED 传播行为
- 当bookService的purchase()方法被另一个事务方法checkout()调用时,它默认会在现有的事务内运行。这个默认的传播行为就是REQUIRED。因此在checkout()方法的开始和终止边界内只有一个事务。这个事务只在checkout()方法结束的时候被提交,结果用户一本书都买不了
- 事务传播属性可以在@Transactional注解的propagation属性中定义
REQUIRES_NEW 传播行为(实现买成功一本,第二本依然失败)
- 另一种常见的传播行为是REQUIRES_NEW.它表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。
例子程序
这里只贴出有差别的类,其他需要的文件均在上一篇博客
Cashier.java
package com.spring.tx;
import java.util.List;
public interface Cashier {
public void checkout(String username,List<String> isbns);
}
CashierImpl.java
package com.spring.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("cashier")
public class CashierImpl implements Cashier{
@Autowired
private BookShopService bookShopService;
@Override
public void checkout(String username, List<String> isbns) {
for(String isbn : isbns ){
bookShopService.purchase(username,isbn);
}
}
}
BookShopServiceImpl.java
package com.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:事务自己的事务,调用的事务方法的事务被挂起
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void purchase(String username, String isbn) {
//1.获取书的单价
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新书的库存
bookShopDao.updateBookStock(isbn);
//3.更新用户的余额
bookShopDao.updateUserAccount(username,price);
}
}
SpringTransactionTest.java
package com.spring.tx;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
public class SpringTransactionTest {
private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
private Cashier cashier = null;
{
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
cashier = ctx.getBean(Cashier.class);
}
public void testTransactionlPropagation(){
cashier.checkout("AA",Arrays.asList("1001","1002"));
}
public void testBookShopService(){
bookShopService.purchase("AA","1001");
}
public void testBookShopDaoFindPriceByIsbn(){
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
public void testBookShopDaoUpdateUserAccount(){
bookShopDao.updateUserAccount("AA",200);
}
public void testBookShopDaoUpdateBookStock(){
bookShopDao.updateBookStock("1001");
}
public static void main(String[] args){
/*new SpringTransactionTest().testBookShopDaoFindPriceByIsbn();
new SpringTransactionTest().testBookShopDaoUpdateBookStock();
new SpringTransactionTest().testBookShopDaoUpdateUserAccount();*/
//
new SpringTransactionTest().testTransactionlPropagation();
}
}
简单的补充:
这样的事务使得若买两本书的时候,钱不够,但是依然能够完成支付一本书,第二本书支付失败的情况。
而上一篇博客的情况,则会两本书都不能支付成功。