最近一个项目用到了多个数据库,所以需要实现动态切换数据源来查询数据,http://www.cnblogs.com/lzrabbit/p/3750803.html这篇文章让我受益匪浅,提供了一种自动切换数据源的思路,但这种方式不支持事务,所以我进一步改进了这个方案,下面直入正题

多数据源配置:

#============================================================================
# DataBaseOne
#============================================================================
jdbc.one.driver=com.mysql.jdbc.Driver
jdbc.one.url=jdbc:mysql://127.0.0.1:3306/DataBaseOne?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.one.username=root
jdbc.one.password=root
============================================================================= #============================================================================
# DataBaseTwo
#============================================================================
jdbc.two.driver=com.mysql.jdbc.Driver
jdbc.two.url=jdbc:mysql://127.0.0.1:3306/DataBaseTwo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.two.username=root
jdbc.two.password=root
============================================================================= #============================================================================
# DataBaseThree
#============================================================================
jdbc.three.driver=com.mysql.jdbc.Driver
jdbc.three.url=jdbc:mysql://127.0.0.1:3306/DataBaseThree?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.three.username=root
jdbc.mysql.password=root
============================================================================= #============================================================================
# 通用配置
#============================================================================
jdbc.initialSize=5
jdbc.minIdle=5
jdbc.maxIdle=20
jdbc.maxActive=100
jdbc.maxWait=100000
jdbc.defaultAutoCommit=false
jdbc.removeAbandoned=true
jdbc.removeAbandonedTimeout=600
jdbc.testWhileIdle=true
jdbc.timeBetweenEvictionRunsMillis=60000
jdbc.numTestsPerEvictionRun=20
jdbc.minEvictableIdleTimeMillis=300000

Spring中使用多数据源:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 自己根据情况补全其他配置,以下只提供了数据源配置 --> <!-- 多数据源配置 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"/>
</bean>
<!-- 第一个数据源dataSourceOne -->
<bean id="dataSourceOne" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.one.driver}"/>
<property name="url" value="${jdbc.one.url}"/>
<property name="username" value="${jdbc.one.username}"/>
<property name="password" value="${jdbc.one.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxIdle" value="${jdbc.maxIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
<property name="maxWait" value="${jdbc.maxWait}"/>
<property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/>
<property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>
<property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
<property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}"/>
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
</bean>
<!-- 第二个数据源dataSourceTwo -->
<bean id="dataSourceTwo" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.two.driver}"/>
<property name="url" value="${jdbc.two.url}"/>
<property name="username" value="${jdbc.two.username}"/>
<property name="password" value="${jdbc.two.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxIdle" value="${jdbc.maxIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
<property name="maxWait" value="${jdbc.maxWait}"/>
<property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/>
<property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>
<property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
<property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}"/>
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
</bean>
<!-- 第三个数据源dataSourceThree -->
<bean id="dataSourceThree" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.three.driver}"/>
<property name="url" value="${jdbc.three.url}"/>
<property name="username" value="${jdbc.three.username}"/>
<property name="password" value="${jdbc.three.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxIdle" value="${jdbc.maxIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
<property name="maxWait" value="${jdbc.maxWait}"/>
<property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/>
<property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>
<property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
<property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}"/>
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
</bean>
<!-- 使用自己实现的数据源实现类MultipleDataSource,这个类随意放在哪个包下都行 -->
<bean id="multipleDataSource" class="com.cnblogs.datasource.MultipleDataSource">
<!-- 设置默认的数据源 -->
<property name="defaultTargetDataSource" ref="dataSourceOne"/>
<property name="targetDataSources">
<map>
<!-- 这个key是对应数据源的别称,通过这个key可以找到对应的数据源,value-ref就是上面数据源的id -->
<entry key="dataSourceOneKey" value-ref="dataSourceOne"/>
<entry key="dataSourceTwoKey" value-ref="dataSourceTwo"/>
<entry key="dataSourceThreeKey" value-ref="dataSourceThree"/>
</map>
</property>
</bean>
<!-- 让spring使用我们配置的多数据源 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="multipleDataSource"/>
</bean> <!-- mybatis.spring自动映射 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.cnblogs.mapper"/>
</bean> <!-- 自动扫描,多个包以 逗号分隔 -->
<context:component-scan base-package="com.cnblogs.**"/> <!-- AOP配置(事务控制) -->
<aop:config>
<!--pointcut元素定义一个切入点,execution中的第一个星号 用以匹配方法的返回类型, 这里星号表明匹配所有返回类型。 com.abc.service.*.*(..)表明匹配com.abc.service包下的所有类的所有方法 -->
<aop:pointcut id="myPointcut"
expression="execution(* com.cnblogs.service.*.*(..))" />
<!--将定义好的事务处理策略应用到上述的切入点 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
</aop:config>
</beans>

