介绍spring事务在不同的传播机制下,执行的不同结果
事务传播配置7中,具体的参见org.springframework.transaction.annotation.Propagation这个类
场景:serviceA类有方法A调用 serviceB类有方法B。B方法出异常,哪个会提交??
初始值
<update id="updateUserA">
UPDATE `t_user` SET `name` = '张三11' WHERE `id` = 1;
</update>
<update id="updateUserB">
UPDATE `t_user` SET `name` = '张三22' WHERE `id` = 2;
</update>
?
1. 默认的REQUIRED
serviceA类有方法A,加@Transactional;调用 serviceB类有方法B。
@Service
public class ServiceA{
@Transactional
public Boolean serviceA() {
System.out.println("进入方法了,开启事务");
userMapper.getUser(1);
userMapper.updateUserA();
try {
userServiceImpl2.serviceB();
}catch (Exception e){
System.out.println("出错了");
}
return true;
}
}
@Service
public class ServiceB{
@Transactional
public Boolean serviceB() {
userMapper.updateUserB();
int a = 1/0;
return null;
}
}
执行结果
?
从图中可以看出,方法AB都加了@Transactional注解开启事务,方法B会采用A方法的事务,不会开启新的事务,方法B运行出错,会标记这次事务回滚,虽然A方法中捕获了异常,但是A方法中执行的更新sql也不会提交。
2.REQUIRES_NEW
测试代码
@Service
public class ServiceA{
@Transactional
public Boolean serviceA() {
System.out.println("进入方法了,开启事务");
userMapper.getUser(1);
userMapper.updateUserA();
try {
userServiceImpl2.serviceB();
}catch (Exception e){
System.out.println("出错了");
}
return true;
}
}
@Service
public class ServiceB{
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Boolean serviceB() {
userMapper.updateUserB();
int a = 1/0;
return null;
}
}
?执行结果
?
从结果图中可以看出,方法B开启了一个新的sqlsession,方法B运行出错了,只会影响自己,没有更新成功,方法A捕获了异常,正常更新了数据1.提交了。
?3.异常回滚
出异常回滚,默认支持RuntimeException,代码中出现其他的异常是不会回滚的。
测试代码
@Override
@Transactional
public Boolean serviceA() throws InterruptedException {
System.out.println("进入方法了,开启事务");
userMapper.getUser(1);
userMapper.updateUserA();
try {
int a = 1/0;
}catch (Exception e){
System.out.println("出错了");
throw new InterruptedException("其他异常");
}
return true;
}
结果
?
?从结果看,虽然代码出现了异常,但是还是提交了,原因是,这个异常不是运行时异常,属于编译异常,自己不捕获,向上抛出,就不会影响事务提交。
源码分析
在org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing
这个方法上。
// 这个类中org.springframework.transaction.interceptor.TransactionAspectSupport
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
//这里会回滚。
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
//这里就提交了。
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
//这个类org.springframework.transaction.interceptor.DefaultTransactionAttribute
@Override
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
//这里判断异常是否满足,回滚条件
解决办法
@Transactional(rollbackFor = Exception.class)
注解改成这样就行了。
其他的几种传播机制,大家可以自己试一试。
|