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

为什么Hibernate在我尝试更新/插入时尝试删除?

  •  20
  • Yuval  · 技术社区  · 17 年前

    在我的应用程序中,我有以下Hibernate映射类型(一般情况):

    class RoleRule {
      private Role role;
      private PermissionAwareEntity entity; // hibernate-mapped entity for which permission is granted
      private PermissionType permissionType; // enum
    
      @ManyToOne
      @JoinColumn(name = "ROLE_ID")
      public Role getRole() {
        return role;
      }
      public void setRole(Role role) {
        this.role = role;
      }
    
    }
    
    class Role {
      private Set<RoleRule> rules = new HashSet<RoleRule>(0);
    
      @OneToMany(cascade=CascadeType.ALL)
      @JoinColumn(name="ROLE_ID")
      public Set<RoleRule> getRules() {
        return rules;
      }
      public void setRules(Set<RoleRule> rules) {
        this.rules = rules;
      }
    
    }
    

    所有班级都有 equals() & hashCode() 覆盖。

    我的应用程序允许调整角色(不必担心,仅限系统管理员),并且在其他字段中,允许创建新的角色规则。创建新规则时,我会尝试创建新规则 RoleRule 对象并将其插入角色的字段中 rules 1.我打电话 session.update(role) 将更改应用于数据库。

    现在是丑陋的部分。。。Hibernate决定在关闭事务和刷新时执行以下操作:

    1. 将新规则插入数据库。杰出的
    2. 更新其他角色字段(不是集合)。到现在为止,一直都还不错。
    3. 更新现有规则,即使其中没有任何更改。我可以接受这个。
    4. 更新现有规则 再一次 .以下是日志中的粘贴,包括自动注释:
    /* delete one-to-many row Role.rules */
    update ROLE_RULE set ROLE_ID=null where ROLE_ID=? and ROLE_RULE_ID=?

    当然,所有字段都不是空的,而且这个操作失败得惊人。

    有人能解释为什么Hibernate会这样做吗???更重要的是,我该怎么做???

    编辑 当前位置我非常确定这与映射有关,然后我的老板突发奇想删除了 equals() hashCode() 在这两个类中,都使用Eclipse重新创建了它们,这神秘地解决了问题。

    不过我对我的问题还是很好奇。有人能解释为什么Hibernate会这样做吗?

    4 回复  |  直到 16 年前
        1
  •  7
  •   Brian Deterling    17 年前

    在Hibernate中,我通常使用两种方法更新集合(一对多集合的多个方面)。蛮力方法是清除集合,在父级上调用save,然后调用flush。然后重新添加所有集合成员,并再次调用父集合上的save。这将删除全部,然后插入全部。中间的刷新是关键,因为它迫使删除发生在插入之前。最好只在小集合上使用此方法,因为它会重新插入所有集合。

    第二种方法更难编码,但效率更高。您循环遍历新的子项集,手动修改仍然存在的子项,删除不存在的子项,然后添加新的子项。在伪代码中,将是:

    copy the list of existing records to a list_to_delete
    for each record from the form
       remove it from the list_to_delete
       if the record exists (based on equals()?  key?)
         change each field that the user can enter
       else if the record doesn't exist
         add it to the collection
    end for
    for each list_to_delete
      remove it
    end for
    save
    

    我在Hibernate论坛上搜索了好几个小时,试图找到解决这个问题的正确方法。您应该能够只更新集合以使其准确,然后保存父集合,但正如您所发现的,Hibernate会在删除子集合之前尝试将其与父集合分离,如果外键不为null,则将失败。

        2
  •  5
  •   Community Mohan Dere    9 年前

    看到问题的答案了吗 Overriding equals and hashCode in Java '.

    它解释了如何重写equals和hashCode方法,这似乎是您的问题,因为它在重写它们之后仍然有效。

    错误地覆盖它们可能会导致hibernate删除您的集合并重新插入它们。(因为散列键在映射中用作键)

        3
  •  3
  •   toolkit    17 年前

    我不确定这是否是解决方案,但您可能希望尝试:

    @OneToMany(mappedBy = "role")
    

    并且没有@JoinColumn注释?我认为这两个实体都在试图“拥有”关联,这就是为什么SQL可能会出错的原因?

    此外,如果要确保仅更新受影响的列,可以在类上使用特定于hibernate的注释:

    @Entity
    @org.hibernate.annotations.Entity(
        dynamicInsert = true, dynamicUpdate = true
    )
    
        4
  •  0
  •   Jeya Balaji    8 年前

    Brian Detering的回答帮助我克服了幻影删除。我希望他写了一个真正的密码。以下是我从他的建议中得到的信息。张贴供某人使用或评论我的代码。

    // snFile and task share many to many relationship
    
    @PersistenceContext
    private EntityManager em;
    
    public SnFile merge(SnFile snFile) {
            log.debug("Request to merge SnFile : {}", snFile);
    
            Set<Task> tasks = taskService.findBySnFilesId(snFile.getId());
            if(snFile.getTasks() != null) {
                snFile.getTasks().clear();
            }
            em.merge(snFile);
            em.flush();
            if(tasks != null) {
                if(snFile.getTasks() != null)
                    snFile.getTasks().addAll(tasks);
                else
                    snFile.setTasks(tasks);
            }
    
            return em.merge(snFile);
        }