Spring 事务失效场景

  1. 事务配置错误
  2. 在接口内直接调用另一个事务管理的接口
  3. 在私有方法上开启事务
  4. 在final方法上开启事务
  5. Bean对象没有被事务管理
  6. 在新线程中调用事务接口
  7. 异常被捕获
    ...

场景1 持久层对象与事务管理器使用的数据源不一致

@Component
public class AppConfig {
    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource datasource = new DriverManagerDataSource();
        datasource.setUrl("jdbc:mysql://192.168.1.100:3307/tt_test");
        datasource.setUsername("root");
        datasource.setPassword("Aa.123456");
        return datasource;
    }
}

在创建 JdbcTemplate / DataSourceTransactionManager 时DataSource不是由容器注入的, 而是直接的方法调用
这会导致创建两个不同的DataSource对象, 在管理事务的时候, 会在代理对象中使用 DataSourceTransactionManager 中的DataSource创建连接并存入 ThreadLocal<DataSource, Connection> 中,JdbcTemplate 在执行sql时, 会使用自己的 DataSource 在ThreadLocal 中获取连接, 由于是不同的DataSource对象, 所以获取不到连接, 然后就会自己创建一个连接
此连接没有关闭自动提交, 于是事务失效

解决办法

  1. 使用 Bean 注入的方式在JdbcTemplate 和 DataSourceTransactionManager 中使用同一个DataSource

    @Component
    public class AppConfig {
    	@Bean
    	public JdbcTemplate jdbcTemplate(DataSource dataSource) {
    		return new JdbcTemplate(dataSource);
    	}
    
    	@Bean
    	public DataSourceTransactionManager transactionManager(DataSource dataSource) {
    		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    		transactionManager.setDataSource(dataSource);
    		return transactionManager;
    	}
    
    	@Bean
    	public DataSource dataSource() {
    		DriverManagerDataSource datasource = new DriverManagerDataSource();
    		datasource.setUrl("jdbc:mysql://192.168.1.100:3307/tt_test");
    		datasource.setUsername("root");
    		datasource.setPassword("Aa.123456");
    		return datasource;
    	}
    }
    
  2. 使用 @Configuration 注解
    此注解会创建AppConfig 代理对象,代理对象中的方法调用会先从Spring 容器中获取对应的Bean, 而不是直接的方法调用创建新的对象

    @Configuration //使用
    public class AppConfig {
    	@Bean
    	public JdbcTemplate jdbcTemplate() {
    		return new JdbcTemplate(dataSource());
    	}
    
    	@Bean
    	public DataSourceTransactionManager transactionManager() {
    		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    		transactionManager.setDataSource(dataSource());
    		return transactionManager;
    	}
    
    	@Bean
    	public DataSource dataSource() {
    		DriverManagerDataSource datasource = new DriverManagerDataSource();
    		datasource.setUrl("jdbc:mysql://192.168.1.100:3307/tt_test");
    		datasource.setUsername("root");
    		datasource.setPassword("Aa.123456");
    		return datasource;
    	}
    }
    
posted @ 2024-03-29 21:45  Dreamsrj  阅读(10)  评论(0)    收藏  举报