美文网首页MySQLspringspringboot
SpringBoot事务失效(1)—dataSource配置问题

SpringBoot事务失效(1)—dataSource配置问题

作者: 小胖学编程 | 来源:发表于2021-11-16 20:10 被阅读0次

    场景:Spring事务没有生效。
    环境:SpringBoot+mybatis 或者SpringBoot+JdbcTemplate等

    1. 问题原因

    xml等配置信息详见:SpringBoot2.x实现链式事务(分库事务)

    @Slf4j
    @Configuration
    public class DBConfig {
    
        @Value("${mysql.mapperLocations}")
        private String mapperLocations;
    
        @Value("${mysql.configLocation}")
        private String configLocation;
    
        /**
         * 数据源
         */
        @Bean("mysqlDataSource")
        @ConfigurationProperties(prefix = "mysql.datasource")
        public DataSource dataSource() {
            return new DruidDataSource();
        }
    
        @Bean(name = "mysqlSessionFactory")
        public SqlSessionFactory sqlSessionFactorys(@Qualifier("mysqlDataSource") DataSource dataSource) throws Exception {
            try {
                SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
                //获取配置文件的dataSource对象
                sessionFactoryBean.setDataSource(dataSource);
                //设置mybatis-config.xml配置文件位置
                sessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
                //设置mapper.xml文件所在位置
                Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocations);
                sessionFactoryBean.setMapperLocations(resources);
                return sessionFactoryBean.getObject();
            } catch (IOException e) {
                log.error("mybatis解析 mapper*xml 失败", e);
                return null;
            } catch (Exception e) {
                log.error("mybatis sqlSessionFactoryBean创建失败", e);
                return null;
            }
        }
    
        /**
         * 操作事务的Template
         * 此处传入的dataSource是mysqlDataSource的bean。
         */
        @Bean(name = "mysqlSessionTemplate")
        public SqlSessionTemplate sqlSessionTemplate(
                @Qualifier("mysqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    
        /**
         * 事务管理,此处填充的AbstractDataSource并不是mysqlDataSource的bean,而是自定义创建出来的DataSource对象。
         * 因为和sqlSessionTemplate并不是一个dataSource,这也是事务失效的原因。
         */
        @Bean(name = "mysqlTransactionManager")
        public PlatformTransactionManager xxxTransactionManager(@Qualifier("mysqlDataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(new AbstractDataSource() {
    
                protected DataSource determineTargetDataSource() {
                    return dataSource;
                }
    
                @Override
                public Connection getConnection() throws SQLException {
                    return determineTargetDataSource().getConnection();
                }
    
                @Override
                public Connection getConnection(String username, String password) throws SQLException {
                    return determineTargetDataSource().getConnection(username, password);
                }
            });
        }
    }
    

    如此上面的配置,在项目启动后,事务不会生效。原因:

    1. Spring开启事务,则事务管理器mysqlTransactionManager在开启事务时,会通过mysqlTransactionManager内部的DataSource获取connection连接;
    2. Spring真正操作sql时,实际上依靠的是mysqlSessionTemplate来实现的,会通过mysqlSessionTemplate内部的DataSource去缓存中获取mysqlTransactionManager开启事务时生成的connection对象,因为不是一个datasource对象,所以没有获取到,最终生成一个新的connection对象;
    3. 因为事务管理器的dataSource和Template的dataSource并不是同一个对象,所以获取的不是同一个connection连接,故事务不会生效。

    2. 源码复现

    开启事务时,事务管理器获取connection。
    源码位置:org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

    事务管理器.png

    源码位置:org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

    实际操作的connection.png

    3. 改进方案

    SqlSessionTemplate需要和PlatformTransactionManager是同一个dataSource,事务才会生效。

        /**
         * 事务管理器和mysqlSessionTemplate的dataSource必须是同一个,事务才会生效。
         */
        @Bean(name = "mysqlTransactionManager")
        public PlatformTransactionManager xxxTransactionManager(@Qualifier("mysqlDataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    

    相关文章

      网友评论

        本文标题:SpringBoot事务失效(1)—dataSource配置问题

        本文链接:https://www.haomeiwen.com/subject/mjehtrtx.html