1. 首先定义个多数据注解
import java.lang.annotation.*;
/**
* @author Wang
* 多数据源注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DynamicDataSource {
String dataSource() default "";
}
2. 多数据源,切面处理类
import com.autoai.boss.core.datasources.annotation.DynamicDataSource;
import com.autoai.boss.core.datasources.config.DynamicContextHolder;
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;
import java.lang.reflect.Method;
/**
* @author Wang
* 多数据源,切面处理类
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.autoai.boss.core.datasources.annotation.DynamicDataSource)")
public void dataSourcePointCut() {
//获取切点
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Class targetClass = point.getTarget().getClass();
Method method = signature.getMethod();
DynamicDataSource targetDataSource = (DynamicDataSource) targetClass.getAnnotation(DynamicDataSource.class);
DynamicDataSource methodDataSource = method.getAnnotation(DynamicDataSource.class);
if (targetDataSource != null || methodDataSource != null) {
String value;
if (methodDataSource != null) {
value = methodDataSource.dataSource();
} else {
value = targetDataSource.dataSource();
}
DynamicContextHolder.push(value);
logger.debug("set datasource is {}", value);
}
try {
return point.proceed();
} finally {
DynamicContextHolder.poll();
logger.debug("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
3. 多数据源上下文
import java.util.ArrayDeque;
import java.util.Deque;
/**
* 多数据源上下文
*
* @author Wang
*/
public class DynamicContextHolder {
private DynamicContextHolder() { }
private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = ThreadLocal.withInitial(ArrayDeque::new);
/**
* 获得当前线程数据源
*
* @return 数据源名称
*/
public static String peek() {
return CONTEXT_HOLDER.get().peek();
}
/**
* 设置当前线程数据源
*
* @param dataSource 数据源名称
*/
public static void push(String dataSource) {
CONTEXT_HOLDER.get().push(dataSource);
}
/**
* 清空当前线程数据源
*/
public static void poll() {
Deque<String> deque = CONTEXT_HOLDER.get();
deque.poll();
if (deque.isEmpty()) {
CONTEXT_HOLDER.remove();
}
}
}
4. 多数据源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 多数据源
*
* @author Wang
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicContextHolder.peek();
}
}
5. 配置多数据源
import com.alibaba.druid.pool.DruidDataSource;
import com.autoai.boss.core.datasources.properties.DataSourceProperties;
import com.autoai.boss.core.datasources.properties.DynamicDataSourceProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 配置多数据源
*
* @author Wang
*/
@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceConfig {
private DynamicDataSourceProperties properties;
@Autowired
public DynamicDataSourceConfig(DynamicDataSourceProperties properties) {
this.properties = properties;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid.default")
public DataSourceProperties deFaultDataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DynamicDataSource dynamicDataSource(DataSourceProperties deFaultDataSourceProperties) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(getDynamicDataSource());
//默认数据源
DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(deFaultDataSourceProperties);
dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
return dynamicDataSource;
}
private Map<Object, Object> getDynamicDataSource(){
Map<String, DataSourceProperties> dataSourcePropertiesMap = properties.getDatasource();
Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size());
dataSourcePropertiesMap.forEach((k, v) -> {
DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v);
targetDataSources.put(k, druidDataSource);
});
return targetDataSources;
}
}
6. DruidDataSource
import com.alibaba.druid.pool.DruidDataSource;
import com.autoai.boss.core.datasources.properties.DataSourceProperties;
import lombok.SneakyThrows;
/**
* DruidDataSource
*
* @author Wang
*/
public class DynamicDataSourceFactory {
private DynamicDataSourceFactory() { }
@SneakyThrows
public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(properties.getDriverClassName());
druidDataSource.setUrl(properties.getUrl());
druidDataSource.setUsername(properties.getUsername());
druidDataSource.setPassword(properties.getPassword());
druidDataSource.setDbType(properties.getDbType());
druidDataSource.setInitialSize(properties.getInitialSize());
druidDataSource.setMaxActive(properties.getMaxActive());
druidDataSource.setMinIdle(properties.getMinIdle());
druidDataSource.setMaxWait(properties.getMaxWait());
druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
druidDataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis());
druidDataSource.setValidationQuery(properties.getValidationQuery());
druidDataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout());
druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
druidDataSource.setTestOnReturn(properties.isTestOnReturn());
druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
druidDataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements());
druidDataSource.setSharePreparedStatements(properties.isSharePreparedStatements());
druidDataSource.setFilters(properties.getFilters());
druidDataSource.init();
return druidDataSource;
}
}
7. 多数据源属性
import lombok.Data;
/**
* 多数据源属性
*
* @author Wang
*/
@Data
public class DataSourceProperties {
private String driverClassName;
private String url;
private String username;
private String password;
private String dbType;
/**
* Druid默认参数
*/
private int initialSize = 2;
private int maxActive = 10;
private int minIdle = -1;
private long maxWait = 60 * 1000L;
private long timeBetweenEvictionRunsMillis = 60 * 1000L;
private long minEvictableIdleTimeMillis = 1000L * 60L * 30L;
private long maxEvictableIdleTimeMillis = 1000L * 60L * 60L * 7;
private String validationQuery = "select 1";
private int validationQueryTimeout = -1;
private boolean testOnBorrow = false;
private boolean testOnReturn = false;
private boolean testWhileIdle = true;
private boolean poolPreparedStatements = false;
private int maxOpenPreparedStatements = -1;
private boolean sharePreparedStatements = false;
private String filters = "stat,wall";
}
8. 多数据源属性
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 多数据源属性
*
* @author Wang
*
*/
@ConfigurationProperties(prefix = "spring.datasource.druid.dynamic")
public class DynamicDataSourceProperties {
private Map<String, DataSourceProperties> datasource = new LinkedHashMap<>();
public Map<String, DataSourceProperties> getDatasource() {
return datasource;
}
public void setDatasource(Map<String, DataSourceProperties> datasource) {
this.datasource = datasource;
}
}
9. 多数据源名,增加数据源,在此配置
/**
* @author Wang
* 增加多数据源,在此配置
*/
public interface DataSourceNames {
String MASTER = "master";
String SLAVE = "slave";
String CLICKHOUSE = "clickhouse";
}
10. 配置文件
spring:
datasource:
druid:
filter:
wall:
enabled: true
db-type: mysql
config:
multi-statement-allow: true
stat:
enabled: true
db-type: mysql
log-slow-sql: true
merge-sql: false
slow-sql-millis: 1000
initial-size: 5
default:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3309/test01?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
username: test01
password: test01
db-type: mysql
dynamic:
datasource:
slave1:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3309/test02?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
username: test02
password: test02
db-type: mysql
slave2:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3309/test03?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
username: test03
password: test03
db-type: mysql
clickhouse:
driver-class-name: ru.yandex.clickhouse.ClickHouseDriver
initialSize: 10
maxActive: 100
maxWait: 6000
minIdle: 10
password: ''
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:clickhouse://172.19.1.202:8123/default
username: default
db-type: mysql #clickhouse需要配置成mysql
max-active: 100
max-pool-prepared-statement-per-connection-size: 20
max-wait: 60000
min-evictable-idle-time-millis: 300000
min-idle: 10
pool-prepared-statements: true
stat-view-servlet:
enabled: true
login-password: username
login-username: password
url-pattern: /druid/*
test-on-borrow: false
test-on-return: false
test-while-idle: true
time-between-eviction-runs-millis: 60000
validation-query: SELECT 1
11. 使用
import com.autoai.boss.core.datasources.DataSourceNames;
import com.autoai.boss.core.datasources.annotation.DynamicDataSource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Wang
*/
@RestController
@ResponseBody
public class HelloController {
@GetMapping(name = "/hello")
//在此指定需要使用的数据源类型
@DynamicDataSource(dataSource = DataSourceNames.SLAVE)
public String sayHello(String name) {
return "aaa";
}
}
上述代码参考https://www.renren.io