溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

springboot 中怎么利用mybatis實現多數據源

發布時間:2021-07-08 16:36:41 來源:億速云 閱讀:222 作者:Leah 欄目:大數據
# 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的集成方案

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女