Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用

时间:2022-09-02 14:33:22

【参考】Spring(AbstractRoutingDataSource)实现动态数据源切换--转载

【参考】 利用Spring的AbstractRoutingDataSource解决多数据源的问题

一:关于具体的原理说明请卡上面的参考链接

二:操作步骤 :在你数据库配置文件中(我的是spring-dao.xml)配置多数据源  这里只展示了 数据库的配置

<!-- 配置整合mybatis-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:jdbc.properties</value>
</list>
</property>
</bean> <!--配置数据库的连接池-->
<bean id="abstractDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 配置链接属性 多数据源配置-->
<!-- <property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="user" value="${username}"/>
<property name="password" value="${password}"/>-->
<!--配置c3p0连接池的私有属性-->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!--关闭链接后不自动commit-->
<property name="autoCommitOnClose" value="false"/>
<property name="checkoutTimeout" value="1000"/>
<!--获取连接失败重试次数-->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!--多数据源配置 parent指向上面的配置数据库的连接池abstractDataSource 这里配置了两个数据源-->
<bean id="base" parent="abstractDataSource">
<property name="driverClass" value="${driver}"/>
<property name="jdbcUrl" value="${url}"/>
<property name="user" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="server_1" parent="abstractDataSource">
<property name="driverClass" value="${server_1_driver}"/>
<property name="jdbcUrl" value="${server_1_url}"/>
<property name="user" value="${server_1_username}"/>
<property name="password" value="${server_1_password}"/>
</bean> <!-- 在RoutingDataSource类中读取 当前的返回值 并匹配key值 选择你的数据库源-->
<bean id="dataSource" class="com.cdms.dao.resources.RoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="base" value-ref="base"></entry>
<entry key="server_1" value-ref="server_1"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="base"></property>
</bean> <!-- 配置sqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据库连接池-->
<property name="dataSource" ref="dataSource"/>
<!-- 配置Mybatisq全局文件:-->
<property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- 扫描entity包-->
<property name="typeAliasesPackage" value="com.cdms.entity"/>
<!-- 扫描sql配置文件-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>

另外 我的jdbc:perperties 配置文件是

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/course_design?useUnicode=true&characterEncoding=UTF-8
username=root
password=root server_1_driver=com.mysql.jdbc.Driver
server_1_url=jdbc:mysql://127.0.0.1:3306/course_design_test?useUnicode=true&characterEncoding=UTF-8
server_1_username=root
server_1_password=root
RoutingDataSource类和DynamicDataSourceHolder类 
public class RoutingDataSource extends AbstractRoutingDataSource{
public final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override
protected Object determineCurrentLookupKey() {
String dataSourceName = DynamicDataSourceHolder.getDataSourceName();
if(dataSourceName == null){
dataSourceName = DataSourceType.BASE.getDataSource();
}
logger.info("选择的数据库是:"+dataSourceName);
return dataSourceName;
}
}
public class DynamicDataSourceHolder {

    //解决线程安全问题
private static final ThreadLocal<String> holder = new ThreadLocal<String>(); public static void putDataSourceName(String dataName){
holder.set(dataName);
} public static String getDataSourceName(){
return holder.get();
} public static class DataSourceName{
public final static String BASE = "base";
}
}

我们可以通过在holder中 put数据库的key值 来实现数据源的动态加载,其中 ThreadLocal<String> holder 保证了线程安全,而 RoutingDataSource
类里面的determineCurrentLookupKey()方法返回值 就是数据源的key值 配置文件通过这个key值找到了当前的配置源链接。这个是的测试 我就不多演示。

那我们怎么动态的进行数据链接呢,我们可以通过aop切面变成来实现

具体的aop切面编程的配置这里不多做介绍,可以参考之前的记过博文  有介绍
三:AOP注入实现 数据源的动态配置  首先需要第一一个切点。

package com.cdms.aop;

import com.cdms.eunms.DataSourceType;

import java.lang.annotation.*;

/**
* 创建 by 草帽boy on 2017/3/29.
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ChooseDataSource {
/**
* 数据库名称 默认似base
* @return 数据库名称
*/
String dataSourceName() default "base"; }

然后定义一个切面方法

package com.cdms.aop;

import com.cdms.dao.resources.DynamicDataSourceHolder;
import com.cdms.eunms.DataSourceType;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* 创建 by 草帽boy on 2017/3/29.
*/
@Aspect
@Component
public class GetDataSource { /**
* 切面设置要选择的数据库
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("@annotation(com.cdms.aop.ChooseDataSource)")
public Object permission(ProceedingJoinPoint joinPoint) throws Throwable {
Object target = joinPoint.getTarget();
Object[] args = joinPoint.getArgs();
Method method = getMethod(joinPoint, args);
//获取数据库名称参数
ChooseDataSource chooseDataSource = method.getAnnotation(ChooseDataSource.class);
if(chooseDataSource != null){
String dataSourceName = chooseDataSource.dataSourceName();
//坚持名称是否合法 如果不合法 就用默认数据库
if(DataSourceType.check(dataSourceName)){
DynamicDataSourceHolder.putDataSourceName(dataSourceName);
}else {
DynamicDataSourceHolder.putDataSourceName(DataSourceType.BASE.getDataSource());
}
}
return joinPoint.proceed();
} /**
* 获取切面方法
* @param joinPoint
* @param args
* @return
* @throws NoSuchMethodException
*/
private Method getMethod(ProceedingJoinPoint joinPoint, Object[] args) throws NoSuchMethodException {
// Class[] argsClazz = new Class[args.length];
// for(int i = 0 ; i < args.length; i++){
// argsClazz[i] = args[i].getClass();
// }
String methodName = joinPoint.getSignature().getName();
Class clazz = joinPoint.getTarget().getClass();
Method[] methods = clazz.getMethods();
for(Method method : methods) {
if (methodName.equals(method.getName())) {
return method;
}
}
return null;
}
}
 <!-- 启动@aspectj的自动代理支持-->
<aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy>
<!-- 定义aspect类 -->
<bean id="logAopAction" class="com.cdms.aop.LogAopAction"></bean>
<bean id="aopTest" class="com.cdms.aop.DemoAopTest"></bean>
<bean id="getDataSourcesName" class="com.cdms.aop.GetDataSource"></bean>

在之后需要改变数据源的地方 只需要 @ChooseDataSource(dataSourceName = "server_1")   表明你的数据源就看了

使用单元测试的结果是 ;(@ChooseDataSource(dataSourceName = "server_1") )

Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用

(@ChooseDataSource(dataSourceName = "base") )

Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用

这个是注入点:

Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用

Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用的更多相关文章

  1. Spring&lpar;AbstractRoutingDataSource&rpar;实现动态数据源切换--转载

    原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...

  2. SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换

    SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换 本文转载至:http://exceptioneye.iteye.com/blog/1698064 Spri ...

  3. AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/u012881904/article/de ...

  4. 30个类手写Spring核心原理之动态数据源切换(8)

    本文节选自<Spring 5核心原理> 阅读本文之前,请先阅读以下内容: 30个类手写Spring核心原理之自定义ORM(上)(6) 30个类手写Spring核心原理之自定义ORM(下)( ...

  5. Spring主从数据库的配置和动态数据源切换原理

    原文:https://www.liaoxuefeng.com/article/00151054582348974482c20f7d8431ead5bc32b30354705000 在大型应用程序中,配 ...

  6. 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...

  7. Spring动态切换多数据源事务开启后,动态数据源切换失效解决方案

    关于某操作中开启事务后,动态切换数据源机制失效的问题,暂时想到一个取巧的方法,在Spring声明式事务配置中,可对不改变数据库数据的方法采用不支持事务的配置,如下: 对单纯查询数据的操作设置为不支持事 ...

  8. AbstractRoutingDataSource 实现动态数据源切换原理简单分析

    AbstractRoutingDataSource 实现动态数据源切换原理简单分析 写在前面,项目中用到了动态数据源切换,记录一下其运行机制. 代码展示 下面列出一些关键代码,后续分析会用到 数据配置 ...

  9. Java注解--实现动态数据源切换

    当一个项目中有多个数据源(也可以是主从库)的时候,我们可以利用注解在mapper接口上标注数据源,从而来实现多个数据源在运行时的动态切换. 实现原理 在Spring 2.0.1中引入了Abstract ...

随机推荐

  1. matlab 求解线性方程组之LU分解

    线性代数中的一个核心思想就是矩阵分解,既将一个复杂的矩阵分解为更简单的矩阵的乘积.常见的有如下分解: LU分解:A=LU,A是m×n矩阵,L是m×m下三角矩阵,U是m×n阶梯形矩阵 QR分解: 秩分解 ...

  2. Oracle 数据库SQL性能查看

    作为一个开发/测试人员,或多或少都得和数据库打交道,而对数据库的操作归根到底都是SQL语句,所有操作到最后都是操作数据,那么对sql性能的掌控又成了我们工作中一件非常重要的工作.下面简单介绍下一些查看 ...

  3. map&lbrack;C&plus;&plus;&rsqb;

    //map是一个存储键值对的容器,也是一个双向链表 #include <iostream> using namespace std; #include <map> int ma ...

  4. HighCharts使用心得

    HighCharts使用心得 前言: 之前很早的一个项目中使用过highcharts,感觉挺方便的,图表类型也比较丰富,而且还支持数据的下钻,但是如果投入商业使用的话还会有一些版权的问题,所以后来就使 ...

  5. git的作用和原理(待续)

    先选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令: $ sudo git init --bare sample.git Git就会创建一个裸仓库,裸仓库没有 ...

  6. ReportNG测试报告模板定制

      部分参考:http://tech.it168.com/a2013/0906/1530/000001530755_3.shtml ReportNG提供了简单的方式来查看测试结果,并能对结果进行着色, ...

  7. Ubuntu 16&period;04 LTS安装搜狗拼音输入法网易云音乐 Remarkable

    第一步 首先在官网上面,下载最新的搜狗拼音输入法 Linux 版本. 第二步 进入命令行 Ctrl+Alt+T sudo dpkg -i sogoupinyin_2.1.0.0082_amd64.de ...

  8. golang 并发模式笔记

    1.并发并不是并行,前者是优先对时间片的抢占,后者是真多核. go中多线程时直接要求并行的方法是: 亦不可滥用,CPU密集型,并发度很高的场景适用. 2.go起的协程 3. function that ...

  9. Puppeteer - 谷歌推出的自动化测试工具库

    Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制无头 Chrome 或 Chromium.它允许你从浏览器之外的环境(即命令行)与Chromium ...

  10. Windows store app&lbrack;Part 4&rsqb;&colon;深入WinRT的异步机制

    接上篇Windows store app[Part 3]:认识WinRT的异步机制 WinRT异步机制回顾: IAsyncInfo接口:WinRT下异步功能的核心,该接口提供所有异步操作的基本功能,如 ...