需求

代码实现读写数据库分离

武器

spring3.0以上版本

实现思路

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

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

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

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

5、配置transactionManager,实现aop。

代码示例

1、自定义的DynamicDataSource

public class DynamicDataSource extends AbstractRoutingDataSource {

/**

* 自动查找数据源
*
* @return 数据源名

*/

@Override

protected Object determineCurrentLookupKey() {

String dataSource = getDataSource();

return dataSource;

}

}

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

public abstract class DBContextHolder {
/**

* 数据源类型管理

* <p>

* 考虑多线程,为保证线程之间互不干扰,所以使用ThreadLocal作线程隔离;<br>

* 参数是数据源键值

* </p>

*

* @see ThreadLocal

*/

private static ThreadLocal<String> contextHolder = new
ThreadLocal<String>();
</span><span style="color: #008000;">/**</span><span style="color: #008000;"></br>
* 数据库源类型</br>
* &lt;p&gt;</br>
* 配置数据源的时候,请遵守以下约束:&lt;br&gt;</br>
* 读写:dataSourceKeyRW;&lt;br&gt;</br>
* 读:dataSourceKeyR.</br>
* &lt;/p&gt;</br>
</span><span style="color: #008000;">*/</span></br>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">enum</span><span style="color: #000000;"> DbType {</br>
DB_TYPE_RW(</span>"dataSourceKeyRW"), DB_TYPE_R("dataSourceKeyR"<span style="color: #000000;">);</br>
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> String dataSourceKey;</br> DbType(String dataSourceKey) {</br>
</span><span style="color: #0000ff;">this</span>.dataSourceKey =<span style="color: #000000;"> dataSourceKey;</br>
}</br> </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String getDataSourceKey() {</br>
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> dataSourceKey;</br>
}</br>
} </span><span style="color: #008000;">/**</span><span style="color: #008000;"></br>
* 获取数据源</br>
* &lt;p&gt;</br>
* 如果未设置,默认返回读数据源</br>
* &lt;/p&gt;</br>
*</br>
* </span><span style="color: #808080;">@return</span><span style="color: #008000;"> 数据源键值</br>
</span><span style="color: #008000;">*/</span></br>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> String getDataSource() {</br>
String dataSource </span>=<span style="color: #000000;"> contextHolder.get();</br>
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (StringUtils.isEmpty(dataSource)) {</br>
dataSource </span>=<span style="color: #000000;"> DbType.DB_TYPE_RW.dataSourceKey;</br>
}</br>
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> dataSource;</br>
}</br> </span><span style="color: #008000;">/**</span><span style="color: #008000;">
* 设置数据源</br>
*
* </span><span style="color: #808080;">@param</span><span style="color: #008000;"> dataSourceKey 数据源键值</br>
</span><span style="color: #008000;">*/</span></br>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setDataSource(String dataSourceKey) {</br>
contextHolder.set(dataSourceKey);</br>
}</br>

}

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


3、实现AOP。



