一、数据源切换及选择处理工具类引入
<dependency>
<groupId>aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.5.3version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.6.11version>
dependency>
package com.myfund.saveplan.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//该类型的注解会被保留到那个阶段
@Retention(RetentionPolicy.RUNTIME)
//注解执行的目标地址 TYPE 类/接口 METHOD 方法 PARAMETER 参数
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
package com.myfund.saveplan.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
public class DataSourceAspect {
/**
* 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
*/
public void intercept(JoinPoint point) {
Class<?> target = point.getTarget().getClass();
MethodSignature signature = (MethodSignature) point.getSignature();
// 默认使用目标类型的注解,如果没有则使用其实现接口的注解
for (Class<?> clazz : target.getInterfaces()) {
resolveDataSource(clazz, signature.getMethod());
}
resolveDataSource(target, signature.getMethod());
}
/**
* 提取目标对象方法注解和类型注解中的数据源标识
*
* @author: haocheng
* @date: 2019-05-21 11:43
*/
private void resolveDataSource(Class<?> clazz, Method method) {
try {
Class<?>[] types = method.getParameterTypes();
//默认使用类型注解
if (clazz.isAnnotationPresent(DataSource.class)) {
DataSource ds = clazz.getAnnotation(DataSource.class);
HandleDataSource.setDataSource(ds.value());
}
// 方法注解可以覆盖类型注解
Method m = clazz.getMethod(method.getName(), types);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource source = m.getAnnotation(DataSource.class);
HandleDataSource.setDataSource(source.value());
}
} catch (Exception e) {
System.out.println(clazz + ":" + e.getMessage());
}
}
}
同时在springmv-mybatis配置文件中配置切面:
<bean id="dataSourceAspect" class="com.myfund.saveplan.config.DataSourceAspect"/>
<aop:config>
<aop:pointcut id="dataSourcePointcut" expression="execution(* com.myfund.saveplan.service..*.*(..))"/>
<aop:aspect ref="dataSourceAspect">
<aop:before pointcut-ref="dataSourcePointcut" method="intercept"/>
aop:aspect>
aop:config>
创建数据源处理类
线程共享工具,将数据源信息保存在ThreadLocal中实现共享,保证数据源信息在同一线程下切换后不被其他线程修改
public class HandleDataSource {
public static final ThreadLocal<String> holder = new ThreadLocal<String>();
/**
* 绑定当前线程数据源路由
*
* @param datasource
*/
public static void setDataSource(String datasource) {
holder.set(datasource);
}
/**
* 获取当前线程的数据源路由
*
* @return
*/
public static String getDataSource() {
return holder.get();
}
}
创建数据源选择类
spring为我们提供了AbstractRoutingDataSource,即带路由的数据源。
继承后我们需要实现它的determineCurrentLookupKey(),该方法用于自定义实际数据源名称的路由选择方法
由于我们将信息保存到了ThreadLocal中,所以只需要从中拿出来即可。
package com.myfund.saveplan.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class ChooseDataSource extends AbstractRoutingDataSource {
/**
* 获取与数据源相关的key
* 此key是Map resolvedDataSources 中与数据源绑定的key值
* 在通过determineTargetDataSource获取目标数据源时使用
*/
@Override
protected Object determineCurrentLookupKey() {
// 从共享线程中获取数据源名称
return HandleDataSource.getDataSource();
}
}
通过以上四个类的创建,实现了整个数据源配置和切换过程的底层处理配置,接下来就是框架中的一些基本配置。
二、项目开发所需数据源配置
开发使用的是SSM框架,所以在jdbc.properties配置文件中配置相关数据库连接信息
#sqlserver
sqlserver.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
sqlserver.url=jdbc:sqlserver://10.20.34.122;DatabaseName=PrimaryData_New
sqlserver.username=
sqlserver.password=
#oracle
oracle.driverClassName=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@10.20.30.9:1521:racdb1
oracle.username=
oracle.password=
#初始化连接大小
jdbc.initialSize=10
#连接池最小空闲
jdbc.minIdle=1
#连接池最大数量
jdbc.maxActive=20
#获取连接最大等待时间
jdbc.maxWait=60000
#验证语句
jdbc.validationQuery=SELECT 1
三、修改spring-mybatis配置文件,对多个数据源进行配置(关键配置)
主要分为两方面:
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${sqlserver.driverClassName}"/>
<property name="url" value="${sqlserver.url}"/>
<property name="username" value="${sqlserver.username}"/>
<property name="password" value="${sqlserver.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
bean>
<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${oracle.driverClassName}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
bean>
<bean id="multipleDataSource" class="com.myfund.saveplan.config.ChooseDataSource">
<property name="defaultTargetDataSource" ref="dataSource1"/>
<property name="targetDataSources">
<map>
<entry key="dataSource1" value-ref="dataSource1"/>
<entry key="dataSource2" value-ref="dataSource2"/>
map>
property>
bean>
同时重新配置事务管理
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="multipleDataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
注: 在spring-mybatis中进行数据源基础信息和数据源路由配置时,当增加新的数据源时,都需要为该数据源配置一个新的bean,同时在数据源路由中注入该bean
四、使用多数据源
在service层接口,通过数据源注解为每一个方法执行的数据源进行具体的配置,数据源注解中的值一定要和相应数据源配置文件中的bean的id值对应
package com.myfund.saveplan.service;
import com.myfund.saveplan.config.DataSource;
import java.util.List;
public interface RankService {
List getRankInfo();
@DataSource(value = "dataSource2")
List getFeerate();
List getYearProfit(String fundcode);
List getMonthLast1DayUnitEquity(String fundcode);
}