my RDBMS is PostgreSQL. I am using SpringBoot and Hibernate as JPA.
Let's consider a very simple one-column table:
Man
Age: integer
And I would like to implement a such method that add a man to the table. That method should satisfy the following condition:
At most one man in table can be 80 years old
.
@Transactional
void addMan(int age){
....
}
It looks like I need to take a exclusive lock for whole table, yes? How to do it?
答案 0 :(得分:0)
还有第二种解决方案。
使用如下所示的SERIALIZABLE
个交易:
START TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT count(*) FROM man WHERE age = 80;
现在,如果结果为0,则继续:
INSERT INTO man VALUES (80);
COMMIT;
如果两个事务同时尝试执行此操作,则会因序列化错误而失败。
在这种情况下,您只需重试该事务,直到成功为止。
可序列化事务的性能比具有较低隔离级别的事务要差,但它们仍然比实际序列化事务的性能更好。
答案 1 :(得分:0)
我会在表中添加unique constraint / index并让数据库完成工作。
create unique index man_age_unique_idx on man (age);
如果已经存在具有该年龄的记录,则没有问题。
如果它确实存在,您应该返回PersistenceException
,原因是Hibernate ConstraintViolationException
。从那里你可以得到违反的约束的名称,例如上例中的man_age_unique_idx
。
try {
entityManager.persist(man);
} catch (PersistenceException e) {
if (e.getCause() instanceof ConstraintViolationException) {
ConstraintViolationException cve = (ConstraintViolationException) e.getCause();
if (Objects.equals(cve.getConstraintName(), "man_age_unique_idx")) {
// handle as appropriate... e.g. throw some custom business exception
throw new DuplicateAgeException("Duplicate age: " + man.getAge(), e);
}
} else {
// handle other causes ...
}
}