spring中驶入失效的原因总结(八种)

一. 数据库不支持事务

以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 异常类及其子类。

以上八种情景,其实发生最多就是下面三个了

  • 自身调用

  • 异常被吃

  • 异常抛出类型不对

上一篇:考过spring官方认证弄明白事务传播是怎么回事


下一篇:终于有人把Java程序员必学知识点全整理出来了,最终入职阿里