操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程(http://blog.csdn.net/yanzi1225627/article/details/26950615/)

或者是使用多个DataSource 然后创建多个SessionFactory,在使用Dao层的时候通过不同的SessionFactory进行处理,不过这样的入侵性比较明显,一般的情况下我们都是使用继承HibernateSupportDao进行封装了的处理,如果多个SessionFactory这样处理就是比较的麻烦了,修改的地方估计也是蛮多的

最后一个,也就是使用AbstractRoutingDataSource的实现类通过AOP或者手动处理实现动态的使用我们的数据源,这样的入侵性较低,非常好的满足使用的需求。比如我们希望对于读写分离或者其他的数据同步的业务场景

下面看看图片

 

单数据源的场景(一般的Web项目工程这样配置进行处理,就已经比较能够满足我们的业务需求)

多数据源多SessionFactory这样的场景,估计作为刚刚开始想象想处理在使用框架的情况下处理业务,配置多个SessionFactory,然后在Dao层中对于特定的请求,通过特定的SessionFactory即可处理实现这样的业务需求,不过这样的处理带来了很多的不便之处,所有很多情况下我们宁愿直接使用封装的JDBC编程,或者使用Mybatis处理这样的业务场景

使用AbstractRoutingDataSource 的实现类,进行灵活的切换,可以通过AOP或者手动编程设置当前的DataSource,不用修改我们编写的对于继承HibernateSupportDao的实现类的修改,这样的编写方式比较好,至于其中的实现原理,让我细细到来。我们想看看如何去应用,实现原理慢慢的说!

编写AbstractRoutingDataSource的实现类,HandlerDataSource就是提供给我们动态选择数据源的数据的信息,我们这里编写一个根据当前线程来选择数据源,然后通过AOP拦截特定的注解,设置当前的数据源信息,也可以手动的设置当前的数据源,在编程的类中。

package com.common.utils.manydatasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**

* descrption: 多数据源的选择

* authohr: wangji

* date: 2017-08-21 10:32

*/

public class MultipleDataSourceToChoose extends AbstractRoutingDataSource {

/**

* @desction: 根据Key获取数据源的信息,上层抽象函数的钩子

* @author: wangji

* @date: 2017/8/21

* @param:

* @return:

*/

@Override

protected Object determineCurrentLookupKey() {

return HandlerDataSource.getDataSource();

}

}

设置动态选择的Datasource,这里的Set方法可以留给AOP调用,或者留给我们的具体的Dao层或者Service层中手动调用,在执行SQL语句之前。

package com.common.utils.manydatasource;

/**

* descrption: 根据当前线程来选择具体的数据源

* authohr: wangji

* date: 2017-08-21 10:36

*/

public class HandlerDataSource {

private static ThreadLocal<String> handlerThredLocal = new ThreadLocal<String>();

/**

* @desction: 提供给AOP去设置当前的线程的数据源的信息

* @author: wangji

* @date: 2017/8/21

* @param: [datasource]

* @return: void

*/

public static void putDataSource(String datasource) {

handlerThredLocal.set(datasource);

}

/**

* @desction: 提供给AbstractRoutingDataSource的实现类,通过key选择数据源

* @author: wangji

* @date: 2017/8/21

* @param: []

* @return: java.lang.String

*/

public static String getDataSource() {

return handlerThredLocal.get();

}

/**

* @desction: 使用默认的数据源

*/

public static void clear() {

handlerThredLocal.remove();

}

}

设置拦截数据源的注解,可以设置在具体的类上,或者在具体的方法上,dataSource是当前数据源的一个别名用于标识我们的数据源的信息。

package com.common.utils.manydatasource;

import java.lang.annotation.*;

/**

* @description: 创建拦截设置数据源的注解

* Created by wangji on 2017/8/21.

*/

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

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface DynamicSwitchDataSource {

String dataSource() default "";

}

AOP拦截类的实现,通过拦截上面的注解,在其执行之前处理设置当前执行SQL的数据源的信息,HandlerDataSource.putDataSource(….),这里的数据源信息从我们设置的注解上面获取信息,如果没有设置就是用默认的数据源的信息。

package com.common.utils.manydatasource;

import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

import org.aspectj.lang.reflect.MethodSignature;

import org.springframework.core.annotation.Order;

import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**

* descrption: 使用AOP拦截特定的注解去动态的切换数据源

* authohr: wangji

* date: 2017-08-21 10:42

*/

@Aspect

@Slf4j

@Component

@Order(1)

public class HandlerDataSourceAop {

//@within在类上设置

//@annotation在方法上进行设置

@Pointcut("@within(com.common.utils.manydatasource.DynamicSwitchDataSource)||@annotation(com.common.utils.manydatasource.DynamicSwitchDataSource)")

public void pointcut() {}

@Before("pointcut()")

public void doBefore(JoinPoint joinPoint)

{

Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();

DynamicSwitchDataSource annotationClass = method.getAnnotation(DynamicSwitchDataSource.class);//获取方法上的注解

if(annotationClass == null){

annotationClass = joinPoint.getTarget().getClass().getAnnotation(DynamicSwitchDataSource.class);//获取类上面的注解

if(annotationClass == null) return;

}

//获取注解上的数据源的值的信息

String dataSourceKey = annotationClass.dataSource();

if(dataSourceKey !=null){

//给当前的执行SQL的操作设置特殊的数据源的信息

HandlerDataSource.putDataSource(dataSourceKey);

}

log.info("AOP动态切换数据源,className"+joinPoint.getTarget().getClass().getName()+"methodName"+method.getName()+";dataSourceKey:"+dataSourceKey==""?"默认数据源":dataSourceKey);

}

@After("pointcut()")

public void after(JoinPoint point) {

//清理掉当前设置的数据源,让默认的数据源不受影响

HandlerDataSource.clear();

}

}

配置数据源在Spring 核心容器中配置

jdbc.driver=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis

jdbc.username=root

jdbc.password=root

jdbc2.url=jdbc:mysql://127.0.0.1:3306/datasource2

<!-- 配置数据源 -->

<bean id="dataSource0" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init">

<property name="driverClassName" value="${jdbc.driver}"/>

<property name="url" value="${jdbc.url}"/>

<property name="username" value="${jdbc.username}"/>

<property name="password" value="${jdbc.password}"/>

<property name="maxActive" value="10"/>

</bean>

<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init">

<property name="driverClassName" value="${jdbc.driver}"/>

<property name="url" value="${jdbc2.url}"/>

<property name="username" value="${jdbc.username}"/>

<property name="password" value="${jdbc.password}"/>

<property name="maxActive" value="10"/>

</bean>

配置之前我们实现的数据源选择的中间层AbstractRoutingDataSource的实现类,这里的key就是数据源信息的别名,通过这个key可以选择到数据源的信息。MultipleDataSourceToChoose就是上面写的数据源选择器的实现类

bean id="dataSource" class="com.common.utils.manydatasource.MultipleDataSourceToChoose" lazy-init="true">

<description>数据源</description>

<property name="targetDataSources">

<map key-type="java.lang.String" value-type="javax.sql.DataSource">

<entry key="datasource0" value-ref="dataSource0" />

<entry key="datasource1" value-ref="dataSource1" />

</map>

</property>

<!-- 设置默认的目标数据源 -->

<property name="defaultTargetDataSource" ref="dataSource0" />

</bean>

SessionFactory的配置还是照旧,使用以前的配置,只不过当前选择的数据源是datasource,也就是数据源选择的中间层MultipleDataSourceToChoose,因为当前的中间层中实现了DataSource这个接口,所以可以看做为DataSource的是实现类啦,所以配置不会出现问题。

简单的使用AOP进行测试一下,这里测试的结果时不同的,所以是生效的,使用了不同的数据源,但是底层的实现没有进行任何的修改处理。

@Service

@Slf4j

public class UserInfoService implements IUserInfoService {

@Resource

private UserDao userDao;

@Autowired

private CommonHibernateDao commonDao;

@TestValidateParam

public User getUserInfoById(Integer id) {

return userDao.findById(id);

}

@DynamicSwitchDataSource(dataSource = "datasource0")

public void save(User user) {

userDao.save(user);

}

@DynamicSwitchDataSource(dataSource = "datasource1")

public List<User> findAll(){

String sql = "select u.userName as name,u.userAge as age,u.userAddress as address,u.id from user u";

List<User> list =commonDao.findListBySQL(sql,User.class);

return list;

}

}

也可以不适用AOP,直接在编程中实现,通过测试,结果分别为两个数据库中的信息

public void test(){

HandlerDataSource.putDataSource("datasource1");

String sql = "select u.userName as name,u.userAge as age,u.userAddress as address,u.id from user u";

List<User> list =commonDao.findListBySQL(sql,User.class);

HandlerDataSource.putDataSource("datasource0");

commonDao.deleteById("2",User.class);

}

AbstractRoutingDataSource动态数据源切换的更多相关文章

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

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

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

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

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

    [参考]Spring(AbstractRoutingDataSource)实现动态数据源切换--转载 [参考] 利用Spring的AbstractRoutingDataSource解决多数据源的问题 ...

  4. @Transactional导致AbstractRoutingDataSource动态数据源无法切换的解决办法

    上午花了大半天排查一个多数据源主从切换的问题,记录一下: 背景: 项目的数据库采用了读写分离多数据源,采用AOP进行拦截,利用ThreadLocal及AbstractRoutingDataSource ...

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

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

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

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

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

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

  8. @Transactional导致无法动态数据源切换

    公司目前数据源为主从模式:主库可读写,从库只负责读.使用spring-jdbc提供的AbstractRoutingDataSource结合ThreadLocal存储key,实现数据源动态切换. 最近项 ...

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

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

随机推荐

  1. python-selenium自动化测试(火狐、谷歌、360浏览器启动)

    一.打开谷歌浏览器 import selenium from selenium import webdriver browser = webdriver.Chrome(executable_path ...

  2. 一个 PHP 面试题

    一个 PHP 面试题 $i = 0; $j =1; if ($i = 5 || ($j =6)) {echo $i,$j++;} 拿来当面试题不错. 实际并不会这样用,但这个题可以考基础.

  3. @NOIP2018 - D2T2@ 填数游戏

    目录 @题目描述@ @题解@ @代码@ @题目描述@ 小 D 特别喜欢玩游戏.这一天,他在玩一款填数游戏. 这个填数游戏的棋盘是一个 n×m 的矩形表格.玩家需要在表格的每个格子中填入一个数字(数字 ...

  4. MapReduce数据流-Reduce

  5. Best Open Source Software

    Best Open Source Software Open Source, Software, Top The promise of open source software is best qua ...

  6. MySQL 8.0 技术详解

    MySQL 8.0 简介 MySQL 5.7 到 8.0,Oracle 官方跳跃了 Major Version 版本号,随之而来的就是在 MySQL 8.0 上做了许多重大更新,在往企业级数据库的路上 ...

  7. poj 2689 Prime Distance (素数二次筛法)

    2689 -- Prime Distance 没怎么研究过数论,还是今天才知道有素数二次筛法这样的东西. 题意是,要求求出给定区间内相邻两个素数的最大和最小差. 二次筛法的意思其实就是先将1~sqrt ...

  8. Ubuntu 开机自动挂载磁盘

    Ubuntu 磁盘挂载 1.使用命令查看分区 sudo fdisk -l 1 可以根据上图提供的磁盘信息确定想挂载的磁盘,以及确定挂载的位置. 我此次的目的是将/dev/sda2 磁盘挂载到/mnt/ ...

  9. 2019-8-31-dotnet-将文件删除到回收站

    title author date CreateTime categories dotnet 将文件删除到回收站 lindexi 2019-08-31 16:55:58 +0800 2019-03-2 ...

  10. 数(aqnum)

    数(aqnum) 3.1 题目描述 秋锅对数论很感兴趣,他特别喜欢一种数字.秋锅把这种数字命名为 农数 ,英文名为 AQ number . 这种数字定义如下: 定义 1 一个数 n 是农数,当且仅当对 ...