`

spring框架多数据源切换问题的解决

阅读更多

 Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。

Spring配置多数据源的方式和具体使用过程。 
Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况: 
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。 
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。

1、根据用户的选择,使用不同的数据源。

2、解决思路锁定:将sessionFactory的属性dataSource设置成不同的数据源,以达到切换数据源的目的。

3、问题产生:因为整个项目用的几乎都是单例模式,当多个用户并发访问数据库的时候,会产生资源争夺的问题。即项目启动时候,所有的bean都被装载到内存,并且每个bean都只有一个对象。正因为只有一个对象,所有的对象属性就如同静态变量(静态变量跟单例很相似,常用静态来实现单例)。整个项目系统的dataSource只有一个,如果很多用户不断的去改变dataSource的值,那必然会出现资源的掠夺问题,造成系统隐患。

4、多资源共享解决思路:同一资源被抢夺的时候,通常有两种做法,a、以时间换空间 b、以空间换时间。

5、线程同步机制就是典型的“以时间换空间”,采用排队稍等的方法,一个个等待,直到前面一个用完,后面的才跟上,多人共用一个变量,用synchronized锁定排队。   

6、ThreadLocal”就是典型的“以空间换时间”,她可以为每一个人提供一份变量,因此可以同时访问并互不干扰。

7、言归正传:sessionFactory的属性dataSource设置成不用的数据源,首先不能在配置文件中写死,我们必须为她单独写一个类,让她来引用这个类,在这个类中再来判断我们到底要选择哪个数据源。

 

spring + mybatis 多数据源切换

DbContextHolder.java

package com.easyway.stage.commons;

public class DbContextHolder
{

    // ThreadLocal是线程安全的,并且不能在多线程之间共享。
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static void setDbType(String dbType)
    {
        contextHolder.set(dbType);
    }

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

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

}

MultiDataSource.java

package com.easyway.stage.commons;

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

public class MultiDataSource extends AbstractRoutingDataSource
{

    @Override
    protected Object determineCurrentLookupKey()
    {
        return  DbContextHolder.getDbType();
    }

}

applicationContext.xml

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="       
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.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
      http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
      	
	<context:annotation-config/>

	<!-- 数据源 -->  
    <bean id="parentDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="1" />
        <property name="maxActive" value="20" /> 
        <property name="minIdle" value="1" />
        
        <!-- 配置获取连接等待超时的时间60s -->
        <property name="maxWait" value="60000" /> 
        
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
        <property name="minEvictableIdleTimeMillis" value="300000" />  
       
        <property name="validationQuery" value="SELECT 'x'" />  
        <property name="testWhileIdle" value="true" />  
        <property name="testOnBorrow" value="false" />  
        <property name="testOnReturn" value="false" />  
        
        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
        
        <!-- 配置监控统计拦截的filters -->
        <property name="filters" value="wall,stat,slf4j" />
        
        <!-- 对于长时间不使用的连接强制关闭  -->
        <property name="removeAbandoned" value="true" />
        <!-- 超过30分钟开始关闭空闲连接 -->
        <property name="removeAbandonedTimeout" value="1800" /> 
        <!-- 关闭abanded连接时输出错误日志 --> 
        <property name="logAbandoned" value="true" />  
    </bean>  
    <bean id="local" parent="parentDataSource" init-method="init" destroy-method="close">    
        <property name="url" value="${local.jdbc.url}" />  
        <property name="username" value="${local.jdbc.username}" />  
        <property name="password" value="${local.jdbc.password}" />    
    </bean>
    <bean id="server" parent="parentDataSource" init-method="init" destroy-method="close">    
        <property name="url" value="${jdbc.url}" />  
        <property name="username" value="${jdbc.username}" />  
        <property name="password" value="${jdbc.password}" />    
    </bean>

	<bean id="dataSource" class="com.autrade.stage.commons.MultiDataSource">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry value-ref="local" key="local"></entry>
				<entry value-ref="server" key="server"></entry>
			</map>
		</property>
		<!-- 默认使用server的数据源 -->
		<property name="defaultTargetDataSource" ref="server"></property>
	</bean>  

	<!-- MyBatis -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	  	<property name="dataSource" ref="dataSource" />
	  	<property name="configLocation" value="classpath:resources/mybatis/myBatisConfig.xml" />
	  	<property name="mapperLocations" value="classpath:resources/mybatis/mapper/*.xml"/>
	</bean>
	<bean class="org.mybatis.spring.SqlSessionTemplate">
      <constructor-arg ref="sqlSessionFactory"/>
    </bean>
	<!-- MyBatis -->
	
	<!-- 配置事务管理对象-->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<!-- 将所有具有@Transactional注解的Bean自动配置为声明式事务支持 -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

	<!-- 自定义的拦截器 -->
	<bean id="methodAdvisor" class="com.easyway.app.interceptor.InjectorManager" />

	<aop:config proxy-target-class="true">
		<aop:pointcut id="baseMethods"
			expression="execution(* com.easyway.app.service..*.*(..))" />
		<aop:advisor advice-ref="methodAdvisor" pointcut-ref="baseMethods" />
	</aop:config>
      
</beans>

 

Test.java测试类

package com.easyway.stage.test;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import com.easyway.stage.commons.DbContextHolder;

public class Test
{

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        ApplicationContext appContext = new ClassPathXmlApplicationContext("client-beans.xml");

        DbContextHolder.setDbType("local");
        String res = "resources/mybatis/myBatisConfig.xml";
        DataSource datasource = (DataSource) appContext.getBean("dataSource");

        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        Resource resource = new FileSystemResource(res);
        bean.setConfigLocation(resource);
        try
        {
            SqlSessionFactory sessionfactory = bean.getObject();
            SqlSession session = sessionfactory.openSession();
            User user = session.selectOne("com.easyway.mybatis.mapper.findOne");
            System.out.println(user.getName());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        DbContextHolder.setDbType("server");
        String res1 = "resources/mybatis/myBatisConfig.xml";
        DataSource datasource1 = (DataSource) appContext.getBean("dataSource");

        SqlSessionFactoryBean bean1 = new SqlSessionFactoryBean();
        bean1.setDataSource(datasource1);
        Resource resource1 = new FileSystemResource(res1);
        bean1.setConfigLocation(resource1);

        try
        {
            SqlSessionFactory sessionfactory = bean.getObject();
            SqlSession session = sessionfactory.openSession();
            User user = session.selectOne("com.easyway.mybatis.mapper.findOne");
            System.out.println(user.getName());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

    }

}

    

分享到:
评论

相关推荐

    Springcloud 多数库 多数据源整合,查询动态切换数据库

    创建一个自定义的数据源切换注解,比如`@SwitchDataSource`,并在需要切换数据源的方法上使用。通过AspectJ的切面处理,我们可以在方法执行前后动态改变ThreadLocal中的数据源引用。 3. **Spring Cloud Config ...

    spring多数据源动态切换方案

    在企业级应用开发中,Spring框架的广泛使用使得...通过这种方式,我们可以灵活地处理复杂的数据源切换场景,适应不同业务的需求。在实际项目中,还需要考虑线程安全、异常处理、测试等方面的问题,确保系统稳定运行。

    spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制

    在现代企业级应用开发中,多数据源切换和分布式事务管理是常见的需求,尤其是在大型分布式系统中。Spring框架因其强大的依赖注入和AOP(面向切面编程)特性,成为Java领域首选的轻量级框架。Druid是一个优秀的数据库...

    SpringBoot配置多数据源实现动态切换数据源

    本文将深入探讨如何在SpringBoot项目中配置多数据源,并实现数据源的动态切换,帮助你理解和掌握这一核心技能。 首先,我们理解"多数据源"的概念。在SpringBoot应用中,多数据源意味着系统能够连接并操作多个不同的...

    spring 动态切换数据源

    在Spring框架中,动态切换数据源是一项重要的功能,它允许应用程序根据业务需求在多个数据库之间灵活切换。这一特性对于多租户系统、读写分离、分布式数据库等场景尤其有用。以下将详细介绍如何实现Spring的动态数据...

    Spring动态切换多数据源Demo

    在Spring框架中,多数据源的支持使得应用可以同时连接并操作多个不同的数据库,这在分布式系统、微服务架构或者需要处理多种数据类型的场景下非常有用。"Spring动态切换多数据源Demo"是一个示例项目,它展示了如何在...

    mybatis+spring实现动态切换数据源

    通过以上步骤,我们就实现了MyBatis与Spring配合下的动态数据源切换。这种机制有助于在多租户系统、读写分离或者高可用架构中灵活地管理数据库访问,提高了系统的可扩展性和灵活性。在实际应用中,还需要考虑数据源...

    如何在spring框架中解决多数据源的问题

    通过上述步骤,我们可以在Spring框架中实现多数据源的支持,并且通过装饰者模式实现了数据源的动态切换,解决了单例模式下的数据源争用问题。这种方法不仅提高了系统的灵活性,还增强了系统的可扩展性和可维护性。在...

    Mybatis+Spring+SpringMVC+quartz多数据源切换

    "Mybatis+Spring+SpringMVC+quartz多数据源切换"的架构设计就是为了满足这样的场景。这个项目结合了四个关键的技术组件,它们分别是Mybatis、Spring、SpringMVC和Quartz,下面将详细介绍这些技术以及它们在多数据源...

    springboot-AOP实现多数据源动态切换(Druid连接池)

    1. **Spring AOP(面向切面编程)**:AOP是Spring框架的核心特性之一,它允许我们定义方法拦截器或切面来跨多个点应用横切关注点。在这里,我们使用AOP来在运行时动态改变数据源。 2. **Druid连接池**:Druid是阿里...

    Spring+SpringMvc+MybatisPlus+Aop(自定义注解)动态切换数据源

    - AOP切面的Java类,实现了方法执行前后的拦截和数据源切换。 - 数据源相关的Java配置类,用于配置Spring的DataSource和MybatisPlus。 - Mapper接口和对应的XML文件,定义数据库操作。 - 业务逻辑层的Java类,其中的...

    spring boot多数据源配置

    二、配置多数据源切换 1. 创建数据源配置类 创建一个Java配置类,利用`@ConfigurationProperties`注解绑定上面的配置,并使用`@Primary`标记主数据源: ```java @Configuration @ConfigurationProperties(prefix =...

    spring动态切换数据源

    然后,定义一个数据源切换的抽象类或接口,用于封装切换逻辑: ```java public interface DataSourceSwitcher { DataSource switchDataSource(DataSourceType type); } ``` 接着,实现这个接口,基于Spring的`...

    基于Spring多数据源实例

    在IT行业中,数据库是系统的核心组成部分,特别是在大型企业级应用中,经常需要处理来自不同来源的数据,这时"多数据源切换"技术就显得尤为重要。本文将深入探讨如何在一个基于Spring框架的应用中实现多数据源的动态...

    spring集合hibernate多数据切换

    7. **测试与调试**:为了验证多数据源切换功能是否正常工作,我们需要编写测试用例。这可能包括单元测试和集成测试,确保在不同数据源之间切换时,业务逻辑的正确性不受影响。 综上所述,"spring集合hibernate多...

    真正意义的spring动态切换数据源源码

    动态数据源切换是在多数据库环境下的常见需求,例如在测试和生产环境中使用不同的数据库,或者在微服务架构中,每个服务可能需要连接到不同的数据源。Spring框架提供了一种灵活的方式来实现这一目标,使得在运行时...

    SSM(Spring+SpringMVC+MyBatis)多数据源配置框架

    9. **安全性**:多数据源配置也可能涉及安全问题,如权限控制和数据隔离,需要确保不同数据源间的访问控制得到妥善处理。 综上所述,SSM多数据源配置框架是一种强大的工具,它为企业级应用提供了灵活的数据处理能力...

    spring-boot集成mybtis+druid实现hive/mysql多数据源切换

    本文将详细讨论如何使用Spring Boot、MyBatis和Druid来实现MySQL与Hive之间的多数据源切换,以满足不同业务场景的需求。 首先,Spring Boot是Spring框架的一种轻量级实现,它简化了配置并提供了快速开发新应用的...

    Spring MVC 切换数据源

    在多数据源场景下,例如不同的业务模块可能需要连接到不同的数据库,或者在高可用性设置中,我们可能需要在主库和备库之间切换,这时候就需要动态数据源切换功能。 1. **Spring MVC与数据源配置**: - 在Spring ...

Global site tag (gtag.js) - Google Analytics