最近接触了一个项目需要夸库取数据,起初使用spring的jdbctemplate去查询数据,后来觉得这种方式太low,于是在空闲期间就摸索尝试一下动态配置切换数据源,在此记录一下便于以后学习使用:
版权声明:本文为博主原创文章,未经博主允许不得转载。
spring已经为我们封装了AbstractRoutingDataSource接口便于切换数据源,我们只要继承此类用于获取数据源
首先需要在spring配置文件中配置多数据源加入以下配置
<!-- 数据源配置, 使用Tomcat JDBC连接池 -->
<bean id="dataSource1" class="org.apache.tomcat.jdbc.pool.DataSource"
destroy-method="close">
<!-- Connection Info -->
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password">
<bean factory-method="decryptPassword"
class="com.qware.platform.framework.utils.ConfigUtil">
<constructor-arg value="${jdbc.pword}" />
</bean>
</property>
<property name="validationQuery" value="SELECT 1" />
<!-- Connection Pooling Info -->
<property name="maxActive" value="${jdbc.pool.maxActive}" />
<property name="maxIdle" value="${jdbc.pool.maxIdle}" />
<property name="minIdle" value="0" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="dataSource2" class="org.apache.tomcat.jdbc.pool.DataSource"
destroy-method="close">
<!-- Connection Info -->
<property name="driverClassName" value="${jdbc2.driver}" />
<property name="url" value="${jdbc2.url}" />
<property name="username" value="${jdbc2.username}" />
<property name="password">
<bean factory-method="decryptPassword"
class="com.qware.platform.framework.utils.ConfigUtil">
<constructor-arg value="${jdbc2.pword}" />
</bean>
</property>
<property name="validationQuery" value="SELECT 1" />
<!-- Connection Pooling Info -->
<property name="maxActive" value="${jdbc2.pool.maxActive}" />
<property name="maxIdle" value="${jdbc2.pool.maxIdle}" />
<property name="minIdle" value="0" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="dataSource" class="cn.com.citycloud.delivery.bdp.datasource.config.RoutingDataSource">
<!-- 为targetDataSources注入两个数据源 -->
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="ds1" value-ref="dataSource1"/>
<entry key="ds2" value-ref="dataSource2"/>
</map>
</property>
<!-- 为指定数据源RoutingDataSource注入默认的数据源-->
<property name="defaultTargetDataSource" ref="dataSource1"/>
1. public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSourceType();
}
}
2.再来一个DataSourceHolder 用本地线程用于设置数据源
public class DataSourceHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
private static Logger logger = LoggerFactory.getLogger(DataSourceHolder.class);
/**
* @Description: 设置数据源类型
* @param dataSourceType 数据库类型
* @return void
* @throws
*/
public static void setDataSourceType(String dataSourceType) {
logger.debug("当前切换数据源为"+dataSourceType);
contextHolder.set(dataSourceType);
}
/**
* @Description: 获取数据源类型
* @param
* @return String
* @throws
*/
public static String getDataSourceType() {
return contextHolder.get();
}
/**
* @Description: 清除数据源类型
* @param
* @return void
* @throws
*/
public static void clearDataSourceType() {
contextHolder.remove();
}
}
3.利用切面Aspect 切点拦截使用到注解的方法
@Order(1)
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@within(cn.com.citycloud.delivery.bdp.datasource.config.DataSource)||@annotation(cn.com.citycloud.delivery.bdp.datasource.config.DataSource)")
private void anyMethod() {
}
/**
* 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
*
* @param point
* @throws Exception
*/
@Before(value="anyMethod()")
public void intercept(JoinPoint point) throws Exception {
String dataSource = DataSources.ds1;
try {
// 获得当前访问的class
Class<?> curClass = point.getTarget().getClass();
// 获得访问的方法名
String methodName = point.getSignature().getName();
// 得到方法的参数的类型
Class<?>[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
// 得到访问的方法对象
Method method = curClass.getMethod(methodName, argClass);
// 判断方法是否存在@DataSource注解
if (method.isAnnotationPresent(DataSource.class)) {
DataSource methodAnnotation = method.getAnnotation(DataSource.class);
// 取出注解中的数据源名
dataSource = methodAnnotation.value();
}
else {
// 判断是否存在@DataSource注解
if (curClass.isAnnotationPresent(DataSource.class)) {
DataSource classAnnotation = curClass.getAnnotation(DataSource.class);
// 取出注解中的数据源名
dataSource = classAnnotation.value();
}
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceHolder.setDataSourceType(dataSource);
}
@AfterReturning(value = "anyMethod()", returning = "result")
public void afterReturning() {
//DataSourceHolder.clearDataSourceType();
DataSourceHolder.setDataSourceType(DataSources.ds1);
}
}
4.写一个注解用于动态切换
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface DataSource {
String value() default "ds1";
}
5.在使用时在service实现类里面的方法上