Spring事务管理的失效和Proxy类型的DataSource

本文分析了使用Proxy类型的DataSource导致Spring事务管理失效的原因,并提出了两种解决方案:一是将TransactionManager与DataSource配置在同一配置文件中;二是改变服务暴露方式,通过IDataSourceHandler服务提供真实的DataSource实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring事务管理的失效和Proxy类型的DataSource

在服务框架中,我们由于需要将DataSource作为第三方服务暴露给其他模块(此处是十分不推荐的,因为如果作为服务那么首先就要求该服务没有状态),因此就采用JdkProxy来实现虚拟DataSource暴露给其他模块以及第三方。

环境:

采用ASF(基于SCA服务框架的应用服务框架)暴露DataSource作为第三方服务,其他模块的Ibatis SqlMapClientSpringTransactionManager都注入了这个Proxy类型的DataSource

问题:

发现Spring注入DataSourceProxy以后,使用是正常的,但是事务却失效了,JDBC事务失效。

分析:

跟踪察看了spring的代码,发现原来spring中的TransactionManager在事务启动和结束的时候,都是首先将DataSourceConnectionHolderThreadLocal中保存,然后在事务过程中获取的都是保存过的Connection,出错也是会滚该Connection

但是由于当前使用的是ProxyDataSource,虽然每一次Call的时候都是调用同一个DataSource,但是在事务开始的时候,获取的是注入到TransactionManager中的DataSource Proxy,在事务进行过程中,使用的是新的Proxy,在ThreadLocalMap中存在了两份DataSource Proxy的信息也就是有两份ConnectionHolder,这导致在事务开始的时候,将connectionautocommitfalse,然后放入到第一个DataSource Proxy作为KeyEntry中,无法被后面事务进行所获取,事务进行的时候默认将会通过DataSource产生一个新的Connection同时autocommit失效,因此产生了事务失效的问题。

Spring的事务处理流程图:

<shapetype id="_x0000_t75" stroked="f" filled="f" path=" m@4@5 l@4@11@9@11@9@5 xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0 "></f><f eqn="sum @0 1 0 "></f><f eqn="sum 0 0 @1 "></f><f eqn="prod @2 1 2 "></f><f eqn="prod @3 21600 pixelWidth "></f><f eqn="prod @3 21600 pixelHeight "></f><f eqn="sum @0 0 1 "></f><f eqn="prod @6 1 2 "></f><f eqn="prod @7 21600 pixelWidth "></f><f eqn="sum @8 21600 0 "></f><f eqn="prod @7 21600 pixelHeight "></f><f eqn="sum @10 21600 0 "></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 585.75pt" coordsize="21600,21600" type="#_x0000_t75"><imagedata o:title="" src="E13197E4.files/image001.emz"></imagedata></shape>

1 Spring 事务处理流程图

解决方法:

其实只需要让Spring TransactionManager能够将DataSource真实实例获得并作为Entry Key保存到ThreadLocal中即可,那么就有两种方式,一种将就是将分开的配置放在一起,那么这里看TransactionManager应该是外部不需要配置的,可以和DataSource配置在一个spring中,那么就不需要用Proxy的方式,另一种不得不用Proxy的方式,例如对于IbatisSqlMapClient。处理方法如下:

<shape id="_x0000_i1026" style="WIDTH: 415.5pt; HEIGHT: 288.75pt" coordsize="21600,21600" type="#_x0000_t75"><imagedata o:title="" src="E13197E4.files/image003.emz"></imagedata></shape>

2 DataSource 解决方法类图

不再将DataSource作为服务暴露给第三方,而是将内部封装DataSourceIDataSourceHandler来作为服务暴露,同时调用方通过工厂类直接将IDataSourceHandler服务注入,就可以达到在调用方这边获取到的是DataSource的实例,但是需要注意的就是,由于ASF服务框架是动态随机加载各个组件,可能在服务被调用的时候尚未发布,因此需要在注入的时候设置为lazy-inittrue

结论:

Proxy被运用约来广泛,但是也会造成一些再运用Proxy代理的实例作为框架业务逻辑的情况,必须弄清楚情况然后选择如何调用和组装,同时作为服务暴露给第三方,尽量选择无状态的接口服务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值