Spring Boot 实现多数据源配置

一、配置流程

在 Spring Boot 中实现多数据源配置通常用于需要连接多个数据库的场景。主要有以下几个步骤:

  1. 配置多个数据源的连接信息。
  2. 定义多个数据源的 Bean。
  3. 为每个数据源配置MyBatis的SqlSessionFactory和事务管理器。
  4. 为每个数据源定义Mapper接口和Mapper XML文件。
  5. 在服务类中使用不同的Mapper接口操作对应的数据源。

二、详细步骤

2.1 添加依赖

首先,在pom.xml中添加MyBatis、数据库驱动和Spring Boot的JDBC依赖。

bash 复制代码
<dependencies>
    <!-- Spring Boot Starter Web (可选,如果需要使用Web功能) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MyBatis Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>

    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- Spring Boot Starter JDBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
</dependencies>
2.2 配置多数据源

在application.properties或application.yml中配置多个数据源的连接信息。

bash 复制代码
spring:
  datasource:
    primary:
      jdbc-url: jdbc:mysql://localhost:3306/db1
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      jdbc-url: jdbc:mysql://localhost:3306/db2
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
2.3 配置数据源Bean

在Spring Boot中,通过@Configuration类来配置多个数据源的Bean。

bash 复制代码
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 {

    // 主数据源
    @Bean(name = "primaryDataSource")
    @Primary
    @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();
    }
}
2.4 配置MyBatis的SqlSessionFactory和事务管理器

为每个数据源配置独立的SqlSessionFactory和DataSourceTransactionManager。

  • PrimaryDataSourceConfig .java

    bash 复制代码
    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.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    
    import javax.sql.DataSource;
    
    @Configuration
    @MapperScan(basePackages = "com.example.mapper.primary", sqlSessionFactoryRef = 	"primarySqlSessionFactory")
    public class PrimaryDataSourceConfig {
    
    	@Bean(name = "primarySqlSessionFactory")
    	@Primary
    	public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        	SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        	sessionFactory.setDataSource(dataSource);
        	// 设置MyBatis的Mapper XML文件路径
        	sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                	.getResources("classpath:mapper/primary/*.xml"));
        	return sessionFactory.getObject();
    	}
    
    	@Bean(name = "primaryTransactionManager")
    	@Primary
        public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
        	return new DataSourceTransactionManager(dataSource);
    	}
    }
  • SecondaryDataSourceConfig.java

    bash 复制代码
    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 org.springframework.jdbc.datasource.DataSourceTransactionManager;
    
    import javax.sql.DataSource;
    
    @Configuration
    @MapperScan(basePackages = "com.example.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory")
    public class SecondaryDataSourceConfig {
    
    	@Bean(name = "secondarySqlSessionFactory")
    	public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
        	SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        	sessionFactory.setDataSource(dataSource);
        	// 设置MyBatis的Mapper XML文件路径
        	sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                	.getResources("classpath:mapper/secondary/*.xml"));
        	return sessionFactory.getObject();
    	}
    
    	@Bean(name = "secondaryTransactionManager")
    	public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
        	return new DataSourceTransactionManager(dataSource);
    	}
    }
2.5 配置Mapper接口和XML文件

将Mapper接口和XML文件分别放在不同的包中,以区分不同的数据源。

  • 主数据源Mapper接口

    bash 复制代码
    package com.example.mapper.primary;
    
    import com.example.model.User;
    import org.apache.ibatis.annotations.Mapper;
    import java.util.List;
    
    @Mapper
    public interface PrimaryUserMapper {
    	List<User> findAll();
    }
  • 次数据源Mapper接口

    bash 复制代码
    package com.example.mapper.secondary;
    
    import com.example.model.Product;
    import org.apache.ibatis.annotations.Mapper;
    import java.util.List;
    
    @Mapper
    public interface SecondaryProductMapper {
    	List<Product> findAll();
    }
  • 主数据源Mapper XML文件

    在src/main/resources/mapper/primary目录下创建UserMapper.xml:

    bash 复制代码
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.mapper.primary.PrimaryUserMapper">
    	<select id="findAll" resultType="com.example.model.User">
        	SELECT * FROM user
    	</select>
    </mapper>
  • 次数据源Mapper XML文件

    在src/main/resources/mapper/secondary目录下创建ProductMapper.xml:

    bash 复制代码
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.mapper.secondary.SecondaryProductMapper">
    	<select id="findAll" resultType="com.example.model.Product">
        	SELECT * FROM product
    	</select>
    </mapper>
2.6 使用多数据源

在Service中注入对应的Mapper接口,并使用@Transactional注解指定事务管理器。

bash 复制代码
import com.example.mapper.primary.PrimaryUserMapper;
import com.example.mapper.secondary.SecondaryProductMapper;
import com.example.model.User;
import com.example.model.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class MyService {

    @Autowired
    private PrimaryUserMapper primaryUserMapper;

    @Autowired
    private SecondaryProductMapper secondaryProductMapper;

    @Transactional("primaryTransactionManager")
    public List<User> getUsers() {
        return primaryUserMapper.findAll();
    }

    @Transactional("secondaryTransactionManager")
    public List<Product> getProducts() {
        return secondaryProductMapper.findAll();
    }
}

三、动态数据源切换

如果需要动态切换数据源,可以通过以下步骤:

  1. 动态数据源配置

    bash 复制代码
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
    	@Override
    	protected Object determineCurrentLookupKey() {
        	return DataSourceContextHolder.getDataSourceType();
    	}
    }
  2. 数据源上下文持有者

    bash 复制代码
    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. AOP切面

    bash 复制代码
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class DataSourceAspect {
    
    	@Before("@annotation(dataSource)")
    	public void switchDataSource(DataSource dataSource) {
        	DataSourceContextHolder.setDataSourceType(dataSource.value());
    	}
    }
  4. 自定义注解

    bash 复制代码
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DataSource {
    	String value();
    }
  5. 使用动态数据源

    bash 复制代码
    @Service
    public class MyService {
    
    	@DataSource("primary")
    	public List<User> getUsers() {
        	return primaryUserMapper.findAll();
    	}
    
    	@DataSource("secondary")
    	public List<Product> getProducts() {
        	return secondaryProductMapper.findAll();
    	}
    }
相关推荐
陈随易20 小时前
FFmpeg 9.0 发布,代号 Lei,音视频处理再升级
前端·后端·程序员
IT界的老黄牛1 天前
Spring Boot 的 `/actuator/health` 不是免费的:健康检查打爆连接池的反面教材
spring boot·hikaricp·actuator·健康检查·线上排障·架构反模式
凌虚1 天前
面向 MySQL 用户的 PostgreSQL 快速上手指南
数据库·后端·架构
都叫我大帅哥1 天前
Java JJWT 完全指南:从入门到生产级实践
java
IT_陈寒1 天前
Vite静态资源引用这个坑我踩得有点疼
前端·人工智能·后端
前端开发张小七1 天前
Java 学习笔记 · 第一课:基础语法与核心概念
java·后端
海盗12341 天前
微软技术周报 ——2026-08-03
后端·python·microsoft·c#·.netcore
沙湖遇雨1 天前
6. Pipeline 的生命周期
后端
青青子衿悠悠我心1 天前
TRAE Work让财务月底对账3小时变5分钟
后端
创安电气研究所1 天前
用TypeScript给Modbus设备做一层适配器
后端