public class DataSourceSwitchingAop {

/**

* 设置切点数据源

* <p>

* 调试输出数据源.

* </p>

*
* @param joinPoint 切点

* @param dataSourceKey 当前数据源键值

*/

private void setDataSourceByKey(JoinPoint joinPoint, String dataSourceKey) {

setDataSource(dataSourceKey);

debugLog(joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature().getName() + "配置数据源:" + getDataSource());

}
</span><span style="color: #008000;">/**</span><span style="color: #008000;"></br>
* 切换数据源</br>
* &lt;p&gt;</br>
* 切换优先级由高到底如下;方法上注解DataSourceKey,方法上注解ReadOnlyKey,类上注解DataSourceKey;&lt;br&gt;</br>
* 如果未注解,则默认设置写数据源.</br>
* &lt;/p&gt;</br>
*
* </span><span style="color: #808080;">@param</span><span style="color: #008000;"> joinPoint 切点</br>
* </span><span style="color: #808080;">@see</span><span style="color: #008000;"> DataSourceKey</br>
* </span><span style="color: #808080;">@see</span><span style="color: #008000;"> ReadOnlyKey</br>
* </span><span style="color: #808080;">@see</span><span style="color: #008000;"> DbType</br>
</span><span style="color: #008000;">*/</span>
<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> switchDataSource(JoinPoint joinPoint) {</br>
Class</span>&lt;?&gt; targetClass =<span style="color: #000000;"> joinPoint.getTarget().getClass();</br>
String methodName </span>=<span style="color: #000000;"> joinPoint.getSignature().getName();</br>
Object[] args </span>=<span style="color: #000000;"> joinPoint.getArgs();</br>
DataSourceKey dataSourceKey </span>= getAnnotationClassMethod(targetClass, methodName, DataSourceKey.<span style="color: #0000ff;">class</span><span style="color: #000000;">, args);</br>
</span><span style="color: #0000ff;">if</span> (dataSourceKey != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {</br>
setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());</br>
</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;</br>
}</br>
ReadOnlyKey readOnlyKey </span>= getAnnotationClassMethod(targetClass, methodName, ReadOnlyKey.<span style="color: #0000ff;">class</span><span style="color: #000000;">, args);</br>
</span><span style="color: #0000ff;">if</span> (readOnlyKey != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {</br>
setDataSourceByKey(joinPoint, DbType.DB_TYPE_R.getDataSourceKey());</br>
</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;</br>
}</br>
dataSourceKey </span>= (DataSourceKey) targetClass.getAnnotation(DataSourceKey.<span style="color: #0000ff;">class</span><span style="color: #000000;">);</br>
</span><span style="color: #0000ff;">if</span> (dataSourceKey != <span style="color: #0000ff;">null</span><span style="color: #000000;">) {</br>
setDataSourceByKey(joinPoint, dataSourceKey.dataSourceKey());</br>
</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;</br>
}</br>
setDataSourceByKey(joinPoint, DbType.DB_TYPE_RW.getDataSourceKey());</br>
}</br>

}

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


@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface ReadOnlyKey {

}

5、配置transaction和AOP

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource"/>

</bean>


<bean id="dataSourceSwitchingAop" class="com.xxx.common.framework2x.dao.DataSourceSwitchingAop"/>
<aop:config>

<aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">

<aop:pointcut id="dataSourceSwitchingService"

expression="execution(* com.xxx.manager..*.*(..))"/>

<aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>

</aop:aspect>

</aop:config>

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


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



@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD, ElementType.TYPE})

public @interface DataSourceKey {

/**

* 配置数据源键值

* <p>

* 默认:dataSource.

* </p>

*

* @return 键值

*/

String dataSourceKey() default "dataSource";
}

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

需要特别注意的地方

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

<aop:config>

<aop:aspect id="dataSourceSwitching" ref="dataSourceSwitchingAop" order="0">

<aop:pointcut id="dataSourceSwitchingService"

expression="execution(* com.xxx.manager..*.*(..))"/>

<aop:before method="switchDataSource" pointcut-ref="dataSourceSwitchingService"/>

</aop:aspect>

</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. C#实现窗口拖动时各个控件同比自己主动放缩大小

    实现方式主要是利用panel控件为主题.对于每一个控件的大小位置和字体这几个属性进行记录. 然后依据窗口改变的大小同一时候放缩. 简要过程例如以下: 1 创建C#窗口程序项目. 2  Panel放置到 ...

  2. 第二天,导出文件sql,查询,视图view,聚合函数,反模式,字符串处理函数

    //把数据库导出到脚本文件mysqldump -uroot -p1234 --databases abc > d:/a/abc.sql CREATE TABLE stud( id INT PRI ...

  3. MathType下载和安装(与Visio搭配使用)

    不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑          ...

  4. Whitening

    The goal of whitening is to make the input less redundant; more formally, our desiderata are that ou ...

  5. Centos7.0 Vmware10.0.3 网络桥接配置

    首先要将Vmware10.0.3设置为桥接模式. CentOS 7.0默认安装好之后是没有自动开启网络连接的! cd  /etc/sysconfig/network-scripts/  #进入网络配置 ...

  6. XSY3244 10.31 D

    XSY3244 10.31 D 题意: ​ 数轴上有\(N\)只老鼠\(M\)个洞,每个洞有一个容量,求所有老鼠进洞的最小代价.(\(N,M\leq1000000\),时限\(2s\)) 题解: ​ ...

  7. Java生产者与消费者(下)

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 上一讲我们让消费者和生产者都各停1毫秒,实际上大多并不是这样的.第二讲,我们讲一个极端的例子和一个正 ...

  8. virmon防火墙64位正式版(暂定)公布

    ChangeLog: 2015-06-2564位版本号签名问题临时得到解决.还要致谢一下某位黑客. 支持版本号x64 Windows Vista.7.8.8.1以上等.个人仅仅在Windows7上做了 ...

  9. 2.Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC)-

    转自:https://blog.csdn.net/a214919447/article/details/55260411 SOAP(Simple Object Access Protocol,简单对象 ...

  10. Object.prototype.toString.call(value)

    使用Object.prototype上的原生toString()方法判断数据类型,使用方法如下: Object.prototype.toString.call(value) 1.判断基本类型: Obj ...