最近整理了一下spring boot关于对多数据源的配置,记录下来:
一, 引入Jar包:
<dependency> <!-- MySql驱动 --> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <!-- 连接池 --> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.
二, 配置参数:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
druid:
one: #数据源1
url: jdbc:mysql://localhost:3306/test1?...
username: root
password: root
two: #数据源2
url: jdbc:mysql://localhost:3306/test2?...
username: root
password: root
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
(参数配置,可参考: https://gitee.com/wenshao/druid/tree/master/druid-spring-boot-starter)
三, 编写配置文件:
1, 定义数据源名称常量 :
package com.gy.fast.common.config.data; /** * 数据源名称 * @author geYang * @date 2018-05-14 */ public interface DataSourceNames { String ONE = "ONE"; String TWO = "TWO"; }
2, 创建动态数据源:
package com.gy.fast.common.config.data; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.Map; /** * 动态数据源 * @author geYang * @date 2018-05-14 */ public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); /** * 配置DataSource, defaultTargetDataSource为主数据库 */ public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return getDataSource(); } public static void setDataSource(String dataSource) { contextHolder.set(dataSource); } public static String getDataSource() { return contextHolder.get(); } public static void clearDataSource() { contextHolder.remove(); } }
3, 动态数据源配置:
package com.gy.fast.common.config.data; import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 配置多数据源 * @author geYang * @date 2018-05-14 */ @Configuration public class DynamicDataSourceConfig { /** * 创建 DataSource Bean * */ @Bean @ConfigurationProperties("spring.datasource.druid.one") public DataSource oneDataSource(){ DataSource dataSource = DruidDataSourceBuilder.create().build(); return dataSource; } @Bean @ConfigurationProperties("spring.datasource.druid.two") public DataSource twoDataSource(){ DataSource dataSource = DruidDataSourceBuilder.create().build(); return dataSource; } /** * 如果还有数据源,在这继续添加 DataSource Bean * */ @Bean @Primary public DynamicDataSource dataSource(DataSource oneDataSource, DataSource twoDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(2); targetDataSources.put(DataSourceNames.ONE, oneDataSource); targetDataSources.put(DataSourceNames.TWO, twoDataSource); // 还有数据源,在targetDataSources中继续添加 System.out.println("DataSources:" + targetDataSources); return new DynamicDataSource(oneDataSource, targetDataSources); } }
4, 定义动态数据源注解:
package com.gy.fast.common.config.data; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 多数据源注解 * @author geYang * @date 2018-05-14 */ @Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { String value() default DataSourceNames.ONE; }
5, 设置数据源 AOP 代理:
package com.gy.fast.common.config.data; import java.lang.reflect.Method; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; /** * 数据源AOP切面处理 * @author geYang * @date 2018-05-14 */ @Aspect @Component public class DataSourceAspect implements Ordered { protected Logger logger = LoggerFactory.getLogger(getClass()); /** * 切点: 所有配置 DataSource 注解的方法 */ @Pointcut("@annotation(com.gy.fast.common.config.data.DataSource)") public void dataSourcePointCut() {} @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource ds = method.getAnnotation(DataSource.class); // 通过判断 DataSource 中的值来判断当前方法应用哪个数据源 DynamicDataSource.setDataSource(ds.value()); System.out.println("当前数据源: " + ds.value()); logger.debug("set datasource is " + ds.value()); try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); logger.debug("clean datasource"); } } @Override public int getOrder() { return 1; } }
四, 修改启动文件:
package com.gy.fast; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.annotation.Import; import com.gy.fast.common.config.data.DynamicDataSourceConfig; /** * 动态数据源配置,需要将自有的配置依赖(DynamicDataSourceConfig),将原有的依赖去除(DataSourceAutoConfiguration) * @author geYang * @date 2018-05-15 */ @Import({DynamicDataSourceConfig.class}) @SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) public class FastApplication { public static void main(String[] args) { SpringApplication.run(FastApplication.class, args); } }
五, 配置完成, 进行测试:
package com.gy.fast; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.gy.fast.common.config.data.DataSource; import com.gy.fast.common.config.data.DataSourceNames; import com.gy.fast.module.sys.entity.SysUser; import com.gy.fast.module.sys.service.SysUserService; /** * 测试多数据源 * @author geYang * @date 2018-05-15 */ @Service public class DataSourceTestService { @Autowired private SysUserService sysUserService; public SysUser test1(Long userId){ return sysUserService.selectById(userId); } @DataSource(DataSourceNames.TWO) public SysUser test2(Long userId){ return sysUserService.selectById(userId); } }
package com.gy.fast; import org.apache.commons.lang3.builder.ToStringBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.gy.fast.module.sys.entity.SysUser; @RunWith(SpringRunner.class) @SpringBootTest public class DynamicDataSourceTest { @Autowired private DataSourceTestService dataSourceTestService; @Test public void test(){ // 数据源ONE SysUser user1 = dataSourceTestService.test1(1L); System.out.println(ToStringBuilder.reflectionToString(user1)); // 数据源TWO SysUser user2 = dataSourceTestService.test2(1L); System.out.println(ToStringBuilder.reflectionToString(user2)); // 数据源ONE SysUser user3 = dataSourceTestService.test1(1L); System.out.println(ToStringBuilder.reflectionToString(user3)); } }
原文:https://www.cnblogs.com/roak/p/14813109.html