【解决多数据源动态切换事务问题】代码实战结合mybatis-plus

介绍

参考:http://www.yanglajiao.com/article/gaoshili001/79378902

原理请参考另外一篇文章:解决多数据源动态切换事务问题】Spring Mybatis 事务管理器源码

网上好多的springboot的事务都是瞎扯,根本不起作用,后来通过各种渠道查证,springboot的声明式事务需要重写Transaction,那么我们来解决多数据源动态切换事务的问题

核心方法:

private void openMainConnection() throws SQLException { 
   this.mainConnection = DataSourceUtils.getConnection(this.dataSource)
}

直接上代码

CiweiSqlSessionFactory

/**
 * 配置sqlSessionFactory
 */
@Configuration
@Slf4j
public class CiweiSqlSessionFactory {

    @Autowired
    DataSourceConfiguration.DynamicDataSource dataSource;

    @Bean
    public SqlSessionFactory sqlSessionFactory() {
        log.info("--------------------  sqlSessionFactory init ---------------------");
        try {
//            SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
            MybatisSqlSessionFactoryBean sessionFactoryB= new MybatisSqlSessionFactoryBean();
            sessionFactorttoryBean.setDataSource(dataSource);
            sessionFactoryBean.setTransactionFactory(new MultiDataSourceTransactionFactory());
            // 读取配置
            sessionFactoryBean.setTypeAliasesPackage("com.example.apollodynamicdatasource.entity");

            //设置mapper.xml文件所在位置
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*.xml");
            sessionFactoryBean.setMapperLocations(resources);
            return sessionFactoryBean.getObject();
        } catch (IOException e) {
            log.error("mybatis resolver mapper*xml is error",e);
            return null;
        } catch (Exception e) {
            log.error("mybatis sqlSessionFactoryBean create error",e);
            return null;
        }
    }

}

MultiDataSourceTransaction

/**
 * 多数据源切换的支持事务
 */
public class MultiDataSourceTransaction implements Transaction {

    private static final Log LOGGER = LogFactory.getLog(MultiDataSourceTransaction.class);

    private final DataSource dataSource;

    private Connection mainConnection;

    private String mainDatabaseIdentification;

    private ConcurrentMap<String, Connection> otherConnectionMap;

    private boolean isConnectionTransactional;

    private boolean autoCommit;

    public MultiDataSourceTransaction(DataSource dataSource) {
        notNull(dataSource, "No DataSource specified");
        this.dataSource = dataSource;
        otherConnectionMap = new ConcurrentHashMap<>();
        mainDatabaseIdentification = DynamicDataSourceContextHolder.getDataSourceKey();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Connection getConnection() throws SQLException {
        String databaseIdentification = DynamicDataSourceContextHolder.getDataSourceKey();
        if (databaseIdentification.equals(mainDatabaseIdentification)) {
            if (mainConnection != null) return mainConnection;
            else {
                openMainConnection();
                mainDatabaseIdentification = databaseIdentification;
                return mainConnection;
            }
        } else {
            if (!otherConnectionMap.containsKey(databaseIdentification)) {
                try {
                    Connection conn = dataSource.getConnection();
                    otherConnectionMap.put(databaseIdentification, conn);
                } catch (SQLException ex) {
                    throw new CannotGetJdbcCoot get JDBC Connection", ex);
                }
            }
            retu    }
            r    }
            }
        }
            return otherConnectionMap.get(databaseIdentification);
        }

    }


    private void openMainConnection() throws SQLException {
        this.mainConnection = DataSourceUtils.getConnection(this.dataSource);
        this.autoCommit = this.mainConnection.getAutoCommit();
//        this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.mainConnection, this.dataSource);
        this.isConnectionTransactional = false;

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(
                    "JDBC Connection ["
                            + this.mainConnection
                            + "] will"
                            + (this.isConnectionTransactional ? " " : " not ")
                            + "be managed by Spring");
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void commit() throws SQLException {
        if (this.mainConnection != null && !this.isConnectionTransactional && !this.autoCommit) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Committing JDBC Connection [" + this.mainConnection + "]");
            }
            this.mainConnection.commit();
            for (Connection connection : otherConnectionMap.values()) {
                connection.commit();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void rollback() throws SQLException {
        if (this.mainConnection != null && !this.isConnectionTransactional && !this.autoCommit) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Rolling back JDBC Connection [" + this.mainConnection + "]");
            }
            this.mainConnection.rollback();
            for (Connection connection : otherConnectionMap.values()) {
                connection.rollback();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void close() throws SQLException {
        DataSourceUtils.releaseConnection(this.mainConnection, this.dataSource);
        for (Connection connection : otherConnectionMap.values()) {
            DataSourceUtils.releaseConnection(connection, this.dataSource);
        }
    }

    @Override
    public Integer getTimeout() throws SQLException {
        return null;
    }

}

MultiDataSourceTransactionFactory

/**
 * 支持Service内多数据源切换的Factory
 */
public class MultiDataSourceTransactionFactory extends SpringManagedTransactionFactory {

    @Override
    public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
        return new MultiDataSourceTransaction(dataSource);
    }

}

最后在代码中引入

@EnableTransactionManagement
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)

需要注意的是:

在spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检查型异常(try catch)则不进行回滚操作:

故而如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}

总结

代码必须关闭自动提交 关闭自动提交会根据代码手动控制提交 回滚的话也是代码中控制

dataSource.setAutoCommit(false);

demo:https://github.com/ciweigg2/apollo-dynamic-datasource


文章作者: Ciwei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ciwei !
 上一篇
【自定义starter实现】@Enable开启自动装配 【自定义starter实现】@Enable开启自动装配
自动配置非常方便,当我们要操作Mongodb的时候,只需要引入spring-boot-starter-data-mongodb的依赖,然后配置Mongodb的链接信息 spring.data.mongodb.uri=mongodb://lo
2019-08-14
下一篇 
【解决多数据源动态切换事务问题】Spring Mybatis 事务管理器源码 【解决多数据源动态切换事务问题】Spring Mybatis 事务管理器源码
介绍了解mybatis是怎么管理事务的 这篇文章主要是为解决多数据源动态切换不支持事务的问题 spring事务注解默认支持第一个开启Connection的事务 重新实现结合声明式事务支持多数据源动态切换的事务 也就是说一个service中可
2019-08-13
  目录