操作数据一般都是在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基础:05集合类型

    Python中,集合对象是一组无序排列的可哈希的值.所以集合成员可以做字典中的键.集合中的元素都是唯一的. 集合(sets)有两种不同的类型,可变集合(set) 和 不可变集合(frozenset). ...

  2. python基础---集合类型(Sets)

    集合类型(Sets) 集合对象是不同的(不可重复)hashable对象的无序集合.常见用法包括:成员关系测试.移除序列中的重复.以及科学计算,例如交集.并集.差分和对称差分.通俗点来说,集合是一个无序 ...

  3. 全面理解Python中的类型提示(Type Hints)

    众所周知,Python 是动态类型语言,运行时不需要指定变量类型.这一点是不会改变的,但是2015年9月创始人 Guido van Rossum 在 Python 3.5 引入了一个类型系统,允许开发 ...

  4. canvas实现碰壁反弹(单个小方块)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. 快速理解bootstrap,bagging,boosting,gradient boost-三个概念

      1 booststraping:意思是依靠你自己的资源,称为自助法,它是一种有放回的抽样方法,它是非参数统计中一种重要的估计统计量方差进而进行区间估计的统计方法. 其核心思想和基本步骤如下: (1 ...

  6. dotnet 设计规范 · 结构体定义

    X 不要给结构体默认构造函数 默认的C#编译器也不让开发者定义有默认构造的结构体 X 不要定义易变的属性 易变的属性指的是在调用属性返回值的时候返回的是新的实例,易变的属性会有很多的问题. ✓ 需要确 ...

  7. java 集合之HashMap的三种遍历

    HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. 这周我们只需记住三种遍历方法 1.通过keySet()获取键,再利用hashmap里面的.get(key)方法通过键获取 ...

  8. Python--day41--threading中的定时器Timer

    定时器Timer:定时开启线程 代码示例: #定时开启线程 import time from threading import Timer def func(): print('时间同步') #1-3 ...

  9. SpringBoot集成thymeleaf(自定义)模板中文乱码的解决办法

    楼主今天在学习SpringBoot集成thymelaf的时候报了中文乱码的错误,经过网上的搜索,现在得到解决的办法,分享给大家: package com.imooc.config; import or ...

  10. fatal: Not a git repository (or any of the parent directories)

    当从github.com上面下载下了Firmware后,无意中删除了Firmware目录下的.git文件夹,再去编译就会出现:   fatal: Not a git repository (or an ...