Mybatis 多数据源事务是否生效问题

导读:本篇文章讲解 Mybatis 多数据源事务是否生效问题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    我们所有的程序员应该都使用过事务注解  @Transactional  ,面试的时候大家或多或少的都能说出来一些东西

    比如:

    1.事务的隔离级别

    2.事务在哪些情况下不生效,比如加在类上,方法上(public,private,protected)

    3.事务的配置使用

   等等的一系列问题,大部分的小伙伴应该对这些方面也是了如指掌,对答如流了。

   但是最近,发现了一个问题,事务在我们的项目代码中不生效了,由于是多数据源,因此我们考虑是否是因为多数据源从而导致了事务的问题,果不其然,解决起来还是比较顺风顺水的。

    问题阐述:在多数据源情况下,抛出异常以后,数据库仍然数据落库了,并且打印了insert日志,按常理来说,不应该这样,只要配置了 @Transactional,在配置类上加上 @EnableTransactionManagement 应该就能达到想要的效果了。但是并不是如此。

    解决思路:在尝试了无数次的insert插入以后,发现肯定是代码有问题了。实则发现问题以后才恍然大悟。由于我们的代码是二次编码,第一次编码用的JDBC的底层框架,Mybatis是由我们后续的小伙伴引入的,因此在配置以及使用方面并不是那么的完善。

   上代码看一下我们的配置(由于涉及到公司代码,所以以下部分代码用A or B or * 代替)

   

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "*.*.*.A",sqlSessionTemplateRef = "ASqlSessionTemplate")
public class ADataSourceConfig {

    @Autowired
    @Qualifier("ADataSource")
    private DataSource ADS;

    @Bean
    public SqlSessionFactory mgSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        tk.mybatis.mapper.session.Configuration configuration = new tk.mybatis.mapper.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        bean.setConfiguration(configuration);
        bean.setDataSource(ADS);
        bean.setTypeAliasesPackage("*.*.*.A");
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:*.xml"));
        return bean.getObject();
    }

    @Bean
    public PlatformTransactionManager ATransactionManager() {
        return new DataSourceTransactionManager(ADS);
    }

    @Bean
    public SqlSessionTemplate ASqlSessionTemplate(@Qualifier("ASqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}



@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "*.*.*.B",sqlSessionTemplateRef = "BSqlSessionTemplate")
public class BDataSourceConfig {

    @Autowired
    @Qualifier("BDataSource")
    private DataSource BDS;

    @Bean
    public SqlSessionFactory mgSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        tk.mybatis.mapper.session.Configuration configuration = new tk.mybatis.mapper.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        bean.setConfiguration(configuration);
        bean.setDataSource(BDS);
        bean.setTypeAliasesPackage("*.*.*.B");
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:*.xml"));
        return bean.getObject();
    }

    @Bean
    public PlatformTransactionManager BTransactionManager() {
        return new DataSourceTransactionManager(BDS);
    }

    @Bean
    public SqlSessionTemplate BSqlSessionTemplate(@Qualifier("BSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

  正常情况下,在单一的数据源配置的时候,我们这样配置或许是生效的,但是在多数据源下就有这么个坑了。

  我们既然使用了事务,那么有一个东西叫做 事务管理器 就必须使用到,也就是  PlatformTransactionManager。

  如果我们再服务中只是简单的默认使用 @Transactional 的所有值,那么Spring容器并不知道我们选择的是哪一个事务管理器,所以会选用默认的事务管理器,而如果默认的事务管理器如果我们没有更改,那么就会加载 Bean name 是 transactionManager

的。而在上面的代码我们可以看到,我们注册的事务管理器的Bean 一个 叫 ATransactionManager,另一个叫 BTransactionManager,因此没有一个事务管理器是满足我们的条件的,所以Spring在选择的时候根本没有选择到,所以我们的事务并没有正常的回滚。

   解决方案: 既然知道了问题所在,那么解决起来就很简单了。我大概总结了3种解决的思路可以供大家参考:

  1、从源头解决问题,我们在往Spring容器注册事务管理器的时候,就可以更改其默认的事务管理器,从而Spring在选择到该数据源的时候就知道用哪一个事务管理器了。下面是相应的配置代码   

    @Autowired
    @Qualifier("ADataSource")
    private DataSource ADS;

  @Bean
    public PlatformTransactionManager uiTransactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(ADS);
		return transactionManager;
    }

  2、在使用 Transactional 注解的时候,我们会发现里面有一个值叫 value,也就是 transactionManager 的别名,在这里指定加载的事务管理器,也可以达到指定的效果。代码如下:

   

--------配置代码开始----------

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "*.*.*.B",sqlSessionTemplateRef = "BSqlSessionTemplate")
public class BDataSourceConfig {

    @Autowired
    @Qualifier("BDataSource")
    private DataSource BDS;

    @Bean
    public SqlSessionFactory mgSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        tk.mybatis.mapper.session.Configuration configuration = new tk.mybatis.mapper.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        bean.setConfiguration(configuration);
        bean.setDataSource(BDS);
        bean.setTypeAliasesPackage("*.*.*.B");
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:*.xml"));
        return bean.getObject();
    }

    //  Spring注册Bean中,方法名即为Bean的名字
    @Bean
    public PlatformTransactionManager BTransactionManager() {
        return new DataSourceTransactionManager(BDS);
    }

    @Bean
    public SqlSessionTemplate BSqlSessionTemplate(@Qualifier("BSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}


--------配置代码结束--------

-------业务代码开始---------
@Transactional(value = "BTransactionManager")
public void test(){
}
-------业务代码结束---------

 3、手动控制事务管理器,和上面一种方案类似,但是这种方案更具源码性吧,不用了Spring原生的 @Transactional 而改用自己实现的方式:

    // 由于是多数据源,因此会有多个事务管理器,因此用Resource根据名称的注入方式更为合理
    @Resource
	PlatformTransactionManager ATransactionManager;
    @Transactional(rollbackFor = Exception.class)
	public boolean addMenu(MenuRequest request){
	    TransactionStatus status = ATransactionManager.getTransaction(new DefaultTransactionDefinition());
	    try {
		    /** 业务代码 **/
	    }catch (Exception e){
            log.error("addMenu {}",e);
		    ATransactionManager.rollback(status);
		    return false;
	    }
	    ATransactionManager.commit(status);
	    return true;
    }

   以上就是解决问题的整个思路和方案~至于Spring在没有指定事务管理器的时候如何选择这部分源码还没有找到,等后续有时间再和大家用源码的方式更为仔细的盘一盘他。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/6470.html

(1)
小半的头像小半

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!