需求

代码实现读写数据库分离

武器

spring3.0以上版本

实现思路

1、继承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource,自定义数据源路由。

2、实现数据源类型管理工具,诸如DBContextHolder,包含设置和读取当前数据源配置。

3、实现数据源切换的AOP。

4、自定义只读注解,诸如@ReadOnlyKey。

5、配置transactionManager,实现aop。

代码示例

1、自定义的DynamicDataSource

  1. public class DynamicDataSource extends AbstractRoutingDataSource {
  2. /**
  3. * 自动查找数据源
  4. *
  5. * @return 数据源名
  6. */
  7. @Override
  8. protected Object determineCurrentLookupKey() {
  9. String dataSource = getDataSource();
  10. return dataSource;
  11. }
  12. }

2、数据源类型管理工具DBContextHolder

  1. public abstract class DBContextHolder {
  2. /**
  3. * 数据源类型管理
  4. * <p>
  5. * 考虑多线程,为保证线程之间互不干扰,所以使用ThreadLocal作线程隔离;<br>
  6. * 参数是数据源键值
  7. * </p>
  8. *
  9. * @see ThreadLocal
  10. */
  11. private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
  12.  
  13. /**
  14. * 数据库源类型
  15. * <p>
  16. * 配置数据源的时候,请遵守以下约束:<br>
  17. * 读写:dataSourceKeyRW;<br>
  18. * 读:dataSourceKeyR.
  19. * </p>
  20. */
  21. public enum DbType {
  22. DB_TYPE_RW("dataSourceKeyRW"), DB_TYPE_R("dataSourceKeyR");
  23. private String dataSourceKey;
  24.  
  25. DbType(String dataSourceKey) {
  26. this.dataSourceKey = dataSourceKey;
  27. }
  28.  
  29. public String getDataSourceKey() {
  30. return dataSourceKey;
  31. }
  32. }
  33.  
  34. /**
  35. * 获取数据源
  36. * <p>
  37. * 如果未设置,默认返回读数据源
  38. * </p>
  39. *
  40. * @return 数据源键值
  41. */
  42. public static String getDataSource() {
  43. String dataSource = contextHolder.get();
  44. if (StringUtils.isEmpty(dataSource)) {
  45. dataSource = DbType.DB_TYPE_RW.dataSourceKey;
  46. }
  47. return dataSource;
  48. }
  49.  
  50. /**
  51. * 设置数据源
  52. *
  53. * @param dataSourceKey 数据源键值
  54. */
  55. public static void setDataSource(String dataSourceKey) {
  56. contextHolder.set(dataSourceKey);
  57. }
  58. }

注:定义了DbType枚举,分别定义了读和写的数据源键值。

3、实现AOP。

  1. public class DataSourceSwitchingAop {
  2. /**
  3. * 设置切点数据源
  4. * <p>
  5. * 调试输出数据源.
  6. * </p>
  7. *
  8. * @param joinPoint 切点
  9. * @param dataSourceKey 当前数据源键值
  10. */
  11. private void setDataSourceByKey(JoinPoint joinPoint, String dataSourceKey) {
  12. setDataSource(dataSourceKey);
  13. debugLog(joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature().getName() + "配置数据源:" + getDataSource());
  14. }
  15.  
  16. /**
  17. * 切换数据源
  18. * <p>
  19. * 切换优先级由高到底如下;方法上注解DataSourceKey,方法上注解ReadOnlyKey,类上注解DataSourceKey;<br>
  20. * 如果未注解,则默认设置写数据源.
  21. * </p>
  22. *
  23. * @param joinPoint 切点
  24. * @see DataSourceKey
  25. * @see ReadOnlyKey
  26. * @see DbType
  27. */
  28. public void switchDataSource(JoinPoint joinPoint) {
  29. Class<?> targetClass = joinPoint.getTarget().getClass();
  30. String methodName = joinPoint.getSignature().getName();
  31. Object[] args = joinPoint.getArgs();
  32. DataSourceKey dataSourceKey = getAnnotationClassMethod(targetClass, methodName, DataSourceKey.class, args);
  33. if (dataSourceKey != null) {
  34. setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());
  35. return;
  36. }
  37. ReadOnlyKey readOnlyKey = getAnnotationClassMethod(targetClass, methodName, ReadOnlyKey.class, args);
  38. if (readOnlyKey != null) {
  39. setDataSourceByKey(joinPoint, DbType.DB_TYPE_R.getDataSourceKey());
  40. return;
  41. }
  42. dataSourceKey = (DataSourceKey) targetClass.getAnnotation(DataSourceKey.class);
  43. if (dataSourceKey != null) {
  44. setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());
  45. return;
  46. }
  47. setDataSourceByKey(joinPoint, DbType.DB_TYPE_RW.getDataSourceKey());
  48. }
  49. }

4、自定义只读注解,@ReadOnlyKey

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.METHOD)
  3. public @interface ReadOnlyKey {
  4. }

5、配置transaction和AOP

  1. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  2. <property name="dataSource" ref="dynamicDataSource"/>
  3. </bean>
  1. <bean id="dataSourceSwitchingAop" class="com.xxx.common.framework2x.dao.DataSourceSwitchingAop"/>
  1. <aop:config>
  2. <aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">
  3. <aop:pointcut id="dataSourceSwitchingService"
  4. expression="execution(* com.xxx.manager..*.*(..))"/>
  5. <aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>
  6. </aop:aspect>
  7. </aop:config>

以上就完成了基于注解实现动态切换读写数据源。

6、如果想要实现多数据源的切换,则可以自定义注解@DataSourceKey

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ElementType.METHOD, ElementType.TYPE})
  3. public @interface DataSourceKey {
  4. /**
  5. * 配置数据源键值
  6. * <p>
  7. * 默认:dataSource.
  8. * </p>
  9. *
  10. * @return 键值
  11. */
  12. String dataSourceKey() default "dataSource";
  13. }

在接口方法上增加注解即可。

需要特别注意的地方

1、切换数据源的事务需要放到数据库事务开启前执行。针对上述代码示例中,配置aop时需要指定order(值越小,执行越靠前)

  1. <aop:config>
  2. <aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">
  3. <aop:pointcut id="dataSourceSwitchingService"
  4. expression="execution(* com.xxx.manager..*.*(..))"/>
  5. <aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>
  6. </aop:aspect>
  7. </aop:config>

2、@DataSourceKey可以加在method上,也可以加到class上,优先级是method>class。

3、@ReadOnlyKey只能加到method上。

4、@DatasourceKey和@ReadOnlyKey可以在一个class中混用,优先级是method的@DatasourceKey>method的@ReadOnlyKey>class的@DatasourceKey。

springAOP实现基于注解的数据源动态切换的更多相关文章

  1. SPRINGAOP实现基于注解的数据源动态切换(转)

    需求 代码实现读写数据库分离 武器 spring3.0以上版本 实现思路 1.继承org.springframework.jdbc.datasource.lookup.AbstractRoutingD ...

  2. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  3. Springboot多数据源配置--数据源动态切换

    在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...

  4. springboot多数据源动态切换和自定义mybatis分页插件

    1.配置多数据源 增加druid依赖 完整pom文件 数据源配置文件 route.datasource.driver-class-name= com.mysql.jdbc.Driver route.d ...

  5. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  6. Spring多数据源动态切换

    title: Spring多数据源动态切换 date: 2019-11-27 categories: Java Spring tags: 数据源 typora-root-url: ...... --- ...

  7. mybatis 多数据源动态切换

    笔者主要从事c#开发,近期因为项目需要,搭建了一套spring-cloud微服务框架,集成了eureka服务注册中心. gateway网关过滤.admin服务监控.auth授权体系验证,集成了redi ...

  8. 实战:Spring AOP实现多数据源动态切换

    需求背景 去年底,公司项目有一个需求中有个接口需要用到平台.算法.大数据等三个不同数据库的数据进行计算.组装以及最后的展示,当时这个需求是另一个老同事在做,我只是负责自己的部分. 直到今年回来了,这个 ...

  9. 基于注解实现jackson动态JsonProperty

    基于注解实现jackson动态JsonProperty @JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name,但是值是固定的 ...

随机推荐

  1. ipython的安装

    ipython:是python的一个交互式shell环境,可以安装到windows和linux上面.作用:用来执行python代码和调试用.windows上面安装:分为2.x版本和3.x版本,分为py ...

  2. Avalon接口协议

    Avalon接口协议 https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/manual/mnl_ava ...

  3. 一年成为emacs高手

    http://blog.csdn.net/redguardtoo/article/details/7222501

  4. dfs 翻棋盘end

    #include<iostream> char data[16]; int a[16]; int d[4] = { -4, 1, 4, -1 }; char b[16]; int flag ...

  5. Generics and Collection (1)

    public static void main(String args[]) { List ints = Arrays.asList(), )}); ; for(Iterator it = ints. ...

  6. HDU 5410 CRB and His Birthday ——(完全背包变形)

    对于每个物品,如果购买,价值为A[i]*x+B[i]的背包问题. 先写了一发是WA的= =.代码如下: #include <stdio.h> #include <algorithm& ...

  7. Python学习之路——基础篇(1)字符串格式化

    字符串格式化 Python的字符串格式化有两种方式: 百分号方式.format方式  百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存. 百分号方式 ...

  8. TimeQuest学习

    1.物理时钟特性:clock skew(时钟差),jitter(拉动),clock latency(时钟潜伏),这些物理时钟特性又称为uncertainl--非定性,或非理想性. clock skew ...

  9. oracle exp imp 导入 正在跳过表 plsql 导入表 成功终止 数据 被导入

    http://blog.csdn.net/agileclipse/article/details/12968011 .导入过程中,所有表导入都出现提示, 正在跳过表...某某表名 最后提示成功终止导入 ...

  10. oracle java SE

    http://www.oracle.com/technetwork/java/javase/downloads/index.html