当前位置: 首页 > article >正文

MyBatis 多数据源支持

MyBatis 多数据源支持详解

在现代企业级应用中,通常会有多个数据源的需求,比如读取数据可能来自不同的数据库或不同的应用模块使用不同的数据库。这种情况下,MyBatis 提供了灵活的方式来支持多数据源操作。通过结合 Spring 框架的多数据源配置,我们可以在同一个项目中轻松实现对多个数据库的访问。

一、为什么需要多数据源

多个数据源的需求通常出现在以下场景中:

  1. 读写分离:将读取操作定向到从数据库,而将写入操作定向到主数据库,从而提高性能和可用性。
  2. 模块分离:不同的业务模块使用不同的数据库,例如用户信息和订单信息分别存储在不同的数据库中。
  3. 跨系统集成:有时需要访问外部系统的数据库,这要求应用程序能够同时连接多个数据库。

在这些场景中,MyBatis 与 Spring 的组合可以提供强大的多数据源支持。

二、MyBatis 多数据源的实现步骤

在 MyBatis 中支持多数据源,通常需要结合 Spring 来配置。实现步骤包括以下几个部分:

  1. 配置多数据源:定义多个 DataSource 对象,分别对应不同的数据库连接。
  2. 配置 SqlSessionFactory:为每个数据源配置对应的 SqlSessionFactory,这是 MyBatis 执行 SQL 的核心。
  3. 配置事务管理:为每个数据源配置相应的事务管理器。
  4. 动态切换数据源:如果需要在运行时动态选择数据源,可以使用 AbstractRoutingDataSource

三、配置多数据源

1. 定义数据源配置类

首先,我们需要为每个数据源定义配置类。这些配置类将从 application.ymlapplication.properties 文件中读取数据库连接参数,并创建对应的 DataSource

以下是定义两个数据源(主数据库和从数据库)的示例:

# application.yml 配置文件
spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/primary_db
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      url: jdbc:mysql://localhost:3306/secondary_db
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
@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 注解标识主数据源,多个数据源中如果有默认的数据源,通常需要使用该注解。
  • @ConfigurationProperties 从配置文件中读取数据库连接属性。
2. 配置 SqlSessionFactory

接下来,为每个数据源配置对应的 SqlSessionFactory,它是 MyBatis 的核心组件,用于执行 SQL 和管理事务。

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

    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }

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

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

    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }

    @Bean(name = "secondarySqlSessionTemplate")
    public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier("secondarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
  • @MapperScan 用于指定 Mapper 接口所在的包路径。basePackages 指定当前数据源所使用的 Mapper 包,sqlSessionFactoryRef 则指向当前数据源的 SqlSessionFactory
  • SqlSessionTemplate 是 MyBatis 提供的线程安全类,用于执行 SQL。
3. 配置事务管理器

为每个数据源配置相应的事务管理器。每个事务管理器与一个数据源绑定。

@Configuration
public class TransactionManagerConfig {

    @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);
    }
}
  • DataSourceTransactionManager 是 Spring 提供的基于数据源的事务管理器,它会管理指定数据源上的事务。

四、动态切换数据源

在某些场景中,我们需要在运行时根据业务逻辑动态切换数据源。可以通过 Spring 提供的 AbstractRoutingDataSource 实现数据源的动态切换。

1. 定义动态数据源

首先,定义一个 DynamicDataSource 类,继承自 AbstractRoutingDataSource,用于动态获取当前线程需要使用的数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceKey();
    }
}

determineCurrentLookupKey() 方法用于确定当前线程需要使用的数据源,通过 ThreadLocal 机制保存和切换数据源。

2. 定义数据源上下文管理类

接下来,定义一个 DataSourceContextHolder 类,用于管理当前线程的数据源信息。

public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    public static void clearDataSourceKey() {
        contextHolder.remove();
    }
}
3. 配置动态数据源

将所有数据源注入到 DynamicDataSource 中,配置动态数据源的路由规则。

