JPA hibernate AbstractRoutingDataSource 在同一方法内使用不同数据源失败

一、问题背景

项目中原来没有使用多数据源的需求,后期需要使用多数据源。
多数据源的实现主要就是通过 实现一个类,继承AbstractRoutingDataSource,并重写determineCurrentLookupKey方法,加上注解、切面,实现根据注解配置指定方法使用的多数据源。

本来实现的好好的,单个方法内使用指定的某一个数据源也走通了,但有个需求需要在指定的方法内,需要访问数据库A的某个表,然后再访问B的某个表。

二、过程

首先是搜了半天百度,找到了这么一篇文章

jpa+AbstractRoutingDataSource+Transactional数据源切换失效-CSDN博客
https://blog.csdn.net/qichangleixin/article/details/117407676

有些相似,但是不完全相似。但提供了一个点就是事务。搜索发现Jpa在进行查询的时候,似乎会开启一个默认的事务。于是在相关的业务代码上加上注解@Transactional(propagation = Propagation.NOT_SUPPORTED)
但是并没有效果,方法还是没法实现两个数据源的切换。

于是只能顺着源码一路往下断点。发现两次查询的分歧点在于

public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImplementor {
	private Connection acquireConnectionIfNeeded() {
		if ( physicalConnection == null ) {
			// todo : is this the right place for these observer calls?
			try {
				physicalConnection = jdbcConnectionAccess.obtainConnection();
			}
			catch (SQLException e) {
				throw sqlExceptionHelper.convert( e, "Unable to acquire JDBC Connection" );
			}
			finally {
				observer.jdbcConnectionAcquisitionEnd( physicalConnection );
			}
		}
		return physicalConnection;
	}
}

第二次请求进来时,会检查是不是已经创建过连接,如果创建过,会使用原有的,进而导致无法进入到

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
	protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
			dataSource = this.resolvedDefaultDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}
}

最终导致无法切换动态数据源。

然后又是一通搜
找到了一篇文章

Spring Data JPA 原理与实战第十一天 Session相关、CompletableFuture、LazyInitializationException_org.hibernate.resource.jdbc.spi.physicalconnection-CSDN博客
https://blog.csdn.net/fegus/article/details/124895552

提到可以修改hibernate.connection.handling_mode调整处理物理连接的模式。

DELAYED_ACQUISITION_AND_HOLD:延迟获取,一直保持连接到 Session 关闭
表示需要的时候再获取连接,需要的时候是指进行 DB 操作的时候,这里主要是指事务打开的时候,就需要获取连接了(因为开启事务的时候要执行“AUTOCOMMIT=0”的操作,所以这里的按需就是指开启事务;我们也可以关闭事务开启的时候改变 AUTOCOMMIT 的行为,那么这个时候的按需就是指执行 DB 操作的时候,不一定开启事务就会获得 DB 的连接);

这种模式导致了在同一个session里,连接会被复用,进而导致无法切换数据源。
于是调整为DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION

DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION:延迟获取,事务执行之后释放。
在事务执行完之后释放连接,同一个事务共享一个连接;

测试代码,当整个查询方法被套上了事务之后,整段代码将只能使用一个数据源,因为中间使用的是同一个连接。
而当为两个查询数据库的方法打上了@Transactional(propagation = Propagation.NOT_SUPPORTED),将两个事务进行分隔之后,就能够正常工作了。

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值