spring @Transactional是否足以保持表中的并发性?

li9yvcax  于 2023-04-19  发布在  Spring
关注(0)|答案(4)|浏览(193)

我有一个表,其中包含要使用的代码的“股票”,如许可证。存储库称为CodeRepository。该代码只能使用一次,所以我需要分配这些代码,每激活一个服务一次。使用这些代码的服务的存储库称为ServiceRepository。我编写了以下代码:

@Transactional
public void assignFreeCodeToService(Service service) {
    Code code = CodeRepository.findFirstByStatus("free").orElseThrow(() -> new ApiException(ApiException.ErrorCode.NOT_FOUND, "No available Code was found"));
    // change code status
    code.setStatus("assigned");
    // assign the code to the service
    service.setCode(code.geId());
    // save the code new status
    codeRepository.save(code);
    // save the service with the new code
    serviceRepository.save(service);
}

所以,当我激活一个服务,这将分配一个“代码”并保存它.我想知道的是,如果这个代码是足够的,以保持并发,如果两个或更多的服务正在被同时激活.将这个代码,以防止使用相同的代码两次?

klh5stk1

klh5stk11#

不,通常事务的隔离级别设置为已提交读,但这并不足以保证您所需的隔离。
提高隔离级别是一种可能的方法,但它会降低并发性,更有针对性的答案是在要更新的行上获得排他锁。
您可以在Spring data JPA中编写一个使用SELECT ... FOR UPDATE语法的原生查询,它将获取行上的独占锁,以便事务可以不受干扰地更新行。

gwbalxhn

gwbalxhn2#

此代码使用@Transactional注解,这意味着它将在数据库中作为单个事务执行。这意味着如果两个或更多个线程或进程同时运行此代码,数据库将确保它们不会将相同的代码分配给两个不同的服务。
当从Code Repository检索代码时,它会更新为“assigned”状态并保存。由于这是在事务内部完成的,因此它将确保状态以原子方式更新,并且其他线程或进程将无法在事务仍在进行时检索相同的代码。
类似地,当使用分配的代码更新服务并将其保存到服务存储库时,也将在同一事务中以原子方式完成。
因此,此代码旨在确保不会为两个服务分配相同的代码,即使在同时激活多个服务时也是如此。

2admgd59

2admgd593#

回答初始Q:
我想知道的是,如果两个或多个服务同时被激活,此代码是否足以保持并发。此代码将防止使用相同的代码两次?
不。理由如下:
1.如果是read committed隔离级别(这是大多数成熟数据库的默认值),您的代码将面对lost update phenomena:它获取具有status=freeCode,并将status更新为acquired,而不考虑代码在获取和更新之间可能发生变化的事实
1.在repeatable read隔离级别(默认为MySQL)或更高级别情况下,您的代码可能会抛出像could not serialize access due to concurrent update这样的愚蠢错误,您应该准备好以某种方式减轻此类错误。
Suggestion of @nathan-hughes使用row-level locks是一个很好的起点,但是spring-data API没有提供足够的锁定行为控制来使代码更并发。

qcbq4gxm

qcbq4gxm4#

@Transactional用于实现本地事务,它将使操作数据库隔离,因此如果环境是独立的,则可以使用它。

相关问题