【发布时间】:2020-05-24 13:13:28
【问题描述】:
我有一个带有以下方法的 Spring 控制器:
@PostMapping("/tables")
@Transactional(isolation = Isolation.SERIALIZABLE)
public ResponseEntity<Table> addTable(@RequestBody Table table) {
if (table.getTableNumber() == 0) {
table.setTableNumber(tableRepository.count() + 1);
}
return new ResponseEntity<>(tableRepository.save(table), HttpStatus.CREATED);
}
我希望多个调用按顺序发生,以便在每次调用时count 方法将返回正确的行数(目前它不会发生,因为多个调用可以同时运行)。
我试图找出最好的方法,我得出了以下结论(如果我错了,请纠正我):
- 使用
@Transactional没有帮助,因为多个事务仍然可以同时运行 - 无论如何,
@Transactional将阻止在多次调用save时仅保留部分数据的复杂方法(不是这种情况) -
isolation = Isolation.SERIALIZABLE仅确定新事务与已完成事务的关系(在这种情况下,新事务将以最新形式查看数据库)
我读到我可以使用带有@Lock 的锁来独占访问表,但我只看到它应用于单个方法(如findById),而在我的情况下,锁必须跨越多个操作(count 和 save)。有没有办法任意锁定表(使用注释或以编程方式)?锁定整个表可能看起来有点激烈,但至少在不同表上操作的方法仍然可以同时运行(这不能仅通过在方法级别使用synchronized 来实现,而通过锁定存储库可以实现,但是这绝对不是一个干净的工作)。
听起来很奇怪,Spring 没有提供一种简单的方式来表达“ehi,我希望在这个存储库上运行的这个方法一次只能从一个线程运行”。我错过了什么吗?
【问题讨论】:
-
对不起,我忘了说我简化了问题的方法。据我了解,即使在并发修改的情况下,我也应该依靠数据库提供的功能(例如约束)以及事务来防止发生令人讨厌的事情。我也明白事务性和并发性不是一回事(这就是为什么我在问题中写了到目前为止我所理解的内容),但我认为这两个问题有些相关,这就是我提到两者的原因。我仍然想了解如何对数据库进行顺序操作。
-
我知道在这种特殊情况下没有必要使调用顺序,因为我可以让它以其他方式工作(如你所说),我只是想展示一个涉及更多的例子而不是在同一张表上的一个操作来显示
@Transactional如何在防止并发访问方面没有帮助(并确保我正确理解它)并了解如何在不改变该代码逻辑的情况下使其工作。 -
事务不是锁,但它们旨在保证数据完整性(在各个级别,取决于隔离)。我建议你阅读不同的隔离级别,看看
SERIALIZABLE在冲突情况下的表现。这实际上与 Spring、JPA 甚至 Java 无关。 -
@NathanHughes 似乎
@Lock不适用于count(我认为这是因为锁定仅在返回实体时发生)。但是,它适用于findAll和findById,我可以使用它们来获得我想要的东西。主要缺点是需要在存储库中重新定义所有这些方法,最重要的是,需要在事务中调用这些方法以使它们真正锁定表。即使在交易之外,我也想这样做。我找到了一些方法(例如,here),但看起来很hacky。
标签: java spring jpa concurrency transactions