开头总述
Spring在同一个类中调用function,事务会失效。
Spring事务是基于AOP代理来实现的。而AOP是使用JDK动态代理来实现的。
第一次试验
/**
* 父类调用子类
* 子类失败,不能影响父类
*
* 预期效果:child回滚,parent插入成功
* 第一次试验 真实效果:都插入成功,child方法因为try catch导致事务未起作用。
*/
@Override
@Transactional
public void insertParent(){
try {
insertChild();
} catch (Exception e) {
// TODO: handle exception
}
user.setName("parent_6");
user.setAge((byte)1);
userMapper.insertSelective(user);
}
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void insertChild(){
user.setName("child_6");
user.setAge((byte)1);
userMapper.insertSelective(user);
int i=1/0;
}
执行结果
原因分析
child方法被try catch住了,同时事务未生效。
在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象,只有被动态代理直接调用的才会产生事务。this调用并非代理对象。this.insertChild走的是真实对象(真实对象)。导致child上的事务无效,先执行insert语句,执行成功以后才抛出异常。异常被parent中的try catch捕获到。不影响parent方法的执行。
第二次试验
/**
* 预期效果:child回滚,parent插入成功
* 第二试验 真实效果:child插入成功,parent插入回滚
*/
@Override
@Transactional
public void insertParent(){
user.setName("parent_7");
user.setAge((byte)1);
userMapper.insertSelective(user);
insertChild();
}
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void insertChild(){
user.setName("child_7");
user.setAge((byte)1);
userMapper.insertSelective(user);
int i=1/0;
}
执行结果
原因分析
去掉try catch,child方法抛出异常,导致回滚。同时异常向上抛,导致parent方法也回滚。
child方法的事务失效,相当于child方法中的code挪到了parent方法中,也类似于在parent中抛出异常。
解决方案
AOP需要先暴露出来。
<!-- 开启切面-->
<!-- 暴露AOP代理到ThreadLocal -->
<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>
获取AOP代理需要从AOP的上下文来获取。得到当前AopProxy,然后通过AOP代理来调用child方法。
/**
* 预期效果:child回滚,parent插入成功
* 第三试验真实效果:child回滚,parent插入成功
*/
@Override
@Transactional
public void insertParent(){
try {
/**
* AopContext.currentProxy()就类似于JDK代理中的
* Proxy.newInstance得到的Proxy对象
* UserService proxy=(UserService) AopContext.currentProxy();
* proxy.insertChild();
*/
((UserService)AopContext.currentProxy()).insertChild();
} catch (Exception e) {
// TODO: handle exception
}
user.setName("parent_7");
user.setAge((byte)1);
userMapper.insertSelective(user);
}
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void insertChild(){
user.setName("child_7");
user.setAge((byte)1);
userMapper.insertSelective(user);
int i=1/0;
}
执行结果
思考:如果最后一段代码 @Transactional(propagation=Propagation.REQUIRES_NEW)改为 @Transactional(propagation=Propagation. PROPAGATION_NESTED)会是什么结果
答案可以参考
Java中高级面试题总览(三)_依赖注入面试题-CSDN博客 里1.16有详细阐述事务的传播属性