最近在做的项目需要操作两个数据源,并且是一个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<String> key = new ThreadLocal<String>();
@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.TYP