一、配置流程
在 Spring Boot 中实现多数据源配置通常用于需要连接多个数据库的场景。主要有以下几个步骤:
- 配置多个数据源的连接信息。
- 定义多个数据源的 Bean。
- 为每个数据源配置MyBatis的SqlSessionFactory和事务管理器。
- 为每个数据源定义Mapper接口和Mapper XML文件。
- 在服务类中使用不同的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
bashimport 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
bashimport 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接口
bashpackage 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接口
bashpackage 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();
}
}
三、动态数据源切换
如果需要动态切换数据源,可以通过以下步骤:
-
动态数据源配置
bashimport org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } }
-
数据源上下文持有者
bashpublic 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切面
bashimport 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()); } }
-
自定义注解
bashimport 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(); }
-
使用动态数据源
bash@Service public class MyService { @DataSource("primary") public List<User> getUsers() { return primaryUserMapper.findAll(); } @DataSource("secondary") public List<Product> getProducts() { return secondaryProductMapper.findAll(); } }