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();
    	}
    }
相关推荐
小咖拉眯1 分钟前
蓝桥杯进制问题秒破解法|冲击省一题单(二)
java·数据结构·算法·蓝桥杯
kkk162224523 分钟前
C# Winform 实现换肤,并自定义皮肤功能
java·算法·c#
添砖Java中24 分钟前
零基础掌握分布式ID生成:从理论到实战的完整指南 [特殊字符]
java·分布式·分布式id
龙仔7251 小时前
打造独一无二的 CI/CD 工厂:Java 应用的自动化之旅
java·spring cloud·ci/cd·自动化·devops
互联网搬砖老肖1 小时前
Python开发合并多个PDF文件
java·python·pdf
এ旧栎1 小时前
蓝桥与力扣刷题(蓝桥 星期计算)
java·数据结构·算法·leetcode·职场和发展·蓝桥杯·规律
小杨4041 小时前
springboot框架项目实践应用八(validation自定义校验)
spring boot·后端·架构
武昌库里写JAVA1 小时前
微服务架构: SpringCloud实战案例
vue.js·spring boot·毕业设计·源码·课程设计
你在我身后1 小时前
spring-设计模式
spring boot