事务与动态数据源
问题描述:
写主库开事务的情况下会导致时不时的将更新/插入操作写入到从库上, 导致mysqlException update command denied
问题原因:
jetty的工作队列会重用处理线程, 导致threadLocal中的值被重用, 然而transaction注解在service层, 会在DynamicDataSourceSwitch被设置之前直接去threadlocal拿数据, 本应拿到null, 但是拿到了之前线程的值
问题解决:
DataSourceAdvice AfterReturn需要删除threadLocal中的数据源key
public class DataSourceAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice {
private static final Logger LOG = LoggerFactory.getLogger(DataSourceAdvice.class);
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
throws Throwable {
DataSourceSwitcher.clearDataSource();
}
}
事务代码调用链:
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去获取数据源