springboot动态使用DruidDataSource切换数据源(动态配置多个数据源)
1、添加依赖,在pom文件中添加
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>2.5.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency>
2、添加数据源名字定义,这个可有可无,没有的话,需要在切面和运用中定义
public class DataSourceConstants {/*** master数据源*/public static final String DS_KEY_MASTER = "master";/*** slave数据源*/public static final String DS_KEY_SLAVE = "slave";
}
3、添加注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 标记注解可使用在方法与类上
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {// 默认值为MASTERString value() default DataSourceConstants.DS_KEY_MASTER;
}
4、动态数据源的切换定义
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** 动态数据源**/
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getContextKey();}
}
5、动态切面实现类
import cn.com.yixiukeji.app.ServiceContext;
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.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;/*** 切面*/
@Aspect
@Component
//@Order(-10)
@ConditionalOnProperty(prefix = "yixiu.cube.center.multi-datasource",value = {"enable"},havingValue = "true"
)
public class DynamicDataSourceAspect {// 以在类上使用了@Service作为切入点@Pointcut("@within(org.springframework.stereotype.Service)")public void dataSourcePointCut() {}@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Class<?> aClass = Class.forName(signature.getDeclaringType().getName());// 方法优先,如果方法上存在注解,则优先使用方法上的注解if (signature.getMethod().isAnnotationPresent(DS.class)) { DynamicDataSourceContextHolder.setContextKey(signature.getMethod().getAnnotation(DS.class).value());// 其次类优先,如果类上存在注解,则使用类上的注解}else if (aClass.isAnnotationPresent(DS.class)) { DynamicDataSourceContextHolder.setContextKey(aClass.getAnnotation(DS.class).value());// 如果都不存在,则使用默认} else {//默认选择源DynamicDataSourceContextHolder.setContextKey("slave");}System.out.println(ServiceContext.getContext().get("yes.req.instanceId"));try {return joinPoint.proceed();} finally {DynamicDataSourceContextHolder.removeContextKey();}}
}
6、动态数据源配置
/*** 动态数据源配置**/
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
@Configuration
@ConditionalOnProperty(prefix = "yixiu.cube.center.multi-datasource",value = {"enable"},havingValue = "true"
)
@MapperScan(basePackages = "cn.com.dao.mapper")
public class DynamicDataSourceConfig {@Bean(DataSourceConstants.DS_KEY_MASTER)// 需要与配置文件中对应@ConfigurationProperties(prefix = "yixiu.cube.center.multi-datasource.datasource.master")public DruidDataSource masterDataSource() {return DruidDataSourceBuilder.create().build();}@Bean(DataSourceConstants.DS_KEY_SLAVE)@ConfigurationProperties(prefix = "yixiu.cube.center.multi-datasource.datasource.slave")public DruidDataSource slaveDataSource() {return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "yixiu.cube.center.multi-datasource")@ConditionalOnMissingBeanpublic MultiDataSourceProperties multiDatasourceProperties() {return new MultiDataSourceProperties();}@Bean@ConditionalOnMissingBeanpublic DataSource dataSource(MultiDataSourceProperties multiDatasourceProperties) {DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();multiDatasourceProperties.getDatasource().forEach((name, dataSourceVo) -> {DataSource data = DataSourceBuilder.create().url(dataSourceVo.getUrl()).username(dataSourceVo.getUsername()).password(dataSourceVo.getPassword()).driverClassName(dataSourceVo.getDriverClassName()).build();dataSource.addDataSource(name,data);});return dataSource;}@Bean@Primarypublic DynamicDataSource dynamicDataSource(MultiDataSourceProperties multiDataSourceProperties) {Map<Object, Object> dataSourceMap = new ConcurrentHashMap<>(multiDataSourceProperties.getDatasource().size());DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();multiDataSourceProperties.getDatasource().forEach((name, dataSourceVo) -> {DataSource data = DataSourceBuilder.create().url(dataSourceVo.getUrl()).username(dataSourceVo.getUsername()).password(dataSourceVo.getPassword()).driverClassName(dataSourceVo.getDriverClassName()).build();dataSource.addDataSource(name,data);dataSourceMap.put(name,data);});
//这种配置是定死的,不好玩
// dataSourceMap.put(DataSourceConstants.DS_KEY_MASTER, masterDataSource());
// dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE, slaveDataSource());//设置动态数据源DynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(dataSourceMap);//dynamicDataSource.setDefaultTargetDataSource(masterDataSource());//dynamicDataSource.setDefaultTargetDataSource(multiDataSourceProperties.getDatasource().get("master"));return dynamicDataSource;}
}
7、动态数据源名称上下文处理
/*** 动态数据源名称上下文处理**/
public class DynamicDataSourceContextHolder {/*** 动态数据源名称上下文*/private static final ThreadLocal<String> DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();/*** 设置数据源* @param key*/public static void setContextKey(String key){DATASOURCE_CONTEXT_KEY_HOLDER.set(key);}/*** 获取数据源名称* @return*/public static String getContextKey(){String key = DATASOURCE_CONTEXT_KEY_HOLDER.get();return key == null?DataSourceConstants.DS_KEY_MASTER:key;}/*** 删除当前数据源名称*/public static void removeContextKey(){DATASOURCE_CONTEXT_KEY_HOLDER.remove();}
}
8、动态配置的映射,可以自定义
import java.util.Map;
public class MultiDataSourceProperties {private String primary = "master";private Map<String, MultiDataSourceVo> datasource;public MultiDataSourceProperties() {}public String getPrimary() {return this.primary;}public void setPrimary(String primary) {this.primary = primary;}public Map<String, MultiDataSourceVo> getDatasource() {return this.datasource;}public void setDatasource(Map<String, MultiDataSourceVo> datasource) {this.datasource = datasource;}public MultiDataSourceVo getPrimaryMultiDataSourceVo() {return this.datasource != null && !this.datasource.isEmpty() ? (MultiDataSourceVo)this.datasource.get(this.primary) : null;}
}
9、动态属性的实体类
import lombok.Data;@Data
public class MultiDataSourceVo {private String driverClassName = "com.mysql.cj.jdbc.Driver";private String url;private String username;private String password;
}
10、yaml文件的配置
yixiu.cube.center.multi-datasource:enable: true #开启多数据源配置,关闭单个数据源配置primary: master #设置默认的数据源或者数据源组,默认值即为masterdatasource:master:driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始⽀持SPI可省略此配url: jdbc:mysql://aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8username: xkcpassword: 123456slave:url: jdbc:mysql://192.168.2.2:30306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8username: xkcpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave1:url: jdbc:mysql://192.168.2.2:30306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8username: xkcpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
11、编写接口添加数据就可以在目标数据库查看了,这个简单就不说明了。其他的配置,可以查看官方说明或者博客资料。
12、参考项目:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter