【Spring事物三千问】Spring配置多数据源 vs 给多个数据源添加事物管理

时间:2023-02-08 14:01:33

在 Spring 中配置多数据源 与 给多个数据源添加事物管理是有区别的。
如果只需要使用到多个数据源,而不需要进行事物管理,是可以进行简单处理的。

Spring 中配置多数据源

如果不需要对数据源进行事物管理的话,在 Spring 中配置多个数据源是相对比较容易的。 根据前面分析 多事务管理器&多数据源的处理,我们知道,如果使用 MyBatis 来操作 DB 的话,那么配置多数据源只需要两步:
1、定义 n 个数据源 DataSource
2、定义基于 DataSource 的 SqlSessionFactory (通过 MyBatis 操作 sql)

多数据源的配置示例如下:

// Master 数据源配置
@Configuration
@MapperScan(basePackages = "com.kvn.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterMybatisConfig {

    @Bean("master")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean("masterSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("master") DataSource dataSource) throws Exception {
        // 设置数据源
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        // mapper 的xml文件位置
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String locationPattern = "classpath*:/mapper/master/*.xml";
        mybatisSqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        // 对应数据库的 entity 位置
        String typeAliasesPackage = "com.kvn.entity.master";
        mybatisSqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
        return mybatisSqlSessionFactoryBean.getObject();
    }
}

------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------

// Slave 数据源配置
@Configuration
@MapperScan(basePackages = "com.kvn.mapper.slave", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class SlaveMybatisConfig {

    @Bean("slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean("slaveSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("slave") DataSource dataSource) throws Exception {
        // 设置数据源
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        // mapper 的xml文件位置
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String locationPattern = "classpath*:/mapper/master/*.xml";
        mybatisSqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        // 对应数据库的 entity 位置
        String typeAliasesPackage = "com.kvn.entity.slave";
        mybatisSqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
        return mybatisSqlSessionFactoryBean.getObject();
    }

}

Spring 中配置多数据源的事物管理

如果要给数据源添加添加事物管理的功能的话,那么就需要在多数据源配置的基础之上,再给每个 DataSource 添加事物管理器(TransactionManager),同时,开启对 @Transactional 方法的拦截。
所以,配置多数据源的事物管理的步骤如下:
1、定义 n 个数据源 DataSource
2、定义基于 DataSource 的 SqlSessionFactory (通过 MyBatis 操作 sql)
3、定义基于 DataSource 的 TransactionManager(事物管理器)
4、通过 @EnableTransactionManagement 来开启对 @Transactional 方法的拦截

多数据源的事物管理配置示例如下:

// Master 数据源配置
@Configuration
@MapperScan(basePackages = "com.kvn.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterMybatisConfig {

    @Bean("master")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 创建 master 数据源的事物管理器
     */
    @Bean(name = "masterTxManager")
    public PlatformTransactionManager masterTxManager(@Qualifier("master") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    
    @Bean("masterSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("master") DataSource dataSource) throws Exception {
        // 设置数据源
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        // mapper 的xml文件位置
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String locationPattern = "classpath*:/mapper/master/*.xml";
        mybatisSqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        // 对应数据库的 entity 位置
        String typeAliasesPackage = "com.kvn.entity.master";
        mybatisSqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
        return mybatisSqlSessionFactoryBean.getObject();
    }
}


------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------


// Slave 数据源配置
@Configuration
@MapperScan(basePackages = "com.kvn.mapper.slave", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class SlaveMybatisConfig {

    @Bean("slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 创建 slave 数据源的事物管理器
     */
    @Bean(name = "slaveTxManager")
    public PlatformTransactionManager slaveTxManager(@Qualifier("slave") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    
    @Bean("slaveSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("slave") DataSource dataSource) throws Exception {
        // 设置数据源
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        // mapper 的xml文件位置
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String locationPattern = "classpath*:/mapper/master/*.xml";
        mybatisSqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
        // 对应数据库的 entity 位置
        String typeAliasesPackage = "com.kvn.entity.slave";
        mybatisSqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
        return mybatisSqlSessionFactoryBean.getObject();
    }
}

小结

抽象出来看的话,在 Spring 中配置多数据源,其实就是定义多个 DataSource 的 bean。
而配置多数据源的事物管理,就是在多个 DataSource bean 的基础之上,再为每个 DataSource 添加 TransactionManager 事物管理器。

附:
PlatformTransactionManager 是 Spring 事务实现的核心接口。它提供了三个方法用来管理事务:
1、 getTransaction(TransactionDefinition)
2、 commit(TransactionStatus)
3、 rollback(TransactionStatus)

PlatformTransactionManager 提供了管理事物的能力,而对 @Transactional 事物方法拦截的处理是通过 @EnableTransactionManagement 来开启的。
@EnableTransactionManagement 的作用是自动注册 BeanFactoryTransactionAttributeSourceAdvisor,让 TransactionInterceptor 来拦截 @Transactional 标记的方法。

所以,PlatformTransactionManager@EnableTransactionManagement 的共同作用下,让 Spring 拥有了对 @Transactional 标记的方法进行事物管理的能力。