Spring+MyBatis多数据源的切换

因为业务需求,这几天一直在研究Spring+MyBatis多数据源切换。按照网上的各种例子,改了又改,就是切换不成功。最后在其他同事的提醒下,终于发现并解决了问题。现在将多数据源切换的配置和出现并解决的问题记录下来,为自己存一份记忆,也为后来人提供帮助。

一、多数据源的配置

1、两个类

这两个类的类名可以根据自己的编码习惯命名,我的命名及实现代码如下:

(1)、DataSourceContextHolder:用于进行数据源的获取、设置及还原

package com.cpms.trasen.common.tk;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据源设置类
 * 
 * @author bigdata-cp
 *
 */
public class DataSourceContextHolder {

    // 日志,与多数据源切换无关
	private final static Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);
	
	// 用于切换数据源的线程
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

	// 设置数据源
	public static void setDataSourceType(String dataSourceType) {
		logger.info("DataSourceContextHolder.setDataSourceType start");
		contextHolder.set(dataSourceType);
		logger.info("DataSourceContextHolder.setDataSourceType end");
	}

	// 获取现在的数据源
	public static String getDataSourceType() {
		logger.info("implement DataSourceContextHolder.getDataSourceType");
		return contextHolder.get();
	}

	// 还原(清除设置的)数据源
	public static void clearDataSourceType() {
		logger.info("implement DataSourceContextHolder.clearDataSourceType");
		contextHolder.remove();
	}
}

(2)、DynamicDataSource:需要继承AbstractRoutingDataSource抽象类用于实现数据源的切换

package com.cpms.trasen.common.tk;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

	private final static Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

	@Override
	protected Object determineCurrentLookupKey() {
		logger.info("implement DynamicDataSource.determineCurrentLookupKey");
		System.out.println("此时获取到的数据源为:" + DataSourceContextHolder.getDataSourceType());
		return DataSourceContextHolder.getDataSourceType();
	}

}

2、配置文件

SSM框架自带的如事物、日志、别名等配置这里就不多说了,一搜一大把。这里主要贴多数据源相关的配置代码。

(1)、数据源文件properties文件:我这里都是SqlServer数据库,如果有其他类型的数据库,手动添加驱动、连接方式和用户密码就行。

#SQLServerDriver
driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

#dataSourceA
dataSourceAurl=jdbc:sqlserver://localhost:1433;DatabaseName=trasen_clinicalPathMonitoring
dataSourceAuname=sa
dataSourceApwd=1

#dataSourceB
dataSourceBurl=jdbc:sqlserver://192.168.2.108:1433;DatabaseName=trasen_clinicalPathMonitoring
dataSourceBuname=sa
dataSourceBpwd=1

(2)、spring-mybatis配置文件:基本配置和多数据源配置

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans      
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd      
                        http://www.springframework.org/schema/context      
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd      
                        http://www.springframework.org/schema/mvc      
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	<!-- 扫描service层 -->
	<mvc:annotation-driven />
	<context:component-scan base-package="com.cpms..trasen.*.server" use-default-filters="false" />

	<!-- 引入数据库配置文件 -->
	<context:property-placeholder location="classpath:sqlServer.properties" />

	<bean id="parentDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass">
			<value>${driverClassName}</value>
		</property>
		<!-- 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
		<property name="initialPoolSize" value="5" />
		<!-- 连接池中保留的最大连接数。Default: 15 -->
		<property name="maxPoolSize" value="200" />
		<!-- 连接池中保留的最小连接数。 -->
		<property name="minPoolSize" value="5" />
		<!-- 配置当连接池所有连接用完时应用程序getConnection的等待时间 -->
		<property name="checkoutTimeout" value="30000" />
		<!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
		<property name="acquireIncrement" value="5" />
		<!-- 最大空闲时间,x秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
		<property name="maxIdleTime" value="601" />
		<!-- 每x秒检查所有连接池中的空闲连接。Default: 0 -->
		<property name="idleConnectionTestPeriod" value="600" />
		<!-- 连接池在获得新连接失败时重试的次数,如果小于等于0则无限重试直至连接获得成功。Default: 30 -->
		<property name="acquireRetryAttempts" value="5" />
		<!-- 两次连接中间隔时间,连接池在获得新连接时的间隔时间。default: 1000 单位ms -->
		<property name="acquireRetryDelay" value="1000" />
		<!-- 测试连接
		<property name="preferredTestQuery">
			<value>SELECT 1</value>
		</property> -->
	</bean>

	<!-- 定义数据源Bean -->
	<bean id="dataSourceA" parent="parentDataSource">
		<property name="jdbcUrl">
			<value>${dataSourceAurl}</value>
		</property>
		<property name="user">
			<value>${dataSourceAuname}</value>
		</property>
		<property name="password">
			<value>${dataSourceApwd}</value>
		</property>
	</bean>

	<bean id="dataSourceB" parent="parentDataSource">
		<property name="jdbcUrl">
			<value>${dataSourceBurl}</value>
		</property>
		<property name="user">
			<value>${dataSourceBuname}</value>
		</property>
		<property name="password">
			<value>${dataSourceBpwd}</value>
		</property>
	</bean>

	<!-- 配置dataSource管理key值和value值对应,默认选择dataSourceA ,其他配置按照正常的spring mvc 配置即可。 -->
	<bean id="dataSourceSwitcher" class="com.cpms.trasen.common.tk.DynamicDataSource">
		<!-- 默认数据源 -->
		<property name="defaultTargetDataSource" ref="dataSourceA"></property>
		<!-- 通过key-value的形式来关联数据源 -->
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry key="dataSourceA" value-ref="dataSourceA"></entry>
				<entry key="dataSourceB" value-ref="dataSourceB"></entry>
			</map>
		</property>
	</bean>

	<!-- 注册SqlSessionFactoryBean -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSourceSwitcher" />
		<property name="configLocation" value="classpath:mybatis-config.xml" />
		<!-- 自动扫描mappers.xml文件 -->
		<property name="mapperLocations" value="classpath:com/cpms/trasen/*/dao/mappings/*.xml" />
	</bean>

	<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.cpms.trasen.*.dao" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>
</beans>

3、数据源的切换

项目需求在用户登录成功后,根据用户切换对应的数据源。博主就是直接写在用于登录的service实现类的方法中,该方法调用了一个WebService用于验证用户的登录信息,所以线程不止一个。在设置数据源后的线程的生命周期,在切换数据源之前结束了,进入了另一个线程,所以获取的数据源为null导致切换失败。最后,将设置数据源的方法转移到了验证session过期的AOP中,既主线程中,才成功切换数据源。

调用设置数据源的方法如下:数据源字符串也可设置为类变量,请根据应用场景进行设置。

// "xxx"为需要切换的数据源字符串,该字符串为xml中配置的多数据源id
DataSourceContextHolder.setDataSourceType("xxx");

多数据源的配置与切换其实内容很简单,网上的例子都是可以行得通的。但关键的问题是每个项目遇到的情况都会不同,所以导致出现的问题也会不同,需要各自发现和解决。看到的各位有什么不懂的欢迎留言一起探讨。

每天积累一点,付出总会得到回报。

发布了8 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/aw277866304/article/details/85335380
今日推荐