Spring+Mybatis配置多数据源

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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.2.xsd 
            http://www.springframework.org/schema/tx
 	  		http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
 	  		http://www.springframework.org/schema/context 
      		http://www.springframework.org/schema/context/spring-context-3.2.xsd
      		http://www.springframework.org/schema/aop 
      		http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
      		
	<!-- 引入配置文件 -->		
	<bean id="propertyConfigurer"
		class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
		<property name="locations" value="classpath:db.properties"/>
	</bean>
	
	<!-- mp数据库源 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		init-method="init" destroy-method="close">
		<property name="url" value="${db.url}" />
		<property name="username" value="${db.username}" />
		<property name="password" value="${db.password}" />
		<!-- 初始化连接大小 -->
		<property name="initialSize" value="${db.initialSize}" />
		<!-- 连接池最大数量 -->
		<property name="maxActive" value="${db.maxActive}" />
		<!-- 连接池最大空闲 -->
	    <property name="maxIdle" value="${db.maxIdle}"></property>
	    <!-- 连接池最小空闲 -->
	    <property name="minIdle" value="${db.minIdle}"></property>
	    <!-- 获取连接最大等待时间 -->
	    <property name="maxWait" value="${db.maxWait}"></property>
	</bean>
	
	<!-- VOS数据库源 -->
	<bean id="vosDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="url" value="${vos.jdbc.url}" />
		<property name="username" value="${vos.jdbc.username}" />
		<property name="password" value="${vos.jdbc.password}" />
		<!-- 初始化连接大小 -->
		<property name="initialSize" value="${vos.jdbc.initialSize}" />
		<!-- 连接池最大数量 -->
		<property name="maxActive" value="${vos.jdbc.maxActive}" />
		<!-- 连接池最大空闲 -->
	    <property name="maxIdle" value="${vos.jdbc.maxIdle}"></property>
	    <!-- 连接池最小空闲 -->
	    <property name="minIdle" value="${vos.jdbc.minIdle}"></property>
	    <!-- 获取连接最大等待时间 -->
	    <property name="maxWait" value="${vos.jdbc.maxWait}"></property>
	</bean>
	
	<!-- 数据库连接池 -->
	<bean id="dynamicDataSource" class="com.mobiprima.sip.common.util.DynamicDataSource">
	   <!--目标数据源,名称不能改-->
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry value-ref="dataSource" key="dataSource" />
				<entry value-ref="vosDataSource" key="vosDataSource" />
			</map>
		</property>

		<!-- 默认使用mpDataSource的数据源 -->
		<property name="defaultTargetDataSource" ref="dataSource" />
	</bean>
	
	<!--配置会话工厂-->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!--扫描mybatis配置文件-->
		<property name="configLocation" value="classpath:mybatis.xml" />
		<!--配置数据库连接池-->
		<property name="dataSource" ref="dynamicDataSource" />
		<!--配置事务-->
		<property name="transactionFactory">
			<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
		</property> 
	</bean>
	
	<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	 	 <!--Repository注解-->
		<property name="annotationClass" value="org.springframework.stereotype.Repository" />
		<property name="basePackage" value="com.mobiprima.sip.common.dao" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>
	
</beans>

Spring提供了AbstractRoutingDataSource抽象类实现根据路由找到数据源,里面提供了afterPropertiesSet方法,在数据库配置文件初始化后执行,如下图:
在这里插入图片描述
从代码中可以出首先会判断目标数据源是否为空,为空则抛出异常,不为空,则将数据源存放到一个map当中。这里的关键在于targetDataSources属性。方法一是直接将要切换的数据源bean添加该map中,因为是固定的,所以后续也不需要修改了。而方法二是在determineCurrentLookupKey()中将数据源bean动态的生成,然后添加到targetDataSources map中,因为是动态生成添加,所以需要调用afterPropertiesSet()方法,通知spring容器更新。AbstractRoutingDataSource中方法引用的都是resolvedDataSources该属性,所以AbstractRoutingDataSource将targetDataSources的键值对信息都添加到resolvedDataSources属性中,以便后续的调用。
2.连接数据库
在这里插入图片描述
spring容器连接哪个数据源,是由该方法决定
在这里插入图片描述
而具体选择哪个数据源又是由determineCurrentLookupKey()方法的返回值决定的,该方法需要我们继承AbstractRoutingDataSource来重写。该方法返回一个key,该key就是数据源bean的beanName,我们根据key去resolvedDataSources中取到DataSource,然后将DataSource返回
DynamicDataSource.java

public class DynamicDataSource extends AbstractRoutingDataSource {

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

}

DataSourceContextHolder.java

public class DataSourceContextHolder {

	public static final String DATA_SOURCE_CASH_LOAN_ADMIN = "dataSource";
	
    public static final String DATA_SOURCE_VOS = "vosDataSource";
    //用ThreadLocal来设置当前线程使用哪个dataSource
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();



    public static void setDbType(String dbType) {
      //void set(Object value)设置当前线程的线程局部变量的值。
        contextHolder.set(dbType);
    }

    public static String getDbType() {
    	//public Object get()该方法返回当前线程所对应的线程局部变量。
        return ((String) contextHolder.get());
    }

    public static void clearDbType() {
    //public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。         需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
        contextHolder.remove();
    }

}

值得注意的是在CustomerContextHolder.java中使用了ThreadLocal类的set方法来设置当前线程要选择的dataSource,看一下set方法的源码:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

显而易见,获取当前线程,并且使用一个hashmap把需要存储的值设置进去。因为tomcat是用的线程池来处理每个请求,所以用ThreadLocal可以保证线程安全问题。
参考:https://blog.csdn.net/u013034378/article/details/81661706

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringMyBatis是两个非常流行的Java开发框架,可以很好地支持双数据源事务。 在Spring配置多数据源,我们需要使用`@Configuration`注解将Java类标记为配置类,并通过`@Bean`注解来声明并注册多个数据源。对于每个数据源,我们需要配置对应的数据源对象、事务管理器和MyBatis的SqlSessionFactoryBean。然后,使用`@Primary`注解标记一个数据源为主数据源,它将作为默认的数据源。 接下来,在事务的配置中,我们需要使用`@EnableTransactionManagement`注解开启Spring的事务管理功能,并使用`@Transactional`注解来标记需要进行事务管理的方法。在方法内部,我们可以使用`TransactionTemplate`或`PlatformTransactionManager`进行事务的控制。 在使用MyBatis时,我们可以通过`@MapperScan`注解来自动扫描并注册MyBatis的Mapper接口。在进行事务配置时,可以使用`@Transactional`注解来标记需要进行事务管理的方法,从而确保在执行数据库操作时,能够正确地开启、提交或回滚事务。 在使用双数据源时,我们需要在方法或类的上方使用`@Transactional(value = "transactionManager")`注解来指定需要使用哪个数据源的事务管理器。这样,在执行相关操作时,Spring就会根据指定的数据源来管理事务,保证数据的一致性。 总结一下,SpringMyBatis提供了完善的支持来实现双数据源事务。我们可以通过合理的配置和注解来实现多个数据源的管理,并使用事务注解来控制事务的开启、提交和回滚。这样,就可以在一个应用程序中同时访问和操作多个数据源,保证数据的一致性和完整性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值