MyBatis 多数据源事务

举报
林欣 发表于 2025/03/31 17:22:12 2025/03/31
39 0 0
【摘要】 在现代企业级应用中,经常会有多个数据源的需求,比如读写分离、模块分离或跨系统集成等场景。MyBatis 提供了灵活的方式来支持多数据源操作,并且可以与 Spring 框架结合来实现多数据源事务管理。以下是 MyBatis 实现多数据源事务的一般步骤和关键点: 一、配置多数据源定义多个数据源:在 Spring 或 Spring Boot 项目中,通过配置文件(如 application.yml...

在现代企业级应用中,经常会有多个数据源的需求,比如读写分离、模块分离或跨系统集成等场景。MyBatis 提供了灵活的方式来支持多数据源操作,并且可以与 Spring 框架结合来实现多数据源事务管理。

以下是 MyBatis 实现多数据源事务的一般步骤和关键点:

一、配置多数据源

  1. 定义多个数据源

    • 在 Spring 或 Spring Boot 项目中,通过配置文件(如 application.yml 或 application.properties)定义多个数据源,每个数据源对应不同的数据库连接。
  2. 创建数据源配置类

    • 使用 Java 配置类(带有 @Configuration 注解)为每个数据源配置 DataSource 对象,并通过 @Bean 注解将其注册到 Spring 容器中。
  3. 配置 SqlSessionFactory

    • 为每个数据源配置对应的 SqlSessionFactory,这是 MyBatis 执行 SQL 的核心。

二、配置事务管理

  1. 定义事务管理器

    • 为每个数据源配置相应的事务管理器,通常使用 DataSourceTransactionManager
  2. 声明事务边界

    • 在需要事务管理的 Service 方法上使用 @Transactional 注解,指定事务管理器。

三、动态数据源切换

  1. 实现动态数据源路由

    • 可以使用 Spring 提供的 AbstractRoutingDataSource 类,通过重写 determineCurrentLookupKey 方法实现动态数据源路由。
  2. 使用 AOP 切换数据源

    • 结合 Spring AOP,在 Service 方法执行前通过自定义注解或线程局部变量设置当前需要使用的数据源。

四、处理事务一致性问题

  1. 分布式事务处理

    • 在多数据源涉及不同数据库实例的情况下,可能需要使用分布式事务解决方案,如 Seata、Narayana 等,以确保不同数据源之间的事务一致性。
  2. 本地事务处理

    • 如果多数据源位于同一数据库实例中,可以使用本地事务管理器来管理事务。

示例代码

以下是一个简单的 Spring Boot 项目中配置 MyBatis 多数据源事务的示例:

@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

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

    @Primary
    @Bean(name = "primaryTransactionManager")
    public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "secondaryTransactionManager")
    public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
                                        @Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("primary", primaryDataSource);
        targetDataSources.put("secondary", secondaryDataSource);
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
        return dynamicDataSource;
    }
}

@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryMyBatisConfig {

    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/primary/*.xml"));
        return factoryBean.getObject();
    }

    @Bean(name = "primarySqlSessionTemplate")
    public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

@Configuration
@MapperScan(basePackages = "com.example.mapper.secondary", sqlSessionTemplateRef = "secondarySqlSessionTemplate")
public class SecondaryMyBatisConfig {

    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/secondary/*.xml"));
        return factoryBean.getObject();
    }

    @Bean(name = "secondarySqlSessionTemplate")
    public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier("secondarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

@Aspect
@Component
public class DataSourceAspect {

    @Before("@annotation(ds)")
    public void changeDataSource(DataSource ds) {
        DynamicDataSource.setDataSource(ds.value());
    }

    @After("@annotation(ds)")
    public void clearDataSource(DataSource ds) {
        DynamicDataSource.clearDataSource();
    }
}

public enum DataSource {
    PRIMARY("primary"), SECONDARY("secondary");

    private final String value;

    DataSource(String value) {
        this.value = value;
    }

    public String value() {
        return this.value;
    }
}

在上述示例中,通过 DynamicDataSource 类实现了数据源的动态切换,并在 Service 方法上使用自定义的 @DataSource 注解来指定当前需要使用的数据源。

注意事项

  1. 事务管理器的选择

    • 在使用多个数据源时,需要为每个数据源配置独立的事务管理器,并在 @Transactional 注解中指定具体的事务管理器。
  2. 数据源切换时机

    • 数据源切换通常需要在 Service 方法执行前完成,并确保在方法执行结束后恢复默认数据源。
  3. 分布式事务处理

    • 在多数据源涉及不同数据库实例的情况下,分布式事务处理是必不可少的,需要选择合适的分布式事务解决方案。

通过以上配置和步骤,MyBatis 可以实现对多个数据源的事务管理,满足复杂业务场景下的数据访问需求。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。