2.多数据源
2.1 多数据源
DataSourceConfig 类
多的不说,上代码,上一篇有关于appliaction.yml pom.xml mybatis.xml 等配置文件
地址:.https://blog.csdn.net/qq_41129021/article/details/83538706
import org.apache.log4j.Logger;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 多数据源配置类 Created by pure on 2018-10-29.
*
* @author 文浩
*
*/
@Configuration
public class DataSourceConfig {
private static final Logger log = Logger.getLogger(DataSourceContextHolder.class);
// 数据源1
@Bean(name = "ds1")
@ConfigurationProperties(prefix = "datasource.ds1") // application.properteis中对应属性的前缀
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
// 数据源2
@Bean(name = "ds2")
@ConfigurationProperties(prefix = "datasource.ds2") // application.properteis中对应属性的前缀
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
*
* @return
*/
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSource1());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap<Object, Object>();
dsMap.put("localhost", dataSource1());
dsMap.put("mstsc", dataSource2());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
/**
* 配置@Transactional注解事物
*
* @return
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
这里只配置了两个数据源。可根据实际需求配置更多数据源,只需在yml添加数据库连接属性.
附: driverClassName、url、userName、password 属性名需对应.否则无法正常注入。会出现Null值情况.
2.2 动态数据源
DataSourceContextHodedr
使用动态数据源的初衷,是能在应用层做到读写分离,即在程序代码中控制不同的查询方法去连接不同的库。除了这种方法以外,数据库中间件也是个不错的选择,它的优点是数据库集群对应用来说只暴露为单库,不需要切换数据源的代码逻辑。
我们通过自定义注解 + AOP的方式实现数据源动态切换。
首先定义一个ContextHolder, 用于保存当前线程使用的数据源名:
import org.apache.log4j.Logger;
/**
* Created by pure on 2018-10-29.
*
* @author 文浩
*
*/
public class DataSourceContextHolder {
/**
* 默认数据源
*/
public static final String DEFAULT_JDBC = "localhost";
private static final Logger log = Logger.getLogger(DataSourceContextHolder.class);
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
log.info("切换到{" + dbType + "}数据源");
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
log.info("clearDataSource : " + contextHolder.get());
contextHolder.remove();
}
}
然后自定义一个javax.sql.DataSource
接口的实现,这里只需要继承Spring为我们预先实现好的父类AbstractRoutingDataSource
即可:
import org.jboss.logging.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by pure on 2018-10-29.
*
* @author 文浩
*
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger log = Logger.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
log.info("DynamicDataSource :" + DataSourceContextHolder.getDB());
return DataSourceContextHolder.getDB();
}
}
自定义注释@JDBC
用于在编码时指定方法使用哪个数据源:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface JDBC {
String value() default "datasource1";
}
编写AOP切面,实现切换逻辑:
import org.apache.log4j.Logger;
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.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* 自定义注解 + AOP的方式实现数据源动态切换。 Created by pure on 2018-10-29.
*
* @author 文浩
*
*/
@Aspect
@Component
public class DynamicDataSourceAspect {
private static final Logger log = Logger.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(JDBC)")
public void beforeSwitchDS(JoinPoint point) {
// 获得当前访问的class
Class<?> className = point.getTarget().getClass();
// 获得访问的方法名
String methodName = point.getSignature().getName();
// 得到方法的参数的类型
Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_JDBC;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
// 判断是否存在@DS注解
if (method.isAnnotationPresent(JDBC.class)) {
JDBC annotation = method.getAnnotation(JDBC.class);
// 取出注解中的数据源名
dataSource = annotation.value();
log.info("取出数据源名:" + dataSource);
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
}
@After("@annotation(DS)")
public void afterSwitchDS(JoinPoint point) {
log.info("clearDataSource :" + DataSourceContextHolder.getDB());
DataSourceContextHolder.clearDB();
}
}
最后:需要关闭spring boot的自动注入。
Application类上添加此注解 @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class }) // 关闭dataSource自动注入
完成之后在service中添加注解指定数据库名就可以啦。
例:
搞定,愉快的切换数据库源吧. 此方法经测试在切换数据源的时候耗时大概有个十来秒左右,目前还不知优化方法,也不知道为什么需要那么久,有知道的小伙伴可以共同交流.