一. 数据库不支持事务
以mysql为例子
- MyISAM 引擎是不支持事务操作的
- InnoDB 才是支持事务的引擎,
所以,一般要支持事务都会使用 InnoDB.
mysql的官网显示,从5.5.5开始默认的存储引擎是InnoDB,之前默认的都是MyISAM.
二. 没有被Spring管理,注解所在的类没有被加载成Bean
举个例子吧:
// 如果把下面的这个注解注释掉了,或者直接就没有写吗,这个类就不会被加载成一个Bean,就不会被Spring管理,事务自然也就失效了.
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional(rollbackFor = Exception.class)
public void updateOrder(Order order) {
//TODO 具体执行更新订单的操作
}
}
三. 方法不是 public
在Spring 的官方文档中有这样一段的介绍
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
翻译后的:
当使用代理时,您应该仅将@Transactional注释应用于具有公共可见性的方法。如果使用@Transactional注释对受保护的、私有的或包可见的方法进行注释,则不会引发错误,但注释的方法不会显示配置的事务设置。如果需要注释非公开方法,请考虑使用AspectJ(见下文)。
所以说 @Transactional 只能用于 public 的方法上,否则事务就会失效,如果要用在非 public 方法上,可以开启 AspectJ
代理模式。
四. 自身调用问题
- 举例1:
OrderServiceImpl 这个类中有一个方法update(),该方法没有 @Transactional 注解,调用了一个有 @Transactional 的updateOrder()方法.
结论: 此时updateOrder()方法上的事务是不管用的.
@Service
public class OrderServiceImpl implements OrderService {
public void update(Order order) {
updateOrder(order);
}
@Transactional
public void updateOrder(Order order) {
// update order
}
}
- 举例2:
这次在 update 方法上加了 @Transactional
,updateOrder 加了 REQUIRES_NEW
新开启一个事务,那么新开的事务管用么?
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateOrder(Order order) {
// update order
}
}
以上两个例子都是发生了自身调用,就是调该类自己的方法,而没有经过 Spring 的代理类,
默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。
这个的解决方案之一就是在的类中注入自己,用注入的对象再调用另外一个方法,这个不太优雅,另外一个可行的方案可以参考《Spring 如何在一个事务中开启另一个事务?》这篇文章。
五.数据源没有配置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
在springboot中可以查看配置类DataSourceTransactionManagerAutoConfiguration
@Configuration
@ConditionalOnClass({JdbcTemplate.class, PlatformTransactionManager.class})
@AutoConfigureOrder(2147483647)
@EnableConfigurationProperties({DataSourceProperties.class})
public class DataSourceTransactionManagerAutoConfiguration {
public DataSourceTransactionManagerAutoConfiguration() {
}
@Configuration
@ConditionalOnSingleCandidate(DataSource.class)
static class DataSourceTransactionManagerConfiguration {
private final DataSource dataSource;
private final TransactionManagerCustomizers transactionManagerCustomizers;
DataSourceTransactionManagerConfiguration(DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
this.dataSource = dataSource;
this.transactionManagerCustomizers = (TransactionManagerCustomizers)transactionManagerCustomizers.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean({PlatformTransactionManager.class})
public DataSourceTransactionManager transactionManager(DataSourceProperties properties) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(this.dataSource);
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
}
}
六. 不支持事务
@Transactional 注解中设置了 (propagation = Propagation.NOT_SUPPORTED),表示不以事务运行,当前若存在事务则挂起.详细的可以参考《事务隔离级别和传播机制》这篇文章。属于主动设置的不支持事务.自然也不会生效了.
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void updateOrder(Order order) {
// update order
}
}
七. 异常被吞了
这个也是大家常见的情景,catch了异常.
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
}
}
}
八.异常类型错误
上面的离职在抛出一个异常.
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
throw new Exception("更新错误");
}
}
}
这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:
@Transactional(rollbackFor = Exception.class)
这个配置仅限于 Throwable
异常类及其子类。
以上八种情景,其实发生最多就是下面三个了
-
自身调用
-
异常被吃
-
异常抛出类型不对