1.首先需要加事务的方法不能是私有的(如果方法私有,则事务不生效)
spring源码如下
在AbstractFallbackTransactionAttributeSource.computeTransactionAttribute方法的第一行判断,判断事务的方法是否为public
2.其次抛出异常的时候,不要去接受异常,让spring去接受异常处理,否则事务不会回滚
TransactionInterceptor.invoke();
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation){
// 1. 获取事务属性
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 2. 获取事务管理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 3. 获取需要事务的方法名称:类目.方法名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 4. 声明式事务
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 5. 获取该方法上事务的信息
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// 6. 目标方法执行,它是一个拦截器链
//如果这里我们的业务代码有catch异常,则这里的外层catch不到异常了
//这会导致异常接收不到,不进行事务回滚
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 7. 事务回滚,该方法中有事务回滚的规则
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 8. 清除事务信息
cleanupTransactionInfo(txInfo);
}
// 9. 事务提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 10. 编程式事务,流程和声明式事务一致
}
}
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);
}
//判断回滚条件RuleBasedTransactionAttribute.rollbackOn(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;
}
}
}
}
这里比较重要的是事务回滚判断的条件,RuleBasedTransactionAttribute.rollbackOn(ex)
@Override
public boolean rollbackOn(Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
}
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
//rollbackRules 回滚的规则
if (this.rollbackRules != null) {
for (RollbackRuleAttribute rule : this.rollbackRules) {
//递归寻找,知道找到父类是Throwable.class或者是规则中的异常类,里面代码大家可以粗略看一下,很简单
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}
// User superclass behavior (rollback on unchecked) if no rule matches.
if (winner == null) {
logger.trace("No relevant rollback rule found: applying default rules");
return super.rollbackOn(ex);
}
return !(winner instanceof NoRollbackRuleAttribute);
}
其中rollbackRules是一串RollbackRuleAttribute的列表,我们看一下他是怎么创建的
看到这里大家应该明白了,只有我们的异常是RuntimeException或者他的子类时,我们这个事务才会回滚
3.内部调用自己的方法,事务不生效
什么意思呢,就是一个对象的一个方法上加上事务,然后该方法调用该对象的另一个方法,就会发生第二个方法事务不生效问题,我在这里举个例子
@Transactional(rollbackFor = Exception.class)
public String testTransactional(Integer id, Integer count) {
for (int i = 0; i < 2; i++) {
DemoRedis demoRedis = new DemoRedis();
demoRedis.setCount(i);
demoRedisMapper.insert(demoRedis);
}
String s = testTransactionalV1(10, 2);
if(s.equals("下单成功3")){
System.out.println("V1事务完结");
}
return "下单成功3";
}
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public String testTransactionalV1(Integer id, Integer count) {
System.out.println("====================================");
for (int i = 0; i < 2; i++) {
DemoRedis demoRedis = new DemoRedis();
demoRedis.setCount(i);
demoRedisMapper.insert(demoRedis);
}
return "下单成功3";
}
然后我们再看一下方法栈
其实也好理解,当我们代理类方法执行完之后,我们执行业务代理,业务代理去调用自己的方法,其实就是调用this.testTransactionalV1()方法,此时的类是未被加强的类,所以也就不存在执行事务的代理,我们将testTransactionalV1执行完,该方法也不会将事务提交,尽管我们这里将当前事务挂起,起一个新的事务,但是却没有执行,失效了;
将testTransactional执行完
那该怎么解决呢
第一种,循环依赖
用依赖的类来调用方法,获取代理对象
方法栈信息
V1执行完之后的结果
第二种方法
在主应用加上该注解
@EnableAspectJAutoProxy(exposeProxy = true)
然后再业务袋中,去获取当前的代理对象
((DemoRedisServiceImpl) AopContext.currentProxy()).testTransactionalV1(10, 2);