21

在我的应用程序中,我有这些 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。我调用session.update(role)将更改应用到数据库。

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

  1. 将新规则插入数据库。出色的。
  2. 更新其他角色字段(不是集合)。到目前为止,一切都很好。
  3. 更新现有规则,即使它们没有任何变化。我可以忍受这个。
  4. 再次更新现有规则。这是日志中的粘贴,包括自动评论:
/* 删除一对多行 Role.rules */
更新 ROLE_RULE 设置 ROLE_ID=null where ROLE_ID=? 和 ROLE_RULE_ID=?

当然,所有字段都不是空的,这个操作非常失败。

任何人都可以尝试解释为什么 Hibernate 会这样做???更重要的是,我该如何解决这个问题???

编辑:我非常确定这与映射有关,然后我的老板一时兴起,删除了两个类中的equals()and hashCode(),使用 Eclipse 重新创建它们,神秘地解决了这个问题。

不过,我仍然对我的问题很好奇。谁能建议为什么 Hibernate 会这样做?

4

4 回答 4

7

在 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 会尝试在删除子级之前将它们与父级分离,如果外键不为空,它将失败。

于 2008-10-07T17:29:38.333 回答
5

请参阅问题“在 Java 中覆盖 equals 和 hashCode ”的答案。

它解释了如何覆盖 equals 和 hashCode 方法,这似乎是你的问题,因为它在重写它们之后起作用。

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

于 2009-07-03T08:05:36.327 回答
3

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

@OneToMany(mappedBy = "role")

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

此外,如果您想确保只更新受影响的列,您可以在类上使用特定于休眠的注释:

@Entity
@org.hibernate.annotations.Entity(
    dynamicInsert = true, dynamicUpdate = true
)
于 2008-10-07T16:34:18.460 回答
0

Brian Deterling 的回答帮助我克服了幻象删除。我希望他放了一个真实的代码。这是我从他的建议中得到的 1. 发布以供某人使用或评论我的代码。

// 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);
    }
于 2018-03-05T03:04:43.367 回答