MultipleDataSource.java实现:

package com.cnblogs.datasource;

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

/** 多数据源java实现 */
public class MultipleDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>(); public static void setDataSourceKey(String dataSource) {
dataSourceKey.set(dataSource);
} @Override
protected Object determineCurrentLookupKey() {
return dataSourceKey.get();
}
}

下面详解使用SpringAOP来实现数据源切换,并支持事务控制

上面我们配置的AOP是针对service层的,所以在调用service层的任何方法时都会经过AOP,因此我们就在AOP中先于service调用时把数据源切换,看代码

新建类MultipleDataSourceAspectAdvice,把MultipleDataSourceAspectAdvice.java放在spring能自动注入的包中,比如controller包,或者自己把这个类所在的包加入到component-scan配置中去,总之要要让spring能自动加载

package com.cnblogs.controller;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import com.cnblogs.datasource.MultipleDataSource;
import com.cnblogs.service.DataBaseOne;
import com.cnblogs.service.DataBaseTwo;
import com.cnblogs.service.DataBaseThree; /**
* 数据库链接自动切换AOP处理
* Order优先级设置到最高,因为在所有service方法调用前都必须把数据源确定
* Order数值越小优先级越高
*/
@Component
@Aspect
@Order(1)
public class MultipleDataSourceAspectAdvice {
private static final Logger LOGGER = Logger.getLogger(MultipleDataSourceAspectAdvice.class); public MultipleDataSourceAspectAdvice() {
LOGGER.info("MultipleDataSourceAspectAdvice 加载成功");
} /**
* 定义切面
*/
@Pointcut("execution(* com.cnblogs.service.*.*(..))")
public void pointCut() {
} // dataSourceOneKey
// dataSourceTwoKey
// dataSourceThreeKey
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint jp) throws Throwable {
if (jp.getTarget() instanceof DataBaseOne) {
LOGGER.debug("使用数据库链接:dataSourceOneKey");
MultipleDataSource.setDataSourceKey("dataSourceOneKey");
} else if (jp.getTarget() instanceof DataBaseTwo) {
LOGGER.debug("使用数据库链接:dataSourceTwoKey");
MultipleDataSource.setDataSourceKey("dataSourceTwoKey");
} else if (jp.getTarget() instanceof DataBaseThree) {
LOGGER.debug("使用数据库链接:dataSourceThreeKey");
MultipleDataSource.setDataSourceKey("dataSourceThreeKey");
} else {
// 默认是dataSourceOneKey
LOGGER.debug("使用数据库链接:dataSourceOneKey");
MultipleDataSource.setDataSourceKey("dataSourceOneKey");
}
return jp.proceed();
}
}

到这里我们所以的spring多数据源配置已经完毕,那如何在执行service方法时让service切换到正确的数据库呢?上面的类中定义了有3个类DataBaseOne,DataBaseTwo,DataBaseThree,这3个类其实只是一个interface,没有任何实现方法,我们让具体业务的service都继承至这3个类以区分不同的service对应不同的数据源,因为业务的service我是知道他用的哪个数据源的,比如FooService继承至DataBaseOne,则在使用FooService任何方法时AOP就会先把数据源切换到dataSourceOneKey,以此就达到了自动切换数据源的目的,并且支持事务,下面看代码:

package com.cnblogs.service;

/**
* DataBaseOne数据库占位类
* 详情参见 MultipleDataSourceAspectAdvice 类
*/
public interface DataBaseOne { }

DataBaseTwo,DataBaseThree与这完全相同

至此多数据源就配置完毕,可以安心写业务逻辑了

原理部分博友http://www.cnblogs.com/lzrabbit/p/3750803.html讲的十分清楚,再次感谢

