代码之家  ›  专栏  ›  技术社区  ›  manash

意外的重复密钥异常

  •  0
  • manash  · 技术社区  · 6 年前

    我在SpringBoot2.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() 而不是 保存() 是捕获重复密钥异常。使用 保存() ,如果Hibernate不执行 INSERT UPDATE 直接来说,异常是在事务提交之前的刷新过程中抛出的,这并不是我真正想要的。

    0 回复  |  直到 6 年前
        1
  •  0
  •   Morten Haraldsen    4 年前

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

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

        2
  •  -1
  •   bornleo    6 年前

    如果调试代码并查看持久性上下文的状态,您可能会得到答案。您是对的,hibernate维护一个排序队列,即在事务期间进行的所有查询都将在提交/刷新时运行。

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