spring事务的传播方式
文章目录
- 文章简介:
- REQUIRED(默认是这个):
- SUPPORTS:
- MANDATORY:
- REQUIRES_NEW:
- NOT_SUPPORTED:
- NEVER
- NESTED
- 增强理解
- 例1,NOT_SUPPORTED
- 例2,REQUIRED_NEW
- 例3,REQUIRED
文章简介:
在写spring几个有趣的小甜点中的事务失效的时候被事务传播机制卡住了,觉得有必要学习整理一下事务的传播。
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。
REQUIRED(默认是这个):
这个方法必须运行在事务中,如果事务存在,则会运行在当前事务中,否则会新开一个事务。
什么意思?从字面意思根本看不懂,举例最实在了。
controller
@RequestMapping("tran")public void hello(){Order order = new Order();order.setName("order1");userService.createOrder(order);}
service
@Override@Transactionalpublic void createOrder(Order order) {orderMapper.save(order);try {UserService proxy= (UserService)AopContext.currentProxy();proxy.sendMsg();}catch (Exception e){System.out.println("rollback");}}@Override@Transactional(propagation= Propagation.REQUIRED)public void sendMsg(){msgMapper.save(new Msg("你已经下单了"));//调用短信网关throw new RuntimeException();//运行时异常,编译时上层可以不捕捉}
createOrder外层事务加了@Transactional,调用sendMsg,sendMsg事务传播方式为REQUIRED,如果createOrder外层事务存在,则sendMsg会运行createOrder事务中,这也就是为什么在sendMsg里面抛运行时异常会.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only。解决方法在spring几个有趣的小甜点事务失效部分有详细解说。
上面是外层createOrder加了 @Transactional的情况,如果外层事务不加这个注解,那么内层事务sendMsg会自己开一个事务。此时的结果应该是订单表add进去了,短信表被回滚。
SUPPORTS:
这个方法不需要事务,但是如果存在事务,就在事务中运行。
createOrder没有事务,所以sendMsg也没有事务,最终的结果就是两张表都add进去了。
@Overridepublic void createOrder(Order order) {orderMapper.save(order);try {UserService proxy= (UserService)AopContext.currentProxy();proxy.sendMsg();}catch (Exception e){System.out.println("rollback");}}@Override@Transactional(propagation= Propagation.SUPPORTS)public void sendMsg(){msgMapper.save(new Msg("你已经下单了"));//调用短信网关throw new RuntimeException();//运行时异常,编译时上层可以不捕捉}
如果给createOrder加事务注解,那么两个方法就都有事务了,而且是在同一个事务中运行,不出意外又是UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
MANDATORY:
该方法必须在事务中运行,如果没有事务,抛异常IllegalTransactionStateException
@Override@Transactionalpublic void createOrder(Order order) {orderMapper.save(order);try {UserService proxy= (UserService)AopContext.currentProxy();proxy.sendMsg();}catch (Exception e){System.out.println("rollback");}}@Override@Transactional(propagation= Propagation.MANDATORY)public void sendMsg(){msgMapper.save(new Msg("你已经下单了"));//调用短信网关throw new RuntimeException();//运行时异常,编译时上层可以不捕捉}
两个方法都有事务了,而且是在同一个事务中运行,不出意外又是UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
如果把createOrder改成这样
@Override
public void createOrder(Order order) {orderMapper.save(order);try {UserService proxy= (UserService)AopContext.currentProxy();proxy.sendMsg();}catch (Exception e){System.out.println("rollback");e.printStackTrace();//打印异常看看}}
会抛出IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’
REQUIRES_NEW:
该方法必须运行在新事务中
@Override@Transactionalpublic void createOrder(Order order) {orderMapper.save(order);try {UserService proxy= (UserService)AopContext.currentProxy();proxy.sendMsg();}catch (Exception e){System.out.println("rollback");e.printStackTrace();}}@Override@Transactional(propagation= Propagation.REQUIRES_NEW)public void sendMsg(){msgMapper.save(new Msg("你已经下单了"));//调用短信网关throw new RuntimeException();//运行时异常,编译时上层可以不捕捉}
很明显无论createOrder加不加事务,sendMsg都是有自己的事务的,所以执行结果是order表add进去,msg回滚。非常符合业务需求。
NOT_SUPPORTED:
该方法不应该运行在事务中,如果当前存在事务,事务会被挂起。
@Override@Transactionalpublic void createOrder(Order order) {orderMapper.save(order);try {UserService proxy= (UserService)AopContext.currentProxy();proxy.sendMsg();}catch (Exception e){System.out.println("rollback");e.printStackTrace();}}@Override@Transactional(propagation= Propagation.NOT_SUPPORTED)public void sendMsg(){msgMapper.save(new Msg("你已经下单了"));//调用短信网关throw new RuntimeException();//运行时异常,编译时上层可以不捕捉}
createOrder不加事务时没什么好说的,createOrder加事务时,这个事务会被挂起,所以相当于没加事务,所以两张表都加进去了。
NEVER
该方法不能运行在事务中,如果存在事务,抛异常,这个最简单,不说了。
NESTED
如果当前存在事务,则该方法会在嵌套事务中运行,该嵌套事务可以独立于当前事务进行提交回滚操作。
如果当前不存在事务,会新开一个事务
@Override@Transactionalpublic void createOrder(Order order) {orderMapper.save(order);try {UserService proxy= (UserService)AopContext.currentProxy();proxy.sendMsg();}catch (Exception e){System.out.println("rollback");e.printStackTrace();}}@Override@Transactional(propagation= Propagation.NESTED)public void sendMsg(){msgMapper.save(new Msg("你已经下单了"));//调用短信网关throw new RuntimeException();//运行时异常,编译时上层可以不捕捉}
此时内层事务sendMsg会独立于外层事务进行事务的提交回滚,所以最终结果时order表add进去,msg被回滚。非常符合业务需求。
增强理解
例1,NOT_SUPPORTED
@Override@Transactionalpublic void testTran() {orderService.saveOrder();//保存订单productService.saveProduct();//保存商品}@Overridepublic void saveOrder(){Order order = new Order();order.setOrderSn(2L);order.setProductId(1);order.setUserId(1);order.setBuyNum(1);orderMapper.save(order);}@Transactional(propagation = Propagation.NOT_SUPPORTED)@Overridepublic void saveProduct(){Product product = new Product();product.setPrice(1);product.setName("2121");productMapper.insert(product);int i = 1/0;}
读者可以先判断下最终order和product表最终的数据状态?
执行流程如下:
结论:订单表回滚,商品表不回滚,not_supported会挂起事务,在执行完后会恢复事务
例2,REQUIRED_NEW
@Override@Transactionalpublic void testTran() {orderService.saveOrder();//保存订单productService.saveProduct();//保存商品int i = 1/0;}@Overridepublic void saveOrder(){Order order = new Order();order.setOrderSn(2L);order.setProductId(1);order.setUserId(1);order.setBuyNum(1);orderMapper.save(order);}@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void saveProduct(){Product product = new Product();product.setPrice(1);product.setName("2121");productMapper.insert(product);}
结果:product不回滚,order回滚,因为saveProduct是一个新开事务,所以没有异常直接就提交了
例3,REQUIRED
REQUIRED作用是需要事务,没有就新开一个事务。
@Override@Transactionalpublic void testTran() {orderService.saveOrder();//保存订单try{productService.saveProduct();//保存商品}catch(Exception e){}}@Overridepublic void saveOrder(){Order order = new Order();order.setOrderSn(2L);order.setProductId(1);order.setUserId(1);order.setBuyNum(1);orderMapper.save(order);}@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void saveProduct(){Product product = new Product();product.setPrice(1);product.setName("2121");productMapper.insert(product);int i = 1/0;}
结果是两个表都回滚,这个就有点奇怪,因为自始至终就只有一个testTran开启的事务,saveProduct也只是延用了testTran开启的事务,就算saveProduct抛了异常,但是testTran捕捉了,其实不应该回滚的吧?但是源代码不是这么处理的。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
