Spring-Boot-实现多数据源配置
Spring Boot 实现多数据源配置
一、配置流程
在 Spring Boot 中实现多数据源配置通常用于需要连接多个数据库的场景。主要有以下几个步骤:
- 配置多个数据源的连接信息。
- 定义多个数据源的 Bean。
- 为每个数据源配置MyBatis的SqlSessionFactory和事务管理器。
- 为每个数据源定义Mapper接口和Mapper XML文件。
- 在服务类中使用不同的Mapper接口操作对应的数据源。
二、详细步骤
2.1 添加依赖
首先,在pom.xml中添加MyBatis、数据库驱动和Spring Boot的JDBC依赖。
org.springframework.boot spring-boot-starter-web
org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.2
mysql mysql-connector-java
org.springframework.boot spring-boot-starter-jdbc
2.2 配置多数据源
在application.properties或application.yml中配置多个数据源的连接信息。 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。 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”) @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 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 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 findAll(); }
- 主数据源Mapper XML文件 在src/main/resources/mapper/primary目录下创建UserMapper.xml: xml version=“1.0” encoding=“UTF-8” ?
SELECT * FROM user
- 次数据源Mapper XML文件 在src/main/resources/mapper/secondary目录下创建ProductMapper.xml: xml version=“1.0” encoding=“UTF-8” ?
SELECT * FROM product
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 { @Autowired private PrimaryUserMapper primaryUserMapper; @Autowired private SecondaryProductMapper secondaryProductMapper; @Transactional(“primaryTransactionManager”) public List getUsers() { return primaryUserMapper.findAll(); } @Transactional(“secondaryTransactionManager”) public List getProducts() { return secondaryProductMapper.findAll(); } }
三、动态数据源切换
如果需要动态切换数据源,可以通过以下步骤:
- 动态数据源配置 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } }
- 数据源上下文持有者 public class DataSourceContextHolder { private static final ThreadLocal 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 getUsers() { return primaryUserMapper.findAll(); } @DataSource(“secondary”) public List getProducts() { return secondaryProductMapper.findAll(); } }