白玉神驹
专注、认真、执着!

@Transactional

生效原则1:除非特殊配置(比如使用AspectJ静态织入实现AOP),否则只有定义在public方法上的@Transactional才能生效。
原因:Spring默认通过动态代理的方式实现AOP,对目标方法进行增强,private方法无法代理到,Spring自然也无法动态增强事务处理逻辑。

生效原则2:必须通过代理过的类从外部调用目标方法才能生效

回滚

事务即使生效不一定能回滚:通过AOP实现事务处理,使用try...catch...来包裹标记了@Transactional注解的方法,当方法出现了异常并且满足一定条件的时候,在catch里面我们可以设置事务回滚,没有异常则直接提交事务。

一定条件

第一,只有异常传播出了标记了@Transactional注解的方法,事务才能回滚。
//在Spring的TransactionAspectSupport类中 invokeWithinTransaction方法

try {
   // This is an around advice: Invoke the next interceptor in the chain.
   // This will normally result in a target object being invoked.
   retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
   // target invocation exception
   completeTransactionAfterThrowing(txInfo, ex);
   throw ex;
}
finally {
   cleanupTransactionInfo(txInfo);
}
第二,默认情况下,出现RuntimeException(非受检异常)或Error的时候,Spring才会回滚事务。即受检异常(一般是业务异常,或者说类似另一种方法的返回值,出现这样的异常可能业务还能完成,所以不会主动回滚);而Error或RuntimeException代表了非预期的结果,应该回滚

/**
 * The default behavior is as with EJB: rollback on unchecked exception
 * ({@link RuntimeException}), assuming an unexpected outcome outside of any
 * business rules. Additionally, we also attempt to rollback on {@link Error} which
 * is clearly an unexpected outcome as well. By contrast, a checked exception is
 * considered a business exception and therefore a regular expected outcome of the
 * transactional business method, i.e. a kind of alternative return value which
 * still allows for regular completion of resource operations.
 * <p>This is largely consistent with TransactionTemplate's default behavior,
 * except that TransactionTemplate also rolls back on undeclared checked exceptions
 * (a corner case). For declarative transactions, we expect checked exceptions to be
 * intentionally declared as business exceptions, leading to a commit by default.
 * @see org.springframework.transaction.support.TransactionTemplate#execute
 */
@Override
public boolean rollbackOn(Throwable ex) {
   return (ex instanceof RuntimeException || ex instanceof Error);
}

手动修复

1. 手动设置让当前事务处于回滚状态

@Transactional
public void createUser(User user) {
    try {
        userDao.save(new User(user));
        throw new RuntimeException("error");
    } catch (Exception ex) {
        log.error("create user failed", ex);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
2. 在注释中声明,期望遇到所有的Exception都回滚事务(覆盖默认不回滚受检异常)

@Transactional(rollbackFor = Exception.class)
public void createUser(User user) { throws IOException {
    userDao.save(new User(user));
    otherTask();
}

独立事务

如果方法涉及多个数据库操作,并希望将他们作为独立的事务进行提交或回滚,我们考虑进一步细化配置事务传播方式,也就是@Transactional 注解的 Propagation 属性。

 

posted on 2021-12-07 17:57  白玉神驹  阅读(110)  评论(0编辑  收藏  举报