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源代码中挖掘更多内容并运行一些实验,但我可能会在稍后再次访问以验证上述声明. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |