@Transactional足以保持表格的并发性吗?

huangapple go评论65阅读模式
英文:

Is @Transactional enough to keep concurrency in a table?

问题

I have a table that contains a "stock" of codes to be used, like licenses. The repository is called CodeRepository. That code can be used just once so I need to assign those codes, once per service i'm activating. The repository for the services that use those codes is called ServiceRepository. I wrote the following code:

@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);
}

So, when i'm activating a service, this will assign a "code" and save it. What I want to know, if this code is enough to keep concurrency if two or more services are being activated at the same time. Will this code to prevent to use the same code twice?

英文:

I have a table that contains a "stock" of codes to be used, like licenses. The repository is called CodeRepository. That code can be used just once so I need to assign those codes, once per service i'm activating. The repository for the services that use those codes is called ServiceRepository. I wrote the following code:

@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);
}

So, when i'm activating a service, this will assign a "code" and save it. What I want to know, if this code is enough to keep concurrency if two or more services are being activated at the same time. Will this code to prevent to use the same code twice?

答案1

得分: 1

答案:

不。理由如下:

  1. 在“读已提交”隔离级别(这是大多数成熟数据库的默认级别)的情况下,您的代码会面临“丢失更新现象”:它获取具有“status=free”的“Code”,并将“status”更新为“acquired”,而不考虑在获取和更新之间代码可能已经发生了更改。
  2. 在“可重复读”隔离级别(默认为“MySQL”)或更高级别的情况下,您的代码可能会抛出诸如“由于并发更新而无法序列化访问”的错误,您应该准备好以某种方式减轻此类错误。

建议使用@nathan-hughes的建议来使用“行级锁”是一个很好的起点,然而“spring-data” API没有提供足够的控制来使代码更具并发性。

英文:

Answering the initial Q:

> What I want to know, if this code is enough to keep concurrency if two or more services are being activated at the same time. Will this code to prevent to use the same code twice?

No. The reasoning is following:

  1. in case of read committed isolation level (which is the default for the most mature DB's) your code is facing with lost update phenomena: it acquires Codes which have status=free and updates status to acquired not taking into account the fact the code has been potentially changed between acquisition and update
  2. in case of repeatable read isolation level (default for MySQL) or higher you code may throw dumb errors like could not serialize access due to concurrent update and you should be ready to somehow mitigate such errors.

Suggestion of @nathan-hughes to use row-level locks is a good starting point, however spring-data API does not provide enough control over locking behaviour to make the code more concurrent.

答案2

得分: 0

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

英文:

@Transactional is used to implement local affair. It will make operation database isolate.So if the environment is stand-alone, you can use it.

huangapple
  • 本文由 发表于 2023年4月11日 07:18:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75981392.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定