Spring+Mybatis多数据源的一种实现方式,支持事务的更多相关文章

  1. spring 整合 mybatis 中数据源的几种配置方式

    因为spring 整合mybatis的过程中, 有好几种整合方式,尤其是数据源那块,经常看到不一样的配置方式,总感觉有点乱,所以今天有空总结下. 一.采用org.mybatis.spring.mapp ...

  2. MyBatis配置数据源的两种方式

    ---------------------siwuxie095                                     MyBatis 配置数据源的两种方式         1.配置方 ...

  3. C# DataGridView绑定数据源的几种常见方式

    开始以前,先认识一下WinForm控件数据绑定的两种形式,简单数据绑定和复杂数据绑定. 1. 简单的数据绑定 例1 using (SqlConnection conn = new SqlConnect ...

  4. spring+mybatis多数据源切换

    在实际的公司项目中,很可能会遇到一个问题就是,一个java项目,但是项目中涉及两个数据库,这两个数据库还在不同IP的机子上. 遇到这种情况的时候,我们有两个选择 1.不走spring的aop方式,直接 ...

  5. spring+mybatis多数据源动态切换

    spring mvc+mybatis+多数据源切换 选取oracle,mysql作为例子切换数据源.oracle为默认数据源,在测试的action中,进行mysql和oracle的动态切换. web. ...

  6. Spring + MyBatis 多数据源实现

    近期,在项目中需要做分库,但是因为某些原因,没有采用开源的分库插件,而是采用了同事之前弄得多数据源形式实现的分库.对于多数据源,本人在实际项目也中遇到的不多,之前的项目大多是服务化,以RPC的形式获得 ...

  7. 读书笔记——spring cloud 中 HystrixCommand的四种执行方式简述

    读了<Spring Cloud 微服务实战>第151-154页, 总结如下: Hystrix存在两种Command,一种是HystrixCommand,另一种是HystrixObserva ...

  8. Spring随笔 —— IOC配置的三种不同方式简介

    在spring framework中,IOC的配置是最基础的部分,常见的配置方式有基于xml文件和基于注解的配置方式.除了这两种配置方式之外,今天这里再介绍另一种配置方式,先用小demo重温下我们熟悉 ...

  9. Spring的核心api和两种实例化方式

    一.spring的核心api Spring有如下的核心api BeanFactory :这是一个工厂,用于生成任意bean.采取延迟加载,第一次getBean时才会初始化Bean Applicatio ...

随机推荐

  1. 69个Spring面试题

    Spring 概述 1. 什么是spring? Spring 是个java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring ...

  2. 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试

    . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...

  3. Cocos2d中update与fixedUpdate的区别(五)

    在真实情况中update:和fixedUpdate方法如何去调用? 由上所述,所以update方法在每帧被调用1次,从而给你一个机会去更新你的游戏对象的状态在其绘制之前.而fixedUpdate:方法 ...

  4. 【Visual C++】游戏编程学习笔记之五:单一背景滚动

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...

  5. 尽量用pass-by-reference-to-const(const引用)替换pass-by-value(传值)

    默认情况下C++以pass-by-value传递对象至函数(或从函数返回). eg1: class Person { public: Person(); virtual ~Person(); priv ...

  6. “《编程珠玑》(第2版)第2章”:C题(查找变位词,排序)

    C题是这样子的: 给定一个英语字典,找出其中的所有变位词集合.例如,“pots”.“stop”和“tops”互为变位词,因为每一个单词都可以通过改变其他单词中字母的顺序来得到. 下段分析摘自该书(P1 ...

  7. C++模板总结

    在编写含有模板的程序的时候,我还是按照一个头文件声明,一个源文件的方法来组织,结果编译的时候总出现一些很奇怪的语法问题,但程序明明是没有问题的.后来经过查阅才知道原来是因为C++编译器不支持对模板的分 ...

  8. Netmask, 子网与 CIDR (Classless Interdomain Routing)

    Netmask, 子网与 CIDR (Classless Interdomain Routing) 我们前面谈到 IP 是有等级的,而设定在一般计算机系统上面的则是 Class A, B, C.现在我 ...

  9. 【编程练习】收集的一些c++代码片,算法排序,读文件,写日志,快速求积分等等

    写日志: class LogFile { public: static LogFile &instance(); operator FILE *() const { return m_file ...

  10. mybatis源码之PreparedStatementHandler

    /** * @author Clinton Begin */ public class PreparedStatementHandler extends BaseStatementHandler { ...