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();
    	}
    }
相关推荐
众少成多积小致巨13 小时前
JNI (Java Native Interface) 技术手册中文参考指南
android·java·c++
leeyi13 小时前
Manus Agent:一个全能 AI,和一支研究团队
后端·aigc·agent
东坡白菜13 小时前
破局全栈:前端开发的Java入门实战记录—JPA(2)
java·后端
代码丰13 小时前
RAG 系统如何实现全链路追踪:AOP 埋点与流式调用追踪实践
后端
小码编匠13 小时前
C# 工控上位机必备:数据转换工具类与十个核心模块
后端·c#·.net
神奇小汤圆14 小时前
一文读懂 OpenAI Codex 源码的原理、架构与未来
后端
道友可好14 小时前
AI 是最好的混乱放大器:代码熵管理实战
前端·人工智能·后端
掘金者阿豪15 小时前
写了很多内容后,我还是决定给自己搭一个Typecho博客
后端
Younglina15 小时前
打了3年羽毛球球才发现:我对自己的装备和胜率一无所知
前端·后端
Go_error16 小时前
Datatypes:Go 轻松支持数据库JSON类型
后端·go