应用场景:在一个项目需要用到两个或两个以上的数据库时,要进行切换数据库,来操作相应的表。
框架:用的是spring 的org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 这个类 实现determineCurrentLookupKey() 方法来切换数据源
1. 配置文件,配置多个数据源。
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${datasource.driverClassName}" />
<property name="url" value="${datasource.url}" />
<property name="username" value="${datasource.username}" />
<property name="password" value="${datasource.password}" />
<property name="filters" value="stat" />
</bean>
<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${datasource2.driverClassName}" />
<property name="url" value="${datasource2.url}" />
<property name="username" value="${datasource2.username}" />
<property name="password" value="${datasource2.password}" />
<property name="maxWait" value="${datasource2.maxWait}"/>
<property name="filters" value="stat" />
</bean>
<bean id="multipleDataSource" class="com.ds.basic.util.MultipleDataSource">
<property name="defaultTargetDataSource" ref="dataSource1"/>
<property name="targetDataSources">
<map>
<entry key="dataSource1" value-ref="dataSource1"/>
<entry key="dataSource2" value-ref="dataSource2"/>
</map>
</property>
</bean>
多了个multipleDataSource 来把多个数据源组合在一起,其他配置记得ref="multipleDataSource" 这个切换到这个来。
还有事务管理器,
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="2"/>
order = '2',增加个这个,下面会用到。
添加aop 依赖包,
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.ds.basic.aop"></context:component-scan>
<!-- 自动为切面方法中匹配的方法所在的类生成代理对象。 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
截止,配置文件完成。
com.ds.basic.util.MultipleDataSource ,切换数据源核心,就是要实现determineCurrentLookupKey ,来切换数据源
public class MultipleDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>(); public static void setDataSourceKey(String dataSource) {
dataSourceKey.set(dataSource);
} @Override
protected Object determineCurrentLookupKey() {
return dataSourceKey.get();
}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
/*
自定义个注解来做个标识,好切换数据源
*/ String value(); }
/**
* order(1) 是为了要在事务之前就切换数据源,不然应该会有问题,大家可以试一试
*/
@Order(1)
@Component
@Aspect
public class DataSourceAspect {
/**
* 拦截,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
*
* @param point
* @throws Exception
* @annotation使用这个表达式来切,却一直没起作用,有大佬知道请评论告知。
*/
// @Before("@annotation(com.ds.basic.util.DataSource)")
@Before(value="execution(* com.shop.*.*.service.*.*(..))")
public void intercept(JoinPoint point) throws Exception {
// 下面的注释是在生产环境下切到第二个数据源时总是不能切回来,加个这个就好了,很奇怪,其实没必要添加这个。
// MultipleDataSource.setDataSourceKey("dataSource1");
Class<?> target = point.getTarget().getClass();
MethodSignature signature = (MethodSignature) point.getSignature();
// 默认使用目标类型的注解,如果没有则使用其实现接口的注解
for (Class<?> clazz : target.getInterfaces()) {
resolveDataSource(clazz, signature.getMethod());
}
resolveDataSource(target, signature.getMethod());
}
/**
* 提取目标对象方法注解和类型注解中的数据源标识
* 这个方法是在一个博客上复制下来的,原来的网址不记得在哪里了
* @param clazz
* @param method
*/
private void resolveDataSource(Class<?> clazz, Method method) {
try {
Class<?>[] types = method.getParameterTypes();
// 默认使用类型注解
if (clazz.isAnnotationPresent(DataSource.class)) {
DataSource source = clazz.getAnnotation(DataSource.class);
MultipleDataSource.setDataSourceKey(source.value());
}
// 方法注解可以覆盖类型注解
Method m = clazz.getMethod(method.getName(), types);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource source = m.getAnnotation(DataSource.class);
MultipleDataSource.setDataSourceKey(source.value());
}
} catch (Exception e) {
// 可以不做这个异常捕捉。
MultipleDataSource.setDataSourceKey("dataSource1");
}
} }
配置完成,大家可以试下,欢迎评论。
使用 @DataSource("dataSource2") 在service 上加上这个就能切换了,没有加,默认是第一个的,
看了网上很多方法 比如用做两个事务管理器来指定不同的数据源,这样在切换事务管理器时就切换了数据源,但是我的配置只是连上了,却无法切换 一直用的还是第一个数据源。