AbstractRoutingDataSource 实现动态切换数据源
扩展AbstractRoutingDataSource类
package com.datasource.test.util.database; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /**
* 获取数据源(依赖于spring)
*/
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
}
DataSourceHolder这个类则是我们自己封装的对数据源进行操作的类:
package com.datasource.test.util.database; /**
* 数据源操作
*/
public class DataSourceHolder {
//线程本地环境
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
//设置数据源
public static void setDataSource(String customerType) {
dataSources.set(customerType);
}
//获取数据源
public static String getDataSource() {
return (String) dataSources.get();
}
//清除数据源
public static void clearDataSource() {
dataSources.remove();
} }
在注解中使用
@DataSource(name=DataSource.slave1)
public List getProducts(){}
注解定义:
package com.datasource.test.util.database;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default DataSource.master;
public static String master = "dataSource1";
public static String slave1 = "dataSource2";
public static String slave2 = "dataSource3";
}
定义拦截器,拦截DataSource的值
public class DataSourceAspect {
/**
* 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
*
* @param point
* @throws Exception
*/
public void intercept(JoinPoint point) throws Exception {
Class<?> target = point.getTarget().getClass();
MethodSignature signature = (MethodSignature) point.getSignature();
// 默认使用目标类型的注解,如果没有则使用其实现接口的注解
for (Class<?> clazz : target.getInterfaces()) {
resolveDataSource(clazz, signature.getMethod());
}
resolveDataSource(target, signature.getMethod());
}
/**
* 提取目标对象方法注解和类型注解中的数据源标识
*
* @param clazz
* @param method
*/
private void resolveDataSource(Class<?> clazz, Method method) {
try {
Class<?>[] types = method.getParameterTypes();
// 默认使用类型注解
if (clazz.isAnnotationPresent(DataSource.class)) {
DataSource source = clazz.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
// 方法注解可以覆盖类型注解
Method m = clazz.getMethod(method.getName(), types);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource source = m.getAnnotation(DataSource.class);
DynamicDataSourceHolder.setDataSource(source.value());
}
} catch (Exception e) {
System.out.println(clazz + ":" + e.getMessage());
}
}
}
spring 中配置拦截器:
<bean id="dataSourceAspect" class="com.test.context.datasource.DataSourceAspect" />
<aop:config>
<aop:aspect ref="dataSourceAspect">
<!-- 拦截所有service方法 -->
<aop:pointcut id="dataSourcePointcut" expression="execution(* com.test.*.dao.*.*(..))"/>
<aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
</aop:aspect>
</aop:config>
</bean>
多数据源配置:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring 数据库相关配置 放在这里 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <bean id = "dataSource1" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
<property name="url" value="${db1.url}"/>
<property name = "user" value = "${db1.user}"/>
<property name = "password" value = "${db1.pwd}"/>
<property name="autoReconnect" value="true"/>
<property name="useUnicode" value="true"/>
<property name="characterEncoding" value="UTF-8"/>
</bean> <bean id = "dataSource2" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
<property name="url" value="${db2.url}"/>
<property name = "user" value = "${db2.user}"/>
<property name = "password" value = "${db2.pwd}"/>
<property name="autoReconnect" value="true"/>
<property name="useUnicode" value="true"/>
<property name="characterEncoding" value="UTF-8"/>
</bean> <bean id = "dataSource3" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
<property name="url" value="${db3.url}"/>
<property name = "user" value = "${db3.user}"/>
<property name = "password" value = "${db3.pwd}"/>
<property name="autoReconnect" value="true"/>
<property name="useUnicode" value="true"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
<!-- 配置多数据源映射关系 -->
<bean id="dataSource" class="com.datasource.test.util.database.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSource1" value-ref="dataSource1"></entry>
<entry key="dataSource2" value-ref="dataSource2"></entry>
<entry key="dataSource3" value-ref="dataSource3"></entry>
</map>
</property>
<!-- 默认目标数据源为你主库数据源 -->
<property name="defaultTargetDataSource" ref="dataSource1"/>
</bean> <bean id="sessionFactoryHibernate" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">com.datasource.test.util.database.ExtendedMySQLDialect</prop>
<prop key="hibernate.show_sql">${SHOWSQL}</prop>
<prop key="hibernate.format_sql">${SHOWSQL}</prop>
<prop key="query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
<prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
<prop key="hibernate.c3p0.max_size">30</prop>
<prop key="hibernate.c3p0.min_size">5</prop>
<prop key="hibernate.c3p0.timeout">120</prop>
<prop key="hibernate.c3p0.idle_test_period">120</prop>
<prop key="hibernate.c3p0.acquire_increment">2</prop>
<prop key="hibernate.c3p0.validate">true</prop>
<prop key="hibernate.c3p0.max_statements">100</prop>
</props>
</property>
</bean> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactoryHibernate"/>
</bean> <bean id="dataSourceExchange" class="com.datasource.test.util.database.DataSourceExchange"/> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryHibernate"/>
</bean> <tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="add*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="update*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="modify*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="edit*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="del*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="save*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="send*" propagation="NESTED" rollback-for="Exception"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="search*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="count*" read-only="true"/>
</tx:attributes>
</tx:advice> <aop:config>
<aop:pointcut id="service" expression="execution(* com.datasource..*.service.*.*(..))"/>
<!-- 关键配置,切换数据源一定要比持久层代码更先执行(事务也算持久层代码) -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="service" order="2"/>
<aop:advisor advice-ref="dataSourceExchange" pointcut-ref="service" order="1"/>
</aop:config> </beans>
PS: 以上是从两个地址复制的代码,不作准确性验证,但思路就是这样。
AbstractRoutingDataSource 实现动态切换数据源的更多相关文章
- 基于AbstractRoutingDataSource实现动态切换数据源
基于AbstractRoutingDataSource实现动态切换数据源 /** * DataSource注解接口 */ @Target({ElementType.TYPE, ElementTyp ...
- Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法
一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...
- hibernate动态切换数据源
起因: 公司的当前产品,主要是两个项目集成的,一个是java项目,还有一个是php项目,两个项目用的是不同的数据源,但都是mysql数据库,因为java这边的开发工作已经基本完成了,而php那边任务还 ...
- 在使用 Spring Boot 和 MyBatis 动态切换数据源时遇到的问题以及解决方法
相关项目地址:https://github.com/helloworlde/SpringBoot-DynamicDataSource 1. org.apache.ibatis.binding.Bind ...
- Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源方法
一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...
- Spring学习总结(16)——Spring AOP实现执行数据库操作前根据业务来动态切换数据源
深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...
- Spring+Mybatis动态切换数据源
功能需求是公司要做一个大的运营平台: 1.运营平台有自身的数据库,维护用户.角色.菜单.部分以及权限等基本功能. 2.运营平台还需要提供其他不同服务(服务A,服务B)的后台运营,服务A.服务B的数据库 ...
- Spring动态切换数据源及事务
前段时间花了几天来解决公司框架ssm上事务问题.如果不动态切换数据源话,直接使用spring的事务配置,是完全没有问题的.由于框架用于各个项目的快速搭建,少去配置各个数据源配置xml文件等.采用了动态 ...
- AOP获取方法注解实现动态切换数据源
AOP获取方法注解实现动态切换数据源(以下方式尚未经过测试,仅提供思路) ------ 自定义一个用于切换数据源的注解: package com.xxx.annotation; import org. ...
随机推荐
- easyui的datagrid改变整行颜色
easyui的datagrid改变单元格颜色方法1:https://www.cnblogs.com/raitorei/p/10395233.html easyui的datagrid改变单元格颜色方法2 ...
- LAB6 SOAP
有web服务的,需要Deploy一下才能跑 通过ls看所有文件的所在地,cd进入对应文件夹,才可以编译 javac 编译,Java是执行 URL必须是WSDL文件点进去里面的:http://local ...
- 转载:如何优雅的实现INotifyPropertyChanged接口
转载来源:http://www.cnblogs.com/TianFang/p/6240933.html 如何优雅的实现INotifyPropertyChanged接口 INotifyPropertyC ...
- python 12 模块与包
一.不知道什么原则 python文件下面只写方法,所有的可直接执行的代码,都放在条件下 原因.该文件有可能被其他文件调用 二.eval() 将字符串转为相应的数据格式可以想象json转为map 三.模 ...
- 《Java并发编程的艺术》Java并发机制的底层实现原理(二)
Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...
- 【java】:多线程面试题
经常面试的时候,让写各种乱七八糟的多线程面试题,收集了很多,有些还是挺好玩的. 1.编写程序实现,子线程循环10次,接着主线程循环20次,接着再子线程循环10次,主线程循环20次,如此反复,循环50次 ...
- join查询优化
更新使用过滤条件中包括自身的表 此方法不能无法在mysql中使用 `UPDATE user1 SET over='齐天大圣' WHERE user1.user_name IN ( SELECT b.u ...
- 编译安装php5.6
sudo yum -y install curl-devel bzip2 bzip2-devel libxml2 libxml2-devel libjpeg libpng freetype li ...
- lumen框架学习01——引入自定义类和函数
引入自定义的functions.php文件,首先把functions.php文件放在app的目录下,然后通过根目录的composer.json文件引入,具体操作如下图: 引入类文件也是一样,具体可参考 ...
- RQNOJ PID51 / 乒乓球 ☆
因为是多行输入,所以用了getchar()进行输入,题目没有说明数据范围,所以开始的时候因为数组开的不够大,WA90了一次,我之前开了10000的长度,之后开100000的长度跑过了 一个基本的模拟, ...