java – 带有集合的JPA实体对分离成员上的contains方法返回false
|
我有两个JPA实体类,Group和User
Group.java: @Entity
@Table(name = "groups")
public class Group {
@Id
@GeneratedValue
private int id;
@ManyToMany
@JoinTable(name = "groups_members",joinColumns = {
@JoinColumn(name = "group_id",referencedColumnName = "id")
},inverseJoinColumns = {
@JoinColumn(name = "user_id",referencedColumnName = "id")
})
private Collection<User> members;
//getters/setters here
}
User.java: @Entity
@Table(name = "users")
public class User {
private int id;
private String email;
private Collection<Group> groups;
public User() {}
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column(name = "email",unique = true,nullable = false)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "groups_members",joinColumns = {
@JoinColumn(name = "user_id")
},inverseJoinColumns = {@JoinColumn(name = "group_id")})
public Collection<Group> getGroups() {
return groups;
}
public void setGroups(Collection<Group> groups) {
this.groups = groups;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
if (id != user.id) return false;
return email.equals(user.email);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + email.hashCode();
return result;
}
}
我尝试为具有一个成员的组运行以下代码段,其中group是刚从JpaRepository检索的实体,user是该组和分离实体的成员. Collection<User> members = group.getMembers();
System.out.println(members.contains(user)); //false
User user1 = members.iterator().next();
System.out.println(user1.equals(user)); //true
经过一些调试后,我发现在.contains()调用期间调用了User.equals(),但是Hibernate集合中的用户有空字段,因此.equals()被评估为false. 那么为什么它如此奇怪以及在这里调用.contains()的正确方法是什么? 解决方法
这个难题有几个部分.首先,@ ManyToMany关联的获取类型是LAZY.因此,在您的组中,成员字段使用延迟加载.当使用延迟加载时,Hibernate将使用对象的代理仅在访问它们时执行实际加载.实际的集合很可能是PersistentBag或PersistentCollection的一些实现(忘了哪些,以及Hibernate javadocs目前似乎无法访问),这些实现了你背后的一些魔力.
现在,您可能想知道,当您调用group.getMembers()时,您是否应该获得实际的集合并且能够使用它而不必担心它的实现?是的,但仍然有一个延迟装载的问题.你看,集合中的对象本身就是代理,它们最初只加载了它们的标识符,但没有加载其他属性.只有在访问这样的属性时才会初始化完整对象.这允许Hibernate做一些聪明的事情: >它可以让您检查集合的大小,而无需加载所有内容. 下一个难题是,在您的User类中,您使用了属性访问而不是字段访问.您的注释位于getter而不是字段(如Group中).也许这已经改变,但至少在一些旧版本的Hibernate中,只通过代理获取标识符只能使用属性访问,因为代理通过替换方法来操作,但不能绕过字段访问. 所以会发生在你的equals方法中这部分可能正常工作:if(id!= user.id)返回false; …但这不是:return email.equals(user.email); 你可能也得到了一个nullpointer异常,并没有发生这样的情况,即contains方法在提供的对象(你的填充,分离用户)上调用相等的,其集合条目作为参数.反过来可能导致nullpointer.这是拼图的最后一部分.你在这里直接使用这些字段而不是使用getter来获取电子邮件,所以你不是强迫Hibernate加载数据. 所以这是你可能会进行的一些实验.我自己尝试一下,但现在已经很晚了,我一定要去.让我知道结果是什么,看看我的答案是否正确,并使其对后来的访问者更有用. >通过在字段上放置JPA / Hibernate注释,将User中的属性访问权限更改为字段访问权限.除非在最近的版本中已经更改,否则它应该在访问集合时初始化User实例的所有属性,而不仅仅是填充了标识符的代理.但是,这可能不再起作用. JPA在你的背后做了一些疯狂的魔法,并试图使它对你来说几乎看不见,但有时它又回来咬你.如果我有时间的话,我会在Hibernate源代码中挖掘更多内容并运行一些实验,但我可能会在稍后再次访问以验证上述声明. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
