意外的重复键异常

时间:2019-02-12 18:58:31

标签: java hibernate spring-boot

我看到Spring Boot 2.0.4 + Hibernate的行为很奇怪。

我有一个包含随机生成代码的实体。如果已经为另一个实体设置了生成的代码,则将按预期抛出DataIntegrityViolationException。这样,循环可以使用新代码再次尝试,希望该代码不会被使用。当发生这种情况时,循环继续,将生成一个新代码,并且对saveAndFlush()的调用将再次引发相同的异常,表明引起问题的原始代码(先前的迭代)已被使用(重复)。但是,我现在要设置一个新代码,而不是异常所提到的代码。

我唯一能想到的是Hibernate不会从“队列”中删除该操作,因此,在对saveAndFlush()的第二次调用发生时,它仍然尝试执行第一个保存,然后执行新的保存。显然,第一次保存与第一次迭代一样失败。也许我错了,但是那是怎么回事?

@Entity
public class Entity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, unique = true)
    private int code;

    public void setCode(int code) {
        this.code = code;
    }

    //Other properties

}

@Transactional
public void myFunction() {
    boolean saved = false;
    do {
        int code = /* Randomly generated code */;
        if(entity == null) {
            entity = new Entity(code, /* other properties */);
        } else {
            entity.setCode(code);
        }

        try {
            entity = myRepository.saveAndFlush(entity);
            saved = true;
        } catch (DataIntegrityViolationException e) {
            /* Ignore so that we can try again */
        }
    } while(!saved);
}

编辑:

如果我将saveAndFlush()替换为save(),问题将消失。我在某处看到,如果还调用flush(),则在先前的保存失败后执行保存可能会出现问题。这正是我的情况。但是,我不明白为什么会这样。我调用saveAndFlush()而不是save()的唯一原因是捕获重复的键异常。使用save(),如果Hibernate不直接执行INSERTUPDATE,则在提交事务之前的刷新期间会抛出异常,这并不是我真正想要的。 / p>

2 个答案:

答案 0 :(得分:0)

在这种情况下,您将需要重新启动事务,因为该错误已与事务上下文/会话绑定。

因此,相反,将重试逻辑放在事务边界之外,或者如果需要维护完整性(保存全部或不保存任何内容),请首先检查是否存在,以避免引发异常。

答案 1 :(得分:-1)

如果您调试代码并查看持久性上下文的状态是什么,您可能会得到答案。没错,hibernate维护着各种队列,即在事务期间进行的所有查询都将在commit / flush上运行。

请发布您在调试时获得的持久性上下文的值。