![](/img/trans.png)
[英]How to implement Transactional for multiple updates in single method Springboot JPA
[英]How to make multiple call of @Transactional method to a single transaction
我有一个方法
@Transactional
public void updateSharedStateByCommunity(List[]idList)
从以下REST API调用此方法:
@RequestMapping(method = RequestMethod.POST)
public ret_type updateUser(param) {
// call updateSharedStateByCommunity
}
现在 ID 列表非常大,比如 200000,当我尝试处理它时,它需要很多时间并且在客户端发生超时错误。
因此,我想将其拆分为两个调用,每个调用的列表大小为 100000。
但是,问题是,它被认为是 2 个独立的交易。
注意:2 次调用是一个例子,如果数字 id 更大,它可以分为多次。
我需要确保对单个事务的两个单独调用。 如果 2 个调用中的任何一个失败,则它应该回滚到所有操作。
另外,在客户端,我们需要显示进度对话框,所以我不能只使用超时。
对您的问题 IMO 最明显的直接答案是稍微更改代码:
@RequestMapping(method = RequestMethod.POST)
public ret_type updateUser(param) {
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):
@Transactional
public updateSharedStatedByCommunityBlocks(resolveIds) {
List<String> [] blocks = split(resolveIds, 100000); // 100000 - bulk size
for(List<String> block :blocks) {
updateSharedStateByCommunity(block);
}
}
如果此方法在同一个服务中,则原始updateSharedStateByCommunity
中的@Transactional
不会做任何事情,因此它会起作用。 如果您将此代码放入其他一些 class 中,那么它将起作用,因为 spring 事务的默认传播级别是“必需的”
所以它满足了苛刻的要求:你想要一个单一的交易——你已经得到了。 现在所有代码都在同一个事务中运行。 现在每个方法都以 100000 运行,而不是所有的 id,一切都是同步的:)
然而,由于许多不同的原因,这种设计是有问题的。
正如您在问题的最后一句中所说的那样,它不允许跟踪进度(向用户显示)。 REST 是同步的。
它假设网络是可靠的并且等待 30 分钟在技术上不是问题(更不用说必须等待的 UX 和“紧张”用户:))
除此之外,网络设备可以强制关闭连接(例如具有预先配置的请求超时的负载平衡器)。
这就是为什么人们建议某种异步流程。
我可以说您仍然可以使用异步流,生成任务,并在每次批量更新一些共享 state(在单个实例的情况下在内存中)和持久性(如在集群的情况下的数据库)。
这样与客户端的交互就会发生变化:
如果在事务执行过程中出现故障,则回滚完成,进程将数据库状态更新为“失败”。
您还可以使用更现代的技术来通知服务器(例如 web sockets),但对于这个问题,它有点超出 scope 。
这里要考虑的另一件事:据我所知,处理 200000 个对象应该在 30 分钟内完成,这对于现代 RDBMS 来说并没有那么多。 当然,在不了解您的用例的情况下很难说出那里发生了什么,但也许您可以优化流程本身(使用批量操作,减少对数据库的请求数量,缓存等等)。
在这些情况下,我首选的方法是使调用异步(Spring Boot 允许使用@Async
注释进行此操作),因此客户端不会期望任何 HTTP 响应。 通知可以通过 WebSocket 完成,它将向客户端推送一条消息,其中包含每个 X 项目处理的进度。
当然,它会给您的应用程序增加更多复杂性,但是如果您正确设计该机制,您将能够将其重用于您将来可能面临的任何其他类似操作。
从技术角度来看,它可以通过org.springframework.transaction.annotation.Propagation#NESTED
传播来完成,NESTED 行为使嵌套的 Spring 事务使用相同的物理事务,但在嵌套调用之间设置保存点,因此内部事务也可以独立回滚外部事务,或者让它们传播。 但该限制仅适用于org.springframework.jdbc.datasource.DataSourceTransactionManager
数据源。
但是对于非常大的数据集,它仍然需要更多的时间来处理并使客户端等待,所以从解决方案的角度来看,也许使用异步方法会更好,但这取决于您的要求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.