OpenSessionInView vs Dynamic Routing DataSource
Posted wssswjq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenSessionInView vs Dynamic Routing DataSource相关的知识,希望对你有一定的参考价值。
开发中有这样的业务场景:有25家法院,法院数据库结构都是一样的,需要遍历25家法院统计每家法院的指标信息。我们项目使用的是spring boot2,对于遍历25家法院执行同样的操作,考虑使用spring自带的AbstractRoutingDataSource来实现数据源的切换(有关AbstractRoutingRoutingDataSource实现多数据源切换网上有很多实例,后面我也自己总结一篇)。大概的实现方式是预先定义好n个DataSource,放入一个map中,在运行中,使用一个线程安全的ThreadLoca<String>l变量来保存map的一个key值,每次访问数据库都先通过key值获取对应的DataSource,通过手动切换key值,就能实现动态数据源的切换了。
在项目中,我们数据访问层使用sring的Jpa实现,然后在service层for循环中切换数据源,启用了spring的事务管理器。但实际使用中发现,跑main函数的一些定时计算任务可以正常切换数据源,遍历25家法院。但部署为web服务器,通过request请求执行service层接口时,获取的数据始终只是一家的,并没有成功遍历25家。查看ThreadLoacl<String>中保存的key,确实是切换了;在service层使用@Autowired注入DataSource,调用getConnection()也能得到正确的数据库连接,但实际执行中就是拿不到正确的数据。数据层接口继承JpaRepository,所有也能很难看到底层jdbc连接的实际情况。
一开始怀疑是事物没有提交,因为同事提醒,只有确保方法执行完commit了事务,才能正常切换数据源。我们把for循环体提取出来单独作为一个public方法,并注解了@Transaction(propagaion=Propagation.REQUIRS_NEW) 也没有成功。最后一位大佬来说hibernate OpenSessionInView的问题。同时我们发现AbstractRoutingDataSource中的determineCurrentLookupKey()方法(在AbstractRoutingDataSource获取真正的DataSource的时候获取当前的key值的方法)并没有被调用。
OpenSessionInView网上搜说是通过filter将session和request请求的线程绑定,把事务的关闭操作放到一次请求结尾,这样能避免懒加载中,session在dao层被提前关闭,而导致的无法获取懒加载的数据的问题。在springboot中,这个配置是默认开启的,但问题就在这里,因为session被绑定到线程,导致虽然在ThreadLocal<String>变量中改变了key值,但在实际执行数据库连接的时候会直接使用线程绑定的session中的连接,我猜测底层数据获取的connection并没有被更新,所以导致了数据源表面上切换成功,但实际拿不到正确数据的现象。但OpenSessionInVIew源码里面确实是看到缓存了Session,但为何session就不会再去取新的connection了?这个问题估计和hibernate的session设计有关,需要再看看。
总之,OpenSessionInView这个默认为true的配置之前一直没有注意到,但网上很多人都在说是个反人类的设置,建议默认设置该为false。
最后在application.yml中增加spring.jpa.open-in-view: false 问题解决~
以上是关于OpenSessionInView vs Dynamic Routing DataSource的主要内容,如果未能解决你的问题,请参考以下文章