spring配置多数据源涉及事务无法切换解决方案(绝对有效)

最近在做的项目需要操作两个数据源,并且是一个service需要同时调用两个数据源,刚开始按照网上说的配置通过切面操作AbstractRoutingDataSource这个类,发现单独去调用每一个数据源可以灵活切换,后来涉及事务一个service调用两个数据源就发现动态数据源无法切换了,琢磨了很久,终于找到原因。

问题根源:

spring涉及事务的代码调用顺序:

service注解上@transactional-->TransactionInterceptor.interpter()-->TransactionAspectSupport.createTransactionIfNecessary()-->AbstractPlatformTransactionManager.getTransaction()-->DataSourceTransactionManager.doBegin()-->AbstractRoutingDataSource.determineTargetDataSource()[lookupKey==null去拿默认的Datasource, 不为空则使用获取到的连接]-->DataSourceTransactionManager.setTransactional()[将连接设置到TransactionUtils的threadLocal中]--->Repository@Annotation-->执行一般调用链, 问题在于SpringManagedTransaction.getConnection()-->openConnection()-->DataSourceUtils.getConnection()-->TransactionSynchronizationManager.getResource(dataSource)不为空[从TransactionUtils的threadLocal中获取数据源], 所以不会再去调用DynamicDataSource去获取数据源

需要解决问题:在操作完数据库后把threadLocal中的数据源清除!

解决方案:

1.先写一个动态数据源继承AbstractRoutingDataSource

package com.dataSource;

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

/**
 * 动态数据源
 * @author 張丶張張張某人
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
	
	/**
	 * 数据源标识,保存在线程变量中,避免多线程操作数据源时互相干扰
	 */
	private static final ThreadLocal key = new ThreadLocal();

	@Override
	protected Object determineCurrentLookupKey() {
		return key.get();
	}
	
	
	
	/**
	 * 设置数据源
	 * @param dataSource 数据源名称
	 */
	public static void setDataSource(String dataSource){
		key.set(dataSource);
	}


	/**
	 * 获取数据源
	 * @return
	 */
	public static String getDatasource() {
		return key.get();
	}

	/**
	 * 清除数据源
	 */
	public static void clearDataSource(){
		key.remove();
	}

}

2.新建一个@DataSource注解用于aop切换数据源

package com.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义数据源切换
 * @author 張丶張張張某人
 *
 */
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {

	String value();
	
	String core = "core";
	
	String send = "send";
}

3.写一个aop切面作用于方法上

package com.dataSource;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;

import com.annotation.DataSource;

/**
 * 自定义数据源切换器
 * @author 張丶張張張某人
 *
 */
@Component
public class DataSourceExchange implements MethodBeforeAdvice, AfterReturningAdvice {
	
	Logger log = LoggerFactory.getLogger("【数据源切换器】");
	
	/**
	 * 方法结束后
	 */
	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		DynamicDataSource.clearDataSource();
		log.debug("数据源已移除!");
		
	}
	
	/**
	 * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
	 */
	@Override
	public void before(Method method, Object[] arg1, Object arg2) throws Throwable {
		if(method.isAnnotationPresent(DataSource.class)){
			DataSource dataSource = method.getAnnotation(DataSource.class);
			DynamicDataSource.setDataSource(dataSource.value());
			log.debug("数据源切换至:"+DynamicDataSource.getDatasource());
		}
		
	}
	
}

4.在dao层的方法上加上注解@DataSource,括号的里的值为需要切换的数据源

        @DataSource(DataSource.core)
	@Override
	public List> getCoreData() {
		StringBuffer sql = new StringBuffer();
		sql.append(" select * from ");
		sql.append(" (select * from maindata where status = 1 order by id) ");
		sql.append(" where rownum <=10 ");
		log.debug("SQL:"+sql);
		
		return exec.queryForList(sql.toString());
	}

5.spring配置文件配置动态数据源


	
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		

		
		
		
		
		
		
		
		
		
	

	
	
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		

		
		
		
		
		
		
		
		
		
	
	
	
	
	
	
	
		
			
				
				
			
		
		
	
	
	
	
    
        
        
        
    
    
    
    
    
        
        
    

6.在spring配置文件配置aop


    
        
    
    
    
    	
    		
    		
    		
    		
    	
    
    
    
    	
    	
    	
    

重点注意:切换数据源的order值要比事务切面的值小,这样优先级高!否则自动切换数据源将会失败!

测试结果:

spring配置多数据源涉及事务无法切换解决方案(绝对有效)_第1张图片

 

你可能感兴趣的:(spring,配置,config,oracle,多数据源)