声明式事务的属性之传播行为
①介绍
- 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
②测试
@Controller
public class BookController {
@Autowired
private CheckoutService checkoutService;
public void checkout(Integer userId ,Integer[] bookIds){
checkoutService.checkout(userId,bookIds);
}
}
public interface CheckoutService {
/**
* 结账
* @param userId
* @param bookIds
*/
void checkout(Integer userId, Integer[] bookIds);
}
@Service
public class CheckoutServiceImpl implements CheckoutService {
@Autowired
private BookService bookService;
@Override
@Transactional
public void checkout(Integer userId, Integer[] bookIds) {
for (Integer bookId : bookIds) {
bookService.buyBook(userId,bookId);
}
}
}
public interface BookService {
/**
* 买书
* @param userId
* @param bookId
*/
void buyBook(Integer userId, Integer bookId);
}
@Service
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void buyBook(Integer userId, Integer bookId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId,price);
}
}
在数据库中将用户的余额修改为100元,书的库存都修改为100
③观察结果
- 可以通过@Transactional中的propagation属性设置事务传播行为
- 修改BookServiceImpl中buyBook()上,注解@Transactional的propagation属性
- @Transactional(propagation = Propagation.REQUIRED),默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行。经过观察,购买图书的方法buyBook()在checkout()中被调用,checkout()上有事务注解,因此在此事务中执行。所购买的两本图书的价格为80和50,而用户的余额为100,因此在购买第二本图书时余额不足失败,导致整个checkout()回滚,即只要有一本书买不了,就都买不了
- @Transactional(propagation = Propagation.REQUIRES_NEW),表示不管当前线程上是否有已经开启的事务,都要开启新事务。同样的场景,每次购买图书都是在buyBook()的事务中执行,因此第一本图书购买成功,事务结束,第二本图书购买失败,只在第二次的buyBook()中回滚,购买第一本图书不受影响,即能买几本就买几本