Druid数据库多数据源
Spring的多数据源支持—AbstractRoutingDataSource,AbstractRoutingDataSource定义了抽象的determineCurrentLookupKey方法,子类实现此方法,来确定要使用的数据源
Druid 实现多数据源支持,核心是Overwrite AbstractRoutingDataSource 的 determineCurrentLookupKey 方法
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean { protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");Object lookupKey = determineCurrentLookupKey();DataSource dataSource = this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource forlookup key [" + lookupKey + "]");}return dataSource;}// 确定当前要使用的数据源protected abstract Object determineCurrentLookupKey();}
配置多数据源
以springboot框架为基础使用aop注解的方式依赖Druid配置多数据源
1.添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- aop注解实现aspectjweaver依赖 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency><!-- mybatis-spring-boot-starter --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency>
2.配置Druid属性
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcefirst:url: jdbc:mysql://localhost:3306/***?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTCusername: rootpassword: *****second:enable: trueurl: jdbc:mysql://localhost:3306/***?useUnicode=true&characterEncoding=utf- 8&serverTimezone=UTCusername: rootpassword: *****druid:# 初始连接数initialSize: 5# 最小连接池数量minIdle: 10# 最大连接池数量maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 300000# 配置一个连接在池中最大生存的时间,单位是毫秒maxEvictableIdleTimeMillis: 900000# 配置检测连接是否有效validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsewebStatFilter:enabled: truestatViewServlet:enabled: true# 控制台管理用户名和密码login-username: adminlogin-password: 123456#配置监控统计拦截的filters:stat:监控统计、self4j(使用log4j的记得导入log4j的依赖):日志记录、wall:防御sql注入 此处配置不能遗漏服务sql监控台不能监控sqlfilters: stat,wall,log4j2filter:stat:enabled: true# 慢SQL记录log-slow-sql: trueslow-sql-millis: 1000merge-sql: truewall:enabled: trueconfig:multi-statement-allow: true
3.配置动态数据源
继承AbstractRoutingDataSource重写determineCurrentLookupKey()方法
/** * 动态数据源 */public class DynamicDatesource extends AbstractRoutingDataSource {public DynamicDatesource(DataSource dataSource,HashMap<Object, Object> map){super.setDefaultTargetDataSource(dataSource);super.setTargetDataSources(map);super.afterPropertiesSet();}/** * 获取key指定数据源 * @return */@Overrideprotected Object determineCurrentLookupKey() {return DynamicDateSourceCut.getLocal();}}
配置数据源
//配置数据源@Configurationpublic class DruidConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.first")public DataSource firstDataSource(DruidSourceProperties druidSourceProperties){return druidSourceProperties.dataSource(new DruidDataSourceWrapper());}@Bean@ConfigurationProperties(prefix = "spring.datasource.second")@ConditionalOnProperty(prefix ="spring.datasource.second",name ="enable",havingValue = "true")public DataSource secondDataSource(DruidSourceProperties druidSourceProperties){return druidSourceProperties.dataSource(new DruidDataSourceWrapper());}@Bean@Primary//在多数据源的时候,使用@Primary注解用于指定其中一个作为主数据源public DynamicDatesource setDymaicDatesource(DataSource firstDataSource){HashMap
4.配置数据源切换
package com.example.springboot.data;/***数据源切换 保存每个数据源指定的lookupKey*/public class DynamicDateSourceCut {/** * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, *所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 */private static ThreadLocal<String> threadLocal=new ThreadLocal<>();/** * 设置数据源的变量 */public static void setLocal(String local){threadLocal.set(local);}/** * 获得数据源的变量 */public static String getLocal(){return threadLocal.get();}/** * 清空数据源变量 */public static void clearLocl() {threadLocal.remove();}}
5.定义aop切面实现
/** * 定义数据源切面 */@Aspect@Componentpublic class DateSourceAspect {private static final Logger log= LoggerFactory.getLogger(DateSourceAspect.class);/** * 定义切点使用注解 * @within类注解 * @annotation方法注解 */@Pointcut("@annotation(com.example.springboot.aspect.DateSource)"+ "||@within(com.example.springboot.aspect.DateSource)")public void PintCut(){}/** * 通知使用环绕的通知方法 */@Around("PintCut()")public Object ResovePoint(ProceedingJoinPoint point) throws Throwable {//获取注解DateSource dateSource = getDateSource(point);if (dateSource!=null){DynamicDateSourceCut.setLocal(dateSource.value().name());}try {//执行方法 return point.proceed();} finally {// 销毁数据源 在执行方法之后DynamicDateSourceCut.clearLocl();}}/** * 获取注解 */protected DateSource getDateSource(ProceedingJoinPoint point){MethodSignature signature = (MethodSignature) point.getSignature();//获取方法或方法类型上的注解DateSource annotation;//获取class类型上注解annotation=AnnotationUtils.findAnnotation(signature.getDeclaringType(),DateSource.class);if (annotation==null){annotation = AnnotationUtils.findAnnotation(signature.getMethod(), DateSource.class);}return annotation;}}
定义的注解和注解值
/** * 定义注解 */@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DateSource {DateSourceType value()default DateSourceType.FIRST;}/***使用枚举定义注解的值*/public enum DateSourceType {//默认数据库FIRST,SECOND;}