1.配置多数据源
根据实际需求配置多个数据源,给副数据源进行别名区分,并统一设置命名方便后面进行获取
# 主数据源(默认数据源)
spring.datasource.url = jdbc:oracle:thin:@10.51.12.88:1521:test
spring.datasource.username=test
spring.datasource.password=123456
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# 副数据源
slave.datasource.one.url = jdbc:oracle:thin:@10.60.13.99:1521:test
slave.datasource.one.username=test
slave.datasource.one.password=123456
slave.datasource.one.driver-class-name=oracle.jdbc.driver.OracleDriver
# 数据源区别名
slave.datasource.names =one(例如:one,two,three)
2.动态数据源上下文管理
创建一个动态数据源上下文管理类,用于线程存储数据源信息
import java.util.ArrayList;
import java.util.List;
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<String>();
public static List<String> dataSourceIds = new ArrayList<String>();
public static void setDataSourceType(String dataSourceType) {
CONTEXT_HOLDER.set(dataSourceType);
}
public static String getDataSourceType() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
public static boolean isContainsDataSource(String dataSourceId) {
return dataSourceIds.contains(dataSourceId);
}
}
3.动态数据源获取类
没执行一次数据库都进行动态获取数据源信息
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
4.多数据源源注册器
初始化对配置文件进行解析多数据源信息
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final String DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
private DataSource defaultDataSource;
private final Map<String, DataSource> slaveDataSources = new HashMap<>();
@Override
public void setEnvironment(Environment environment) {
initDefaultDataSource(environment);
initSlaveDataSources(environment);
}
private void initDefaultDataSource(Environment env) {
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("driver-class-name", env.getProperty("spring.datasource.driver-class-name"));
dsMap.put("url", env.getProperty("spring.datasource.url"));
dsMap.put("username", env.getProperty("spring.datasource.username"));
dsMap.put("password", env.getProperty("spring.datasource.password"));
defaultDataSource = buildDataSource(dsMap);
System.err.println("配置默认数据源成功");
}
private void initSlaveDataSources(Environment env) {
String dsPrefixs = env.getProperty("slave.datasource.names");
for (String dsPrefix : dsPrefixs.split(",")) {
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("driver-class-name", env.getProperty("slave.datasource." + dsPrefix + ".driver-class-name"));
dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url"));
dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username"));
dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password"));
DataSource ds = buildDataSource(dsMap);
System.err.println("配置"+dsPrefix+"数据源成功");
slaveDataSources.put(dsPrefix, ds);
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
targetDataSources.put("dataSource", this.defaultDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
targetDataSources.putAll(slaveDataSources);
DynamicDataSourceContextHolder.dataSourceIds.addAll(slaveDataSources.keySet());
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
}
public DataSource buildDataSource(Map<String, Object> dataSourceMap) {
try {
Object type = dataSourceMap.get("type");
if (type == null) {
type = DATASOURCE_TYPE_DEFAULT;
}
Class<? extends DataSource> dataSourceType;
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
String driverClassName = dataSourceMap.get("driver-class-name").toString();
String url = dataSourceMap.get("url").toString();
String username = dataSourceMap.get("username").toString();
String password = dataSourceMap.get("password").toString();
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
.username(username).password(password).type(dataSourceType);
return factory.build();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
配置完后必须在启动类上加上注解@Import(DynamicDataSourceRegister.class),才能在启动应用后自动进行数据源注册
import com.cowin.mes.config.DynamicDataSourceRegister;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@SpringBootApplication
@MapperScan("com.xxx.xxx.xxx")
@Import(DynamicDataSourceRegister.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(CowinApplication.class, args);
}
}
5.自定义注解
首先定义一个注解可以用在方法或者接口上面用以标明使用哪一个数据源进行操作。
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String name();
}
6.定义切面AOP处理注解
对注解进行解析,判断使用哪一个数据源,并进行切换。
import com.cowin.mes.annotation.TargetDataSource;
import com.cowin.mes.config.DynamicDataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Order(-1)
@Component("dynamicDataSourceAspect")
public class DynamicDataSourceAspect {
@Pointcut("@annotation(com.cowin.mes.annotation.TargetDataSource)")
public void webLog(){}
@Before("webLog()")
public void changeDataSource(JoinPoint joinPoint) {
TargetDataSource targetDataSource = getAnnotationDataSource(joinPoint);
String dbid = targetDataSource.name();
if (!DynamicDataSourceContextHolder.isContainsDataSource(dbid)) {
} else {
DynamicDataSourceContextHolder.setDataSourceType(dbid);
}
}
@After("webLog()")
public void clearDataSource() {
DynamicDataSourceContextHolder.clearDataSourceType();
}
private TargetDataSource getAnnotationDataSource(JoinPoint joinPoint){
TargetDataSource targetDataSource = null;
try {
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
if(null == targetDataSource) {
Method method = methodSignature.getMethod();
if(method != null) {
targetDataSource = method.getAnnotation(TargetDataSource.class);
if(null == targetDataSource) {
targetDataSource = method.getDeclaringClass().getAnnotation(TargetDataSource.class);
}
}
}
return targetDataSource;
}
catch(Exception e) {
e.printStackTrace();
}
return null;
}
}
7.使用
在接口或者方法上添加注解@@TargetDataSource(name = "one"),name为配置文件中自己配的数据库别名,添加完后,调用接口则会使用该数据库进行操作.
@TargetDataSource(name = "one")
@GetMapping(value = "/listSupplier")
public String listSupplier(Supplier query) {
List<Supplier> list = supplierManager.listSupplier(query);
return Result.ok(list);
}