Assert a specific and unique value in column

时间:2018-05-28 18:52:41

标签: spring postgresql hibernate

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?

2 个答案:

答案 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 ...
    }
}