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 {
// 默认值为MASTER
String value() default DataSourceConstants.DS_KEY_MASTER;
}
4、动态数据源的切换定义
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源
**/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected 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"
)
@ConditionalOnMissingBean
public MultiDataSourceProperties multiDatasourceProperties() {
return new MultiDataSourceProperties();
}
@Bean
@ConditionalOnMissingBean
public 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
@Primary
public 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 #设置默认的数据源或者数据源组,默认值即为master
datasource:
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%2b8
username: xkc
password: 123456
slave:
url: jdbc:mysql://192.168.2.2:30306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8
username: xkc
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave1:
url: jdbc:mysql://192.168.2.2:30306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2b8
username: xkc
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
11、编写接口添加数据就可以在目标数据库查看了,这个简单就不说明了。其他的配置,可以查看官方说明或者博客资料。
12、参考项目:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter