Spring Boot整合多个MyBatis数据源实战教程

Spring Boot整合多个MyBatis数据源实战教程

在实际项目开发中,经常会遇到需要同时连接多个数据库的场景,例如读写分离架构(主库写、从库读)、多业务系统数据集成等。本文将详细讲解Spring Boot整合MyBatis实现多数据源的两种核心方案:静态多数据源 (固定数据源映射)和动态多数据源(运行时切换),并提供完整的代码示例和避坑指南。

一、前期准备

1.1 开发环境

  • 开发工具:IntelliJ IDEA

  • 构建工具:Maven 3.6+

  • 框架版本:Spring Boot 2.7.x、MyBatis 3.5.x

  • 数据库:MySQL 8.0(两个独立数据库,本文以db1和db2为例)

  • 连接池:HikariCP(Spring Boot默认,性能最优)

1.2 项目依赖

在pom.xml中引入核心依赖,包含Spring Boot Web、MyBatis、MySQL驱动及动态数据源专用starter(如需动态切换):

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.3.1</version>
    </dependency>
    
    
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.5.0</version>
    </dependency>
    
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1.3 数据库准备

创建两个独立数据库并初始化测试表,以用户表为例:

sql 复制代码
-- 数据库1:db1,存储主业务数据
CREATE DATABASE IF NOT EXISTS db1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE db1;
CREATE TABLE IF NOT EXISTS t_user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    age INT NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 数据库2:db2,存储日志或附属业务数据
CREATE DATABASE IF NOT EXISTS db2 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE db2;
CREATE TABLE IF NOT EXISTS t_log (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    operation VARCHAR(100) NOT NULL,
    operator VARCHAR(50) NOT NULL,
    operate_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

二、静态多数据源实现(主从分离场景)

静态多数据源适用于固定数据源映射场景,例如不同业务模块对应不同数据库,或主库写、从库读的读写分离架构。核心思路是为每个数据源独立配置DataSource、SqlSessionFactory和事务管理器。

2.1 配置文件编写

在application.yml中配置两个数据源的连接信息,指定主数据源(@Primary标识):

yaml 复制代码
spring:
  datasource:
    # 主数据源(db1)
    primary:
      url: jdbc:mysql://localhost:3306/db1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
      # Hikari连接池配置
      hikari:
        minimum-idle: 5
        maximum-pool-size: 20
        connection-timeout: 30000
    # 从数据源(db2)
    secondary:
      url: jdbc:mysql://localhost:3306/db2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
      hikari:
        minimum-idle: 5
        maximum-pool-size: 20
        connection-timeout: 30000

# MyBatis全局配置
mybatis:
  configuration:
    map-underscore-to-camel-case: true # 下划线转驼峰
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志
  mapper-locations: classpath:mapper/**/*.xml # Mapper XML文件路径

2.2 数据源配置类

创建数据源配置类,通过@ConfigurationProperties绑定配置文件中的数据源信息,为主数据源添加@Primary注解避免Bean冲突:

java 复制代码
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

    // 主数据源(db1)
    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        // 指定连接池类型为HikariCP
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    // 从数据源(db2)
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }
}

2.3 MyBatis配置类

为每个数据源独立配置SqlSessionFactory和Mapper扫描路径,通过@MapperScan指定不同数据源对应的Mapper接口包:

2.3.1 主数据源MyBatis配置
java 复制代码
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;

// 扫描主数据源的Mapper接口(com.example.mapper.primary包下)
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryMyBatisConfig {

    private final DataSource primaryDataSource;

    // 注入主数据源
    public PrimaryMyBatisConfig(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
        this.primaryDataSource = primaryDataSource;
    }

    // 主数据源的SqlSessionFactory
    @Primary
    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(primaryDataSource);
        // 配置主数据源的Mapper XML路径(可选,若全局配置已覆盖可省略)
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/primary/*.xml"));
        return factoryBean.getObject();
    }
}
2.3.2 从数据源MyBatis配置
java 复制代码
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;

// 扫描从数据源的Mapper接口(com.example.mapper.secondary包下)
@Configuration
@MapperScan(basePackages = "com.example.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryMyBatisConfig {

    private final DataSource secondaryDataSource;

    // 注入从数据源
    public SecondaryMyBatisConfig(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        this.secondaryDataSource = secondaryDataSource;
    }

    // 从数据源的SqlSessionFactory
    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(secondaryDataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/secondary/*.xml"));
        return factoryBean.getObject();
    }
}

2.4 事务管理器配置(可选)

若需要事务支持,需为每个数据源配置独立的事务管理器,使用@Transactional的value属性指定事务管理器:

java 复制代码
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;

@Configuration
public class TransactionManagerConfig {

    // 主数据源事务管理器
    @Primary
    @Bean(name = "primaryTransactionManager")
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("primaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    // 从数据源事务管理器
    @Bean(name = "secondaryTransactionManager")
    public PlatformTransactionManager secondaryTransactionManager(
            @Qualifier("secondaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

2.5 业务层实现

按数据源划分Mapper接口和XML文件,编写Service层代码调用不同数据源的操作:

2.5.1 实体类
java 复制代码
// 主数据源实体(db1.t_user)
import lombok.Data;
import java.time.LocalDateTime;

@Data
public class User {
    private Long id;
    private String username;
    private Integer age;
    private LocalDateTime createTime;
}

// 从数据源实体(db2.t_log)
@Data
public class Log {
    private Long id;
    private String operation;
    private String operator;
    private LocalDateTime operateTime;
}
2.5.2 Mapper接口与XML
java 复制代码
// 主数据源Mapper(com.example.mapper.primary.UserMapper)
import com.example.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;

public interface UserMapper {
    @Insert("INSERT INTO t_user(username, age) VALUES(#{username}, #{age})")
    int insertUser(User user);

    @Select("SELECT * FROM t_user")
    List<User> listAllUsers();
}

// 从数据源Mapper(com.example.mapper.secondary.LogMapper)
import com.example.entity.Log;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;

public interface LogMapper {
    @Insert("INSERT INTO t_log(operation, operator) VALUES(#{operation}, #{operator})")
    int insertLog(Log log);

    @Select("SELECT * FROM t_log")
    List<Log> listAllLogs();
}
2.5.3 Service层
java 复制代码
import com.example.entity.Log;
import com.example.entity.User;
import com.example.mapper.primary.UserMapper;
import com.example.mapper.secondary.LogMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Service
@RequiredArgsConstructor
public class MultiDataSourceService {

    private final UserMapper userMapper;
    private final LogMapper logMapper;

    // 操作主数据源,指定主事务管理器
    @Transactional(value = "primaryTransactionManager")
    public void addUser(User user) {
        userMapper.insertUser(user);
        // 模拟日志记录,调用从数据源
        Log log = new Log();
        log.setOperation("新增用户:" + user.getUsername());
        log.setOperator("admin");
        logMapper.insertLog(log);
    }

    // 查询主数据源数据
    public List<User> getAllUsers() {
        return userMapper.listAllUsers();
    }

    // 查询从数据源数据
    public List<Log> getAllLogs() {
        return logMapper.listAllLogs();
    }
}

三、动态多数据源实现(运行时切换)

动态多数据源适用于运行时根据条件切换数据源的场景,例如按用户ID分库、按业务类型切换数据库等。核心思路是继承AbstractRoutingDataSource实现数据源路由,通过ThreadLocal存储当前线程的数据源标识。

3.1 核心组件开发

3.1.1 数据源上下文管理器

使用ThreadLocal存储当前线程的数据源标识,确保线程安全:

(注:文档部分内容可能由 AI 生成)

相关推荐
bcbnb2 小时前
如何解析iOS崩溃日志:从获取到符号化分析
后端
许泽宇的技术分享3 小时前
当AI学会“说人话“:Azure语音合成技术的魔法世界
后端·python·flask
用户69371750013843 小时前
4.Kotlin 流程控制:强大的 when 表达式:取代 Switch
android·后端·kotlin
用户69371750013843 小时前
5.Kotlin 流程控制:循环的艺术:for 循环与区间 (Range)
android·后端·kotlin
vx_bisheyuange3 小时前
基于SpringBoot的宠物商城网站的设计与实现
spring boot·后端·宠物
bcbnb3 小时前
全面解析网络抓包工具使用:Wireshark和TCPDUMP教程
后端
leonardee3 小时前
Spring Security安全框架原理与实战
java·后端
一个处女座的程序猿O(∩_∩)O3 小时前
Spring Boot、Redis、RabbitMQ 在项目中的核心作用详解
spring boot·redis·java-rabbitmq
回家路上绕了弯3 小时前
包冲突排查指南:从发现到解决的全流程实战
分布式·后端
爱分享的鱼鱼3 小时前
部署Vue+Java Web应用到云服务器完整指南
前端·后端·全栈