一、配置流程
在 Spring Boot 中实现多数据源配置通常用于需要连接多个数据库的场景。主要有以下几个步骤:
- 配置多个数据源的连接信息。
- 定义多个数据源的 Bean。
- 为每个数据源配置MyBatis的SqlSessionFactory和事务管理器。
- 为每个数据源定义Mapper接口和Mapper XML文件。
- 在服务类中使用不同的Mapper接口操作对应的数据源。
二、详细步骤
2.1 添加依赖
首先,在pom.xml中添加MyBatis、数据库驱动和Spring Boot的JDBC依赖。
<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中配置多个数据源的连接信息。
spring:datasource:primary:jdbc-url: jdbc:mysql://localhost:3306/db1username: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driversecondary:jdbc-url: jdbc:mysql://localhost:3306/db2username: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driver
2.3 配置数据源Bean
在Spring Boot中,通过@Configuration类来配置多个数据源的Bean。
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
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")@Primarypublic 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")@Primarypublic DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);} }
-
SecondaryDataSourceConfig.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 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接口
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接口
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:<?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:<?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注解指定事务管理器。
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 {@Autowiredprivate PrimaryUserMapper primaryUserMapper;@Autowiredprivate SecondaryProductMapper secondaryProductMapper;@Transactional("primaryTransactionManager")public List<User> getUsers() {return primaryUserMapper.findAll();}@Transactional("secondaryTransactionManager")public List<Product> getProducts() {return secondaryProductMapper.findAll();}
}
三、动态数据源切换
如果需要动态切换数据源,可以通过以下步骤:
- 动态数据源配置
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSourceType();} }
- 数据源上下文持有者
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();} }
- AOP切面
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());} }
- 自定义注解
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(); }
- 使用动态数据源
@Service public class MyService {@DataSource("primary")public List<User> getUsers() {return primaryUserMapper.findAll();}@DataSource("secondary")public List<Product> getProducts() {return secondaryProductMapper.findAll();} }