@Configuration
public class DynamicDataSourceConfig {

    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
                                        @Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("primary", primaryDataSource);
        targetDataSources.put("secondary", secondaryDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
        return dynamicDataSource;
    }
}
  • setTargetDataSources 方法定义了不同的数据源,通过 key 区分。
  • setDefaultTargetDataSource 定义了默认数据源,当没有指定数据源时使用默认的数据源。
4. 动态切换数据源示例

在代码中,可以使用 DataSourceContextHolder 动态切换数据源:

public void performDatabaseOperation() {
    // 切换到主数据库
    DataSourceContextHolder.setDataSourceKey("primary");
    userService.selectUsersFromPrimaryDb();

    // 切换到从数据库
    DataSourceContextHolder.setDataSourceKey("secondary");
    userService.selectUsersFromSecondaryDb();

    // 恢复默认数据源
    DataSourceContextHolder.clearDataSourceKey();
}

五、测试和运行

在实际开发中,可以通过 JUnit 或者集成测试的方式来测试多数据源的配置。确认每个 Mapper 使用了正确的数据源并且事务管理器正常工作。

以下是多数据源的典型调用流程:

@Service
public class UserService {

    @Autowired
    private UserMapperPrimary userMapperPrimary;

    @Autowired
    private UserMapperSecondary userMapperSecondary;

    @Transactional("primaryTransactionManager")
    public List<User> selectUsersFromPrimaryDb() {
        return userMapperPrimary.selectUsers();


    }

    @Transactional("secondaryTransactionManager")
    public List<User> selectUsersFromSecondaryDb() {
        return userMapperSecondary.selectUsers();
    }
}

在这个例子中,selectUsersFromPrimaryDb 方法使用主数据库的事务管理器和数据源,而 selectUsersFromSecondaryDb 使用从数据库的事务管理器和数据源。

六、总结

MyBatis 支持多数据源配置并与 Spring 框架无缝集成。通过为每个数据源配置单独的 DataSourceSqlSessionFactoryTransactionManager,可以轻松实现对多个数据库的访问。在复杂的场景下,还可以使用 AbstractRoutingDataSource 实现动态数据源切换。

通过合理使用多数据源配置,能够实现读写分离、模块分离和跨系统集成,从而提升系统的性能和灵活性。在开发过程中,确保每个数据源的配置正确,事务管理器和数据源的绑定清晰,是确保多数据源功能正常工作的关键。


http://www.kler.cn/a/318724.html

相关文章:

  • spring中r类是什么
  • 算法演练----24点游戏
  • 使用HTML、CSS和JavaScript创建动态圣诞树
  • Redis主从复制(replication)
  • 什么是 Real-Time Factor (RTF)
  • Linux——gcc编译过程详解与ACM时间和进度条的制作
  • MySQL版本问题无法使用 group by xxx
  • GitLab 批量创建用户
  • 【web开发】Spring Boot 快速搭建Web项目(三)
  • Milvus - 比特集机制及其应用场景详解
  • DashVector x 通义千问大模型:打造基于专属知识的问答服务
  • 【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
  • 测试面试题:接口测试与功能测试相比较的优点有哪些?
  • 利士策分享,如何在有限的时间内过上富足的生活?
  • YOLOv9改进策略【损失函数篇】| 2024 引进Focaler-IoU损失函数 加强边界框回归
  • 扩散模型实战:从零开始训练手写数字生成模型
  • ★ C++进阶篇 ★ 二叉搜索树
  • service 命令:管理系统服务
  • AI学习指南深度学习篇-Adagrad超参数调优与性能优化
  • C语言 | Leetcode C语言题解之第435题无重叠区间
  • 编译原理3——词法分析
  • Pytest-如何将allure报告发布至公司内网
  • 微生物多样性数据的可视化技巧
  • 新能源汽车数据大全(产销数据\充电桩\专利等)
  • brpc之io事件分发器
  • 【会议征稿通知】第三届图像处理、计算机视觉与机器学习国际学术会议(ICICML 2024)