如何对单个事务多次调用@Transactional方法

我有一个方法


@Transactional

public void updateSharedStateByCommunity(List[]idList)

从以下 REST API 调用此方法:


@RequestMapping(method = RequestMethod.POST)

public ret_type updateUser(param) {

  // call updateSharedStateByCommunity

}

现在ID列表非常大,比如200000,当我尝试处理它时,需要花费很多时间并且在客户端发生超时错误。


因此,我想将其拆分为两个调用,每个调用的列表大小为 100000。


但是,问题是,它被视为两个独立的交易。


注意:2次呼叫只是一个例子,如果号码id更大的话,它可以分为多次。


我需要确保对单个事务进行两次单独的调用。如果这 2 个调用中的任何一个失败,那么它应该回滚到所有操作。


另外,在客户端,我们需要显示进度对话框,所以我不能只使用超时。


暮色呼如
浏览 125回答 4
4回答

繁星点点滴滴

IMO 对您的问题最明显的直接答案是稍微更改代码:@RequestMapping(method = RequestMethod.POST)public ret_type updateUser(param) {&nbsp; &nbsp; updateSharedStateByCommunityBlocks(resolveIds);}...And in Service introduce a new method (if you can't change the code of the service provide an intermediate class that you'll call from controller with the following functionality):@Transactionalpublic updateSharedStatedByCommunityBlocks(resolveIds) {&nbsp; &nbsp; List<String> [] blocks = split(resolveIds, 100000);&nbsp; // 100000 - bulk size&nbsp; &nbsp; for(List<String> block :blocks) {&nbsp; &nbsp; &nbsp; &nbsp;updateSharedStateByCommunity(block);&nbsp;&nbsp; &nbsp; }}如果此方法位于同一服务中,则@Transactional原始方法updateSharedStateByCommunity不会执行任何操作,因此它会起作用。如果您将此代码放入其他类中,那么它将起作用,因为 spring 事务的默认传播级别是“必需”因此它满足了苛刻的要求:您想要进行一次交易 - 您已经做到了。现在所有代码都在同一个事务中运行。现在每个方法都使用 100000 个 ID 运行,而不是使用所有 id,一切都是同步的:)然而,这种设计由于许多不同的原因而存在问题。正如您在问题的最后一句中所说,它不允许跟踪进度(向用户显示)。REST 是同步的。它假设网络是可靠的,并且等待 30 分钟在技术上不是问题(不考虑 UX 和必须等待的“紧张”用户:))除此之外,网络设备可以强制关闭连接(例如具有预先配置的请求超时的负载均衡器)。这就是为什么人们建议某种异步流。我可以说,您仍然可以使用异步流,生成任务,并在每次批量更新后一些共享状态(在单个实例的情况下在内存中)和持久状态(如集群情况下的数据库)。这样与客户端的交互就会改变:客户端使用 200000 个 id 调用“updateUser”服务会“立即”响应,例如“我收到了您的请求,这是一个请求 ID,请偶尔对我进行 ping 操作,看看会发生什么情况。服务启动异步任务并在单个事务中逐块处理数据客户端使用该 id 调用“get”方法,服务器从共享状态读取进度。一旦准备好,“获取”方法将响应“完成”。如果事务执行期间出现故障,则回滚完成,并且进程将数据库状态更新为“失败”。您还可以使用更现代的技术来通知服务器(例如网络套接字),但这超出了这个问题的范围。这里需要考虑的另一件事是:据我所知,处理 200000 个对象应该在不到 30 分钟的时间内完成,对于现代 RDBMS 来说还不够。当然,在不知道您的用例的情况下,很难判断那里发生了什么,但也许您可以优化流程本身(使用批量操作、减少对数据库的请求数量、缓存等)。

四季花海

在这些场景中,我的首选方法是使调用异步(Spring Boot 允许使用注释@Async),因此客户端不会期望任何 HTTP 响应。该通知可以通过 WebSocket 完成,该 WebSocket 将向客户端推送一条消息,其中包含每个 X 项的处理进度。当然,它会给您的应用程序增加更多的复杂性,但如果您正确设计该机制,您将能够将其重用于您将来可能面临的任何其他类似操作。

MYYA

从技术角度来看,可以通过传播来完成org.springframework.transaction.annotation.Propagation#NESTED,NESTED 行为使嵌套 Spring 事务使用相同的物理事务,但在嵌套调用之间设置保存点,因此内部事务也可以独立于外部事务回滚,或者让它们传播。但限制仅适用于org.springframework.jdbc.datasource.DataSourceTransactionManager数据源。但是对于非常大的数据集,它仍然需要更多的时间来处理并使客户端等待,因此从解决方案的角度来看,也许使用异步方法会更好,但这取决于您的要求。

一只斗牛犬

该@Transactional注释接受 atimeout(尽管并非所有底层实现都支持它)。我反对尝试将 ID 拆分为两个调用,而是尝试修复超时(毕竟,您真正想要的是单个、全有或全无的事务)。您可以为整个应用程序设置超时,而不是针对每个方法设置超时。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java