今天朋友扔来一个“简单”的事务代码,初看无味,再看惊奇。
也问了身边的一些朋友无一人回答正确,你也试试?
@Service
public class TestService {
@Autowired
private TestMapper testMapper;
@Autowired
private TestServiceTwo testServiceTwo;
@Transactional(rollbackFor = Exception.class)
public String funOne(){
// 这个就是一个简单的insert语句
testMapper.insert("a");
try {
testServiceTwo.funTwo();
}catch (Exception e){
System.out.println("异常了~~~");
}
return "da";
}
}
@Service
public class TestServiceTwo {
@Transactional(rollbackFor = Exception.class)
public void funTwo(){
throw new RuntimeException("DASDASDA");
}
}
问:最后数据库插入了几条数据?(答案放在文中)
下面有几个结论我们一个个去证明,看完你就会明白了。
一、spring基于注解的事务是基于代理的,不走代理,被调用的方法就不受事务管理代码的控制
所谓代理其实就是是否是用过依赖注入的,如果我们手动new一个对象,或者直接去调用本类的方法则不走代理。
通过下面这个代码可以打印出当前事务的hashCode
,如果hashCode
相同则认为是同一个事务。
TransactionAspectSupport.currentTransactionStatus().hashCode()
-
可以把它放在上面的
funOne
和funTwo
里面,发现最后打印出来的hashCode
并不相同。 -
如果我们调用
funTwo
的时候,并不使用@Autowired
的方式,而是使用new
的方式,你会发现打印的hashCode
确是相同的。 -
-
TestServiceTwo two = new TestServiceTwo(); -
-
two.funTwo(); -
如果去掉方法
funTwo
上面的@Transactional
注解,再次打印的hashCode
也是相同的。 -
如果不去调用
funTwo
,而是去调用和funOne
同类(同一个类)的funThree
,打印的hashCode
也是相同的。
注:如果hashCode
相同那么说明是用一个事务。
二、嵌套事务如果子事务抛出了异常,父事务同样会回滚,并且会抛出一个异常
Transaction rolled back because it has been marked as rollback-only
其实这个结论就在本题的结果,最后插入0条数据,原因就是父事务也回滚了。
funOne
调用funTwo
,funOne
中抓了funTwo
的异常,当funTwo
发生异常的时候,funTwo
的操作应该回滚,但是funOne
吃了异常,funOne
方法中没有产生异常,所以funOne
的操作又应该提交,二者是相互矛盾的。
spring的事务关联拦截器在抓到funTwo
的异常后就会标记rollback-only为true,当funOne
执行完准备提交后,发现rollback-only为true,也会回滚,并抛出异常告诉调用者。
但也不是绝对的,我们知道上面的两个事务虽然是独立的两个事务,但是依旧是有关联的,你可以理解成关联事务(就是上面所说spring的事务关联拦截器……)
如果我们让其事务不进行关联,是两个独立的事务,也就是说不会抛出rollback-only
这个异常,那么funOne
就不会回滚。
事务的传播行为有很多种,默认是REQUIRED
,我们改成REQUIRES_NEW
,再来试试结果发现是插入了一条数据的。
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
事务的传播行为:
value | desc |
---|---|
REQUIRED (默认) | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常 |
NESTED | 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务 |
注:这里的当前事务和我们一般理解有些不同,不是它的父事务。
三、如果在同一个事务内,不管是父方法异常,还是子方法异常,全部会回滚。(如果把异常try catch
,则不会)
这里的同一个事务指(其实我们可以通过打印hashCode
的方式来判断)
-
同一个类去调用子方法 -
调用另外一个类的没有 @Transactional
的方法
eg:
@Service
public class TestService {
@Autowired
private TestMapper testMapper;
@Transactional(rollbackFor = Exception.class)
public String funOne(){
testMapper.insert("a");
funThree();
return "das";
}
public void funThree(){
int i = 1 / 0;
}
}
原文始发于微信公众号(小道仙97):月薪10k-20k都无法回答的事务问题,你会吗?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/41453.html