springboot 中怎么利用mybatis实现多数据源

发布时间:2021-07-08 16:36:41 作者:Leah
来源:亿速云 阅读:232
# SpringBoot 中怎么利用 MyBatis 实现多数据源

## 前言

在现代企业级应用开发中,多数据源的需求越来越普遍。例如:
- 需要同时访问多个业务数据库
- 读写分离场景
- 分库分表场景
- 需要连接不同数据库类型(MySQL + Oracle)

SpringBoot 结合 MyBatis 作为流行的 Java 持久层框架组合,如何优雅地实现多数据源配置是开发者必须掌握的技能。本文将详细介绍在 SpringBoot 2.x 环境下,通过 MyBatis 实现多数据源的完整方案。

## 一、多数据源实现原理

### 1.1 Spring 数据源抽象

Spring 通过 `DataSource` 接口抽象数据源概念,多数据源本质上是创建多个 `DataSource` 实例,并在不同场景下选择使用哪个数据源。

### 1.2 关键实现点

1. **多数据源配置**:配置多个 `DataSource` Bean
2. **SQLSessionFactory 绑定**:为每个数据源创建独立的 `SqlSessionFactory`
3. **事务管理**:配置多个 `DataSourceTransactionManager`
4. **动态切换**:通过 AOP 或手动方式切换数据源

## 二、基础多数据源实现

### 2.1 环境准备

```xml
<!-- pom.xml 关键依赖 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

2.2 多数据源配置

// 主数据源配置
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", 
           sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {

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

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

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

// 从数据源配置
@Configuration
@MapperScan(basePackages = "com.example.mapper.secondary", 
           sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {

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

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

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

2.3 application.yml 配置

spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/primary_db
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      url: jdbc:mysql://localhost:3306/secondary_db
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

2.4 使用示例

@Service
public class UserService {
    
    @Autowired
    @Qualifier("primaryUserMapper")
    private UserMapper primaryUserMapper;
    
    @Autowired
    @Qualifier("secondaryUserMapper")
    private UserMapper secondaryUserMapper;
    
    @Transactional(transactionManager = "primaryTransactionManager")
    public void addPrimaryUser(User user) {
        primaryUserMapper.insert(user);
    }
    
    @Transactional(transactionManager = "secondaryTransactionManager")
    public void addSecondaryUser(User user) {
        secondaryUserMapper.insert(user);
    }
}

三、动态数据源实现

基础方案需要显式指定使用哪个数据源,更优雅的方式是实现动态切换。

3.1 动态数据源抽象

public class DynamicDataSource extends AbstractRoutingDataSource {
    
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}

3.2 数据源上下文

public class DataSourceContextHolder {
    
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }
    
    public static String getDataSourceType() {
        return contextHolder.get();
    }
    
    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}

3.3 动态数据源配置

@Configuration
public class DataSourceConfig {
    
    @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();
    }
    
    @Bean
    @Primary
    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 dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(primaryDataSource);
        return dataSource;
    }
}

3.4 自定义注解实现切换

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "primary";
}

@Aspect
@Component
@Order(1)
public class DataSourceAspect {
    
    @Before("@annotation(dataSource)")
    public void beforeSwitchDataSource(DataSource dataSource) {
        DataSourceContextHolder.setDataSourceType(dataSource.value());
    }
    
    @After("@annotation(dataSource)")
    public void afterSwitchDataSource(DataSource dataSource) {
        DataSourceContextHolder.clearDataSourceType();
    }
}

3.5 使用示例

@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @DataSource("primary")
    public List<Order> getPrimaryOrders() {
        return orderMapper.selectAll();
    }
    
    @DataSource("secondary")
    public List<Order> getSecondaryOrders() {
        return orderMapper.selectAll();
    }
}

四、多数据源事务管理

多数据源环境下的事务管理需要特别注意:

4.1 JTA 分布式事务

对于严格一致性要求的场景,可以使用 JTA 实现分布式事务:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

4.2 链式事务管理

@Configuration
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(
            DynamicDataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }
}

4.3 事务传播行为

@Service
public class ComplexService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void complexOperation() {
        // 方法体
    }
}

五、性能优化建议

  1. 连接池配置:为每个数据源配置合适的连接池参数

    spring:
     datasource:
       primary:
         hikari:
           maximum-pool-size: 20
           minimum-idle: 5
    
  2. MyBatis 二级缓存:合理配置缓存策略

    <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
    
  3. SQL 优化:针对不同数据源特点优化 SQL

  4. 监控集成:集成 Druid 等监控工具

六、常见问题解决方案

6.1 启动时报错”找不到数据源”

解决方案: 1. 检查 @MapperScan 注解的 sqlSessionFactoryRef 配置 2. 确保 yml 配置前缀正确

6.2 事务不生效问题

解决方案: 1. 确保 @Transactional 指定了正确的 transactionManager 2. 检查方法是否为 public 3. 避免自调用问题

6.3 动态切换失效

解决方案: 1. 确保切面执行顺序高于事务切面 2. 检查 ThreadLocal 是否正确清理

七、扩展应用场景

7.1 读写分离实现

@Aspect
@Component
@Order(1)
public class ReadWriteDataSourceAspect {
    
    @Before("execution(* com.example.mapper.*.select*(..)) || " +
            "execution(* com.example.mapper.*.get*(..)) || " +
            "execution(* com.example.mapper.*.find*(..))")
    public void setReadDataSource() {
        DataSourceContextHolder.setDataSourceType("read");
    }
    
    @Before("execution(* com.example.mapper.*.insert*(..)) || " +
            "execution(* com.example.mapper.*.update*(..)) || " +
            "execution(* com.example.mapper.*.delete*(..))")
    public void setWriteDataSource() {
        DataSourceContextHolder.setDataSourceType("write");
    }
}

7.2 多租户实现

public class TenantDataSourceRouter extends AbstractRoutingDataSource {
    
    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getCurrentTenant();
    }
}

八、总结

本文详细介绍了 SpringBoot + MyBatis 实现多数据源的多种方案:

  1. 基础静态多数据源:适合固定场景的简单需求
  2. 动态数据源:更灵活的切换方式
  3. 高级应用:读写分离、多租户等场景

实际项目中选择方案时需要考虑: - 业务复杂程度 - 性能要求 - 一致性要求 - 团队技术栈

多数据源虽然强大,但也会带来复杂度提升,建议在真正需要时才引入多数据源方案。

附录:完整代码示例

GitHub 仓库地址

”`

注:由于篇幅限制,本文实际约3200字。要扩展到5350字,可以: 1. 增加更多实现细节和原理分析 2. 添加性能测试数据和对比 3. 补充更多异常场景处理方案 4. 增加不同数据库类型(如Oracle、PostgreSQL)的配置示例 5. 添加Spring Boot 3.x的适配说明 6. 扩展分布式事务的详细讲解 7. 增加与Spring Cloud的集成方案

推荐阅读:
  1. 怎么在springboot中利用Mybatis实现读写分离
  2. 详解SpringBoot和Mybatis配置多数据源

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mybatis spring boot

上一篇:SpringBoot中怎么配置单点Redis缓存

下一篇:SpringBoot中怎么搭建Thymeleaf环境

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》