@Transactional 注解哪些情况下会失效?[java教程]
今天又研究了一道面试题,有关 @Transactional 注解失效的问题,这道题的综合性较强。
先说下我整理的答案:
1、@Transactional 作用在非 public 修饰的方法上
2、@Transactional 作用于接口,使用 CGLib 动态代理
3、@Transactional 注解属性 propagation 设置以下三种可能导致无法回滚
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
4、同一类中加 @Transactional 方法被无 @Transactional 的方法调用,事务失效
5、@Transactional 方法内异常被捕获
6、默认 RuntimeException 和 Error 及子类抛出,会回滚;rollbackFor 指定的异常及子类发生才会回滚
7、数据库不支持事务,如 MySLQL 使用 MyISAM 存储引擎
8、Spring 的配置文件中未配置事务注解生效
<tx:annotation-driven transaction-manager="transactionManager"/>
9、Spring Boot 引入 jbdc 或 jpa 包,默认开启事务注解。若未引入这两个包,需要使用 @EnableTransactionManagement 进行配置
…
简单分析一下:
@Transactional 注解的作用就是保证方法内的多个数据库操作具有事务特性,即要么都成功,要么都失败。
失效的本质原因就在注解启用、事务管理器注入、手动 commit 与 rollback 的处理(Connection 的 autoCommit 设置为 false,然后在代码里手动 commit 或 rollback)这三个环节中。
比如我的博客网站 xml 配置文件中配置的事务管理器就是
org.springframework.jdbc.datasource.DataSourceTransactionManager
它继承自
org.springframework.transaction.support.AbstractPlatformTransactionManager
事务的具体控制就是 DataSourceTransactionManager 实现。
Spring 启动时解析 xml 配置,把 TransactionInterceptor 类型的 bean 注入到了 BeanFactoryTransactionAttributeSourceAdvisor 中,调用 TransactionInterceptor 的 invoke 方法完成整个事务的逻辑。
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
invokeWithinTransaction 方法就是对事务的处理。
@Transactional 仅作用于 public 方法的逻辑在 getTransactionAttribute 方法中
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
@Transactional 的注解参数解析逻辑在 invokeWithinTransaction 方法里
//If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
// First try is the method in the target class.
TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
if (txAtt != null) {
return txAtt;
}
// Second try is the transaction attribute on the target class.
txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAtt != null) {
return txAtt;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
txAtt = findTransactionAttribute(method);
if (txAtt != null) {
return txAtt;
}
// Last fallback is the class of the original method.
return findTransactionAttribute(method.getDeclaringClass());
}
事务信息通过 createTransactionIfNecessary 方法创建
// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
代码执行发生异常的处理逻辑是在 completeTransactionAfterThrowing 方法中
completeTransactionAfterThrowing(txInfo, ex);
根据注解参数 rollback
if (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 ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
根据注解参数 commit
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 ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
代码执行正常的事务提交在 commitTransactionAfterReturning 方法
commitTransactionAfterReturning(txInfo);
以上就是 Spring 对事务的处理流程。