Java 如何对单个事务多次调用@Transactional方法
我有办法Java 如何对单个事务多次调用@Transactional方法,java,spring-boot,server,Java,Spring Boot,Server,我有办法 @Transactional public void updateSharedStateByCommunity(List[]idList) 此方法是从以下REST API调用的: @RequestMapping(method = RequestMethod.POST) public ret_type updateUser(param) { // call updateSharedStateByCommunity } 现在ID列表非常大,比如200000,当我尝试处理它时,会花费
@Transactional
public void updateSharedStateByCommunity(List[]idList)
此方法是从以下REST API调用的:
@RequestMapping(method = RequestMethod.POST)
public ret_type updateUser(param) {
// call updateSharedStateByCommunity
}
现在ID列表非常大,比如200000,当我尝试处理它时,会花费很多时间,并且在客户端发生超时错误
所以,我想把它分成两个电话,每个电话的列表大小为100000
但是,问题是,它被视为两个独立的交易
注意:这两个呼叫就是一个例子,如果号码ID更大,它可以被分成很多次
我需要确保对单个事务进行两次单独调用。如果两个调用中的任何一个失败,那么它应该回滚到所有操作
此外,在客户端,我们需要显示进度对话框,因此我不能只使用超时。事务性注释(尽管并非所有底层实现都支持它)。我反对尝试将ID分成两个调用,而是尝试修复超时(毕竟,您真正想要的是单个、全部或无事务)。您可以设置超时,而不是基于每个方法。事务注释(尽管并非所有底层实现都支持它)。我反对尝试将ID分成两个调用,而是尝试修复超时(毕竟,您真正想要的是单个、全部或无事务)。您可以设置超时,而不是基于每个方法设置超时。在这些场景中,我首选的方法是使调用异步(Spring Boot允许使用
@Async
注释进行此操作),因此客户端不会期望任何HTTP响应。通知可以通过WebSocket完成,WebSocket将向客户端推送消息,其中包含每个X项处理的进度
当然,这会增加应用程序的复杂性,但如果您正确设计该机制,您将能够将其重新用于将来可能面临的任何其他类似操作。在这些场景中,我首选的方法是使调用异步(Spring Boot允许使用
@Async
注释进行此操作),因此,客户机不会期望任何HTTP响应。通知可以通过WebSocket完成,WebSocket将向客户端推送消息,其中包含每个X项处理的进度
当然,这会增加应用程序的复杂性,但如果您正确设计该机制,您将能够将其重新用于将来可能面临的任何其他类似操作。从技术角度来看,可以使用
org.springframework.transaction.annotation.Propagation#嵌套的传播,嵌套行为使嵌套的Spring事务使用相同的物理事务,但在嵌套调用之间设置保存点,因此内部事务也可以独立于外部事务回滚,或者让它们传播。但是限制仅适用于org.springframework.jdbc.datasource.DataSourceTransactionManager
datasource
但是对于非常大的数据集,它仍然需要更多的时间来处理并让客户端等待,因此从解决方案的角度来看,使用异步方法可能会更好,但这取决于您的需求。从技术角度来看,这可以通过org.springframework.transaction.annotation.Propagation#NESTED
Propagation完成,嵌套行为使嵌套的Spring事务使用相同的物理事务,但在嵌套调用之间设置保存点,因此内部事务也可以独立于外部事务回滚,或者让它们传播。但是限制仅适用于org.springframework.jdbc.datasource.DataSourceTransactionManager
datasource
但是对于非常大的数据集,它仍然需要更多的时间来处理并让客户端等待,因此从解决方案的角度来看,使用异步方法可能会更好,但这取决于您的需求。对于您的问题,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);
}
}
@RequestMapping(method=RequestMethod.POST)
公共ret_类型更新程序(参数){
更新SharedStateBycomunityBlocks(ResolveId);
}
...
并且在服务中引入一个新方法(如果您不能更改服务的代码,请提供一个中间类,您将使用以下功能从控制器调用该类):
@交易的
公共更新SharedStatedByCommunityBlock(ResolveId){
List[]blocks=split(resolveIds,100000);//100000-批量大小
用于(列表块:块){
更新SharedStateBycomunity(块);
}
}
如果此方法在同一服务中,则原始更新SharedStateBycomunity
中的@Transactional
将不会执行任何操作,因此它将工作。如果将此代码放入其他类中,那么它将工作,因为spring事务的默认传播级别为“必需”
因此,它解决了苛刻的要求:您希望有一个单一的交易-您已经得到了。现在所有代码都在同一事务中运行。现在每个方法都使用100000个ID运行,而不是所有ID,所有内容都是同步的:)
然而,由于许多不同的原因,这种设计存在问题
它不允许跟踪进度(向用户显示),正如您在问题的最后一句中所述。REST是同步的
它假设网络是可靠的,等待30分钟从技术上讲不是问题(让用户体验和“紧张”的用户不得不等待:)
除此之外,网络设备还可以强制关闭连接(如具有预先配置的请求超时的负载平衡器)
这就是为什么人们建议某种异步流
我可以说,您仍然可以使用异步流,生成任务,并在每次批量更新之后更新一些共享状态(对于单个实例,在内存中)和持久状态(对于集群,就像数据库)