接下来的用法相比大家也应该猜到了。
就是在每次调用数据库之前我们都要先通过
DataSourceHolder
来设置当前的数据源。看下demo:
@Test public void selectByPrimaryKey() throws Exception { DataSourceHolder.setDataSources(Constants.DATASOURCE_TWO); Datasource datasource = dataSourceService.selectByPrimaryKey(7); System.out.println(JSON.toJSONString(datasource)); }
详见我的单测。
使用起来也是非常简单。但是不知道大家注意到没有,这样的做法槽点很多:
- 每次使用需要手动切换,总有一些人会忘记写(比如我)。
- 如果是后期需求变了,查询其他的表了还得一个个改回来。
那有没有什么方法可以自动的帮我们切换呢?
肯定是有的,大家应该也想得到。就是利用Spring
的AOP
了。
自动切换数据源
首先要定义好我们的切面类DataSourceExchange
:
package com.crossoverJie.util; import org.aspectj.lang.JoinPoint; /** * Function:拦截器方法 * * @author chenjiec * Date: 2017/1/3 上午12:34 * @since JDK 1.7 */ public class DataSourceExchange { /** * * @param point */ public void before(JoinPoint point) { //获取目标对象的类类型 Class<?> aClass = point.getTarget().getClass(); //获取包名用于区分不同数据源 String whichDataSource = aClass.getName().substring(25, aClass.getName().lastIndexOf(".")); if ("ssmone".equals(whichDataSource)) { DataSourceHolder.setDataSources(Constants.DATASOURCE_ONE); } else { DataSourceHolder.setDataSources(Constants.DATASOURCE_TWO); } } /** * 执行后将数据源置为空 */ public void after() { DataSourceHolder.setDataSources(null); } }
逻辑也比较简单,就是在执行数据库操作之前做一个切面。
- 通过
JoinPoint
对象获取目标对象。
- 在目标对象中获取包名来区分不同的数据源。
- 根据不同数据源来进行赋值。
- 执行完毕之后将数据源清空。
关于一些JoinPoint
的API:
package org.aspectj.lang; import org.aspectj.lang.reflect.SourceLocation; public interface JoinPoint { String toString(); //连接点所在位置的相关信息 String toShortString(); //连接点所在位置的简短相关信息 String toLongString(); //连接点所在位置的全部相关信息 Object getThis(); //返回AOP代理对象 Object getTarget(); //返回目标对象 Object[] getArgs(); //返回被通知方法参数列表 Signature getSignature(); //返回当前连接点签名 SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置 String getKind(); //连接点类型 StaticPart getStaticPart(); //返回连接点静态部分 }
为了通过包名
来区分不同数据源,我将目录结构稍微调整了下:
2
将两个不同的数据源的实现类放到不同的包中,这样今后如果还需要新增其他数据源也可以灵活的切换。
看下Spring
的配置:
<bean id="dataSourceExchange" class="com.crossoverJie.util.DataSourceExchange"/> <!--配置切面拦截方法 --> <aop:config proxy-target-class="false"> <!--将com.crossoverJie.service包下的所有select开头的方法加入拦截 去掉select则加入所有方法 --> <aop:pointcut id="controllerMethodPointcut" expression=" execution(* com.crossoverJie.service.*.select*(..))"/> <aop:pointcut id="selectMethodPointcut" expression=" execution(* com.crossoverJie.dao..*Mapper.select*(..))"/> <aop:advisor advice-ref="methodCacheInterceptor" pointcut-ref="controllerMethodPointcut"/> <!--所有数据库操作的方法加入切面--> <aop:aspect ref="dataSourceExchange"> <aop:pointcut id="dataSourcePointcut" expression="execution(* com.crossoverJie.service.*.*(..))"/> <aop:before pointcut-ref="dataSourcePointcut" method="before"/> <aop:after pointcut-ref="dataSourcePointcut" method="after"/> </aop:aspect> </aop:config>
这是在我们上一篇整合redis缓存的基础上进行修改的。
这样缓存和多数据源都满足了。
实际使用:
@Test public void selectByPrimaryKey() throws Exception { Rediscontent rediscontent = rediscontentService.selectByPrimaryKey(30); System.out.println(JSON.toJSONString(rediscontent)); }
3
这样看起来就和使用一个数据源这样简单,再也不用关心切换的问题了。
总结
不过按照这样的写法是无法做到在一个事务里控制两个数据源的。这个我还在学习中,有相关经验的大牛不妨指点一下。
个人博客地址:crossoverjie.top。
GitHub地址:github.com/crossoverJi…