在我的应用程序中,我有这些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决定在关闭事务和刷新时执行以下操作:
/* 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会这样做?
答案 0 :(得分: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,则会失败。
答案 1 :(得分:5)
查看问题“Overriding equals and hashCode in Java”的答案。
它解释了如何覆盖equals和hashCode方法,这似乎是你的问题,因为它在重写之后起作用。
错误地覆盖它们会导致hibernate删除您的集合并重新插入它们。 (因为哈希键在地图中用作键)
答案 2 :(得分:3)
我不确定这是否是解决方案,但您可能想尝试:
@OneToMany(mappedBy = "role")
没有@JoinColumn注释?我认为这两个实体都试图“拥有”这个关联,这就是为什么SQL可能搞砸了?
此外,如果您想确保只更新受影响的列,您可以在类上使用特定于hibernate的注释:
@Entity
@org.hibernate.annotations.Entity(
dynamicInsert = true, dynamicUpdate = true
)
答案 3 :(得分: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);
}