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

问题描述 投票:20回答:4

在我的应用程序中,我有这些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. 再次更新现有规则。这是来自日志的粘贴,包括自动注释:
/* 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会这样做?

hibernate
4个回答
7
投票

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

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

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,则将失败。


5
投票

请参阅问题'Overriding equals and hashCode in Java'的答案。

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

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


3
投票

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

@OneToMany(mappedBy = "role")

而没有@JoinColumn注释?我认为这两个实体都试图“拥有”该关联,这就是为什么SQL可能搞砸了?

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

@Entity
@org.hibernate.annotations.Entity(
    dynamicInsert = true, dynamicUpdate = true
)

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);
    }
© www.soinside.com 2019 - 2024. All rights reserved.