我正在使用Spring,Spring JPA,Hibernate和Postgresql开发REST API。 我需要在实体中拥有一系列代码。
考虑以下实体:
public class Document{
private Long id;
private String code;
//getters and setters
}
在我的春天方法中,我做了类似的事情来保存新的entity
:
String prefix = "D";
//get records
List<Document> documents = this.documentRepository.findAll();
//find max value of code
int max = 0;
for(Document d:documents){
String code = d.getCode();
int number = Integer.parseInt(code.substring(prefix.length()));
if(number>max) max = number;
}
//increment
long currentNumber = max+1;
entity.setCode(prefix+currentNumber);
this.documentRepository.save(entity);
这导致我遇到这样的情况:如果我尝试两次调用此方法,我会得到两个具有相同code
的文档。
要解决此问题,我尝试在我的方法中添加@Transactional
和@Transactional(isolation = Isolation.SERIALIZABLE)
注释。正在正确创建事务,但随后其中一个api调用失败并带有
ERROR: could not serialize access due to read/write dependencies among transactions
从我读到的关于隔离级别的内容来看,我需要SERIALIZABLE
。此外,Postgres看起来像是积极的&#34;处理此隔离级别,期望两个事务成功提交。但是,情况可能并非总是如此,并且客户端应用程序应该重试失败的事务。
观察:
D4
已删除,则接受下一个创建的文档变为D5
。所以,这并非如此&#34;无间隙&#34;正如名称本身所述。findAll
,我只是为了这个问题而简化了。在我的例子中,假设Document
包含在Folder
中。无间隙序列只是在该文件夹中,而不是全局。每个Folder
都有许多Documents
。因此,对于每个文件夹,我们都有D1
,D2
,D3
,... 可接受的解决方案:
答案 0 :(得分:0)
从我读到的关于隔离级别的内容来看,我需要的是SERIALIZABLE
如果以这种方式存在很多冲突,您将获得非常高的回滚率。
我建议使用锁定方法。
有一个序列生成器表。在此表中,使用本机查询获取新值:
UPDATE mysequencetable SET nextvalue = nextvalue + 1 RETURNING nextvalue
但是,您仍然需要处理重试,因为如果您有多个这样的表,您的事务可以以不同的顺序获取行,然后死锁。
因此,您的应用必须才能重试交易。编写良好的应用程序无论如何都必须这样做,否则如果数据库重新启动,暂时无法访问等,它将无法正常运行。
现在,如果你必须在DELETE
上重新编号,这是一个不同的问题,你需要一个触发器。