应用场景:在一个项目需要用到两个或两个以上的数据库时,要进行切换数据库,来操作相应的表。
框架:用的是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 上加上这个就能切换了,没有加,默认是第一个的,
看了网上很多方法 比如用做两个事务管理器来指定不同的数据源,这样在切换事务管理器时就切换了数据源,但是我的配置只是连上了,却无法切换 一直用的还是第一个数据源。