spring+mybatis多数据源动态切换

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

web.xml

<context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>trac</param-value>
    </context-param>  

    <!-- Spring的log4j监听器 -->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>  

    <!-- 字符集 过滤器 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>  

    <!-- Spring view分发器 -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/dispatcher.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>  

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener> 

applicationContext.xml

<bean id="parentDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    </bean>  

    <bean id="mySqlDataSource" parent="parentDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>  

    <bean id="oracleDataSource" parent="parentDataSource">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
        <property name="url" value="jdbc:oracle:thin:@10.16.17.40:1531:addb"></property>
        <property name="username" value="trac"></property>
        <property name="password" value="trac"></property>
    </bean>  

    <bean id="dataSource" class="com.trac.dao.datasource.DataSources">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry value-ref="mySqlDataSource" key="MYSQL"></entry>
                <entry value-ref="oracleDataSource" key="ORACLE"></entry>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="oracleDataSource"></property>
    </bean>  

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>  

    <!-- 创建SqlSessionFactory,同时指定数据源和mapper -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath*:com/trac/ibatis/dbcp/*.xml" />
    </bean>  

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>  

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.trac.dao" />
    </bean> 

配置 parentDataSource 的父bean.再配置多个数据源继承这个父bean,对driverClass,url,username,password,等数据源连接参数进行各自的重写。例如 mySqlDataSource ,在 DataSources bean中注入所有要切换的数据源,并且设置默认的数据源。

DataSourceInstances.java

public class DataSourceInstances{
    public static final String MYSQL="MYSQL";
    public static final String ORACLE="ORACLE";
}

DataSourceSwitch.java

public class DataSourceSwitch{
    private static final ThreadLocal contextHolder=new ThreadLocal();  

    public static void setDataSourceType(String dataSourceType){
        contextHolder.set(dataSourceType);
    }  

    public static String getDataSourceType(){
        return (String) contextHolder.get();
    }  

    public static void clearDataSourceType(){
        contextHolder.remove();
    }
}

  

DataSources.java

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

public class DataSources extends AbstractRoutingDataSource{  

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceSwitch.getDataSourceType();
    }  

}

  

测试

@Controller
@SuppressWarnings("unused")
public class TestAction {
@Autowired
    TestMapper testMapper;  

   @RequestMapping("/test.action")
    public ModelAndView test(
            HttpServletRequest request,
            HttpServletResponse resp){
        ModelAndView model = new ModelAndView("test");
        model.addObject("test1", "这是一个测试,获取默认数据连接MYSQL:"+testMapper.test());
        DataSourceSwitch.setDataSourceType(DataSourceInstances.ORACLE);
        model.addObject("test2", "这是一个测试,获取数据连接ORACLE:"+testMapper.test());
        DataSourceSwitch.setDataSourceType(DataSourceInstances.MYSQL);
        model.addObject("test3", "这是一个测试,获取数据连接MYSQL:"+testMapper.test());
        return model;
    }
}

代码解释:

查看AbstractRoutingDataSource中的获取数据库连接源码

    public Connection getConnection()
        throws SQLException
    {
        return determineTargetDataSource().getConnection();
    }

查看determineTargetDataSource方法

protected DataSource determineTargetDataSource()
    {
        Assert.notNull(resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = (DataSource)resolvedDataSources.get(lookupKey);
        if(dataSource == null && (lenientFallback || lookupKey == null))
            dataSource = resolvedDefaultDataSource;
        if(dataSource == null)
            throw new IllegalStateException((new StringBuilder()).append("Cannot determine target DataSource for lookup key [").append(lookupKey).append("]").toString());
        else
            return dataSource;
    }

其中DataSource dataSource = (DataSource)resolvedDataSources.get(lookupKey); 中的resolvedDataSources 就是我们spring中设置的targetDataSources,是一个Map类型,里面有我们设置的MYSQL和ORACLE数据库连接池

注意determineCurrentLookupKey方法,

protected abstract Object determineCurrentLookupKey();

是一个抽象方法,需要我们去实现,我们将数据源对应的KEY放在本地线程中,那么可以随时在代码中进行切换数据源

默认数据源

在spring配置文件中,我们将defaultTargetDataSource注入到AbstractRoutingDataSource中

public void afterPropertiesSet()
    {
        if(targetDataSources == null)
            throw new IllegalArgumentException("Property ‘targetDataSources‘ is required");
        resolvedDataSources = new HashMap(targetDataSources.size());
        Object lookupKey;
        DataSource dataSource;
        for(Iterator iterator = targetDataSources.entrySet().iterator(); iterator.hasNext(); resolvedDataSources.put(lookupKey, dataSource))
        {
            java.util.Map.Entry entry = (java.util.Map.Entry)iterator.next();
            lookupKey = resolveSpecifiedLookupKey(entry.getKey());
            dataSource = resolveSpecifiedDataSource(entry.getValue());
        }

        if(defaultTargetDataSource != null)
            resolvedDefaultDataSource = resolveSpecifiedDataSource(defaultTargetDataSource);
    }

AbstractRoutingDataSource类实现了InitializingBean接口,项目启动会实现方法afterPropertiesSet,生成resolvedDefaultDataSource实例,这样在determineTargetDataSource方法中如果获取本地线程变量中的连接位空,那么就选择默认数据源。

  

时间: 2024-08-10 19:18:44

spring+mybatis多数据源动态切换的相关文章

Spring + Mybatis 项目实现动态切换数据源

项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库. 最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法. 参考了两篇文章如下: http://blog.csdn.net/zl3450341/article/details/20150687 http://www.blogjava.net/hoojo/archive/2013/10/22/405488.html 这两篇文章都对原理进行了分析,下面只写自己

基于spring的aop实现多数据源动态切换

https://lanjingling.github.io/2016/02/15/spring-aop-dynamicdatasource/ 基于spring的aop实现多数据源动态切换 发表于 2016-02-15   |   分类于 spring  | 一.多数据源动态切换原理 项目中我们经常会遇到多数据源的问题,尤其是数据同步或定时任务等项目更是如此:又例如:读写分离数据库配置的系统. 1.多数据源设置: 1)静态数据源切换:一般情况下,我们可以配置多个数据源,然后为每个数据源写一套对应的

springboot多数据源动态切换和自定义mybatis件分页插

1.配置多数据源 增加druid依赖 完整pom文件 数据源配置文件 route.datasource.driver-class-name= com.mysql.jdbc.Driver route.datasource.url= jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 route.datasource.username= root route.datasource.password= 1234

基于Struts2 Spring ibatis Oracle10g架构 多数据源动态切换实例

一.概述 基于Spring动态配置多数据源,在大型的应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效的提高系统的水平伸缩性,而这样的解决方案就会补同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求以及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据. Spring配置多个数据源的方式和具体使用过程,Spring对于多数据源,以数据库表为参照,大体上可以分为两大类情况: 1.表级上的跨数据库,即对于不同的数据库却有不相同的表(表名和表结构完全

spring 数据源动态切换 与dubbo服务

1:问题描述 项目用了spring数据源动态切换,服务用的是dubbo.在运行一段时间后程序异常,更新操作没有切换到主库上.这个问题在先调用读操作后再调用写操作会出现.经分析原因有3: 第一:当程序运行一段时间后调用duboo服务时,读操作与写操作有可能会在一个线程里,当这种情况出现时 2:数据源配置 <idclass>    <name>       <key-type>          <keyvalue-ref/>          <keyv

Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法(转)

一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基本上没有什么问题,但唯一可能出现问题的就是在hibernate做添加操作生成主键策略的时候.因为我们都知道hibernate的数据库本地方言会针对不同的数据库采用不同的主键生成策略. 所以针对这一问题不得不采用自定义的主键生成策略,自己写一个主键生成器的表来维护主键生成方式或以及使用其他的方式来生成

koala 多数据源动态切换

koala 特点:多数据源动态切换,数据源分组和负载均衡(轮询) 项目源码以及demo:https://github.com/zeq9069/koala 请大家关注一下哦!!哈哈! 最近,在开发项目的时候后,要用到多个数据源的动态切换,于是就开发了koala 这个小小得框架,koala支持多数据源的切换,数据源的分组和负载均衡(轮询)! 只需要将jar引入到你自己的项目中之后,然后进行简单配置就可以使用了!!使用起来 非常方便,该项目提供了三个核心的注解@ChangeTo,@DataSource

spring + mybatis + 多数据源整合事务

1.核心思想,spring提供了一个DataSource的子类,该类支持多个数据源 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 该类的源码如下: org.springframework.jdbc.datasource.lookup; java.sql.Connection; java.sql.SQLException; java.util.HashMap; java.util.Iterator; jav

IT忍者神龟之Spring+MyBatis多数据源配置实现

最近用到了MyBatis配置多数据源,原以为简单配置下就行了,实际操作后发现还是要费些事的,这里记录下,以作备忘 不多废话,直接上代码,后面会有简单的实现介绍 jdbc和log4j的配置 #定义输出格式 ConversionPattern=%d %-5p [%t] %c - %m%n log4j.rootLogger=DEBUG,Console log4j.logger.com.cnblogs.lzrabbit=DEBUG log4j.logger.org.springframework=ERR