加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 带有集合的JPA实体对分离成员上的contains方法返回false

发布时间:2020-12-15 04:38:24 所属栏目:Java 来源:网络整理
导读:我有两个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",referencedColumn
我有两个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做一些聪明的事情:

>它可以让您检查集合的大小,而无需加载所有内容.
>您只能获取集合中对象的标识符(主键),而不会查询整个对象.当父对象使用连接加载时,外键通常非常有效,并且用于很多事情,例如检查对象是否在持久化上下文中是已知的.
>您可以在集合中获取特定对象并初始化该集合,而不需要初始化集合中的每个对象.虽然这可能导致许多查询(“N 1问题”),但它也可以确保通过网络发送的数据不会超过所需的数据并加载到内存中.

下一个难题是,在您的User类中,您使用了属性访问而不是字段访问.您的注释位于getter而不是字段(如Group中).也许这已经改变,但至少在一些旧版本的Hibernate中,只通过代理获取标识符只能使用属性访问,因为代理通过替换方法来操作,但不能绕过字段访问.

所以会发生在你的equals方法中这部分可能正常工作:if(id!= user.id)返回false;

…但这不是:return email.equals(user.email);

你可能也得到了一个nullpointer异常,并没有发生这样的情况,即contains方法在提供的对象(你的填充,分离用户)上调用相等的,其集合条目作为参数.反过来可能导致nullpointer.这是拼图的最后一部分.你在这里直接使用这些字段而不是使用getter来获取电子邮件,所以你不是强迫Hibernate加载数据.

所以这是你可能会进行的一些实验.我自己尝试一下,但现在已经很晚了,我一定要去.让我知道结果是什么,看看我的答案是否正确,并使其对后来的访问者更有用.

>通过在字段上放置JPA / Hibernate注释,将User中的属性访问权限更改为字段访问权限.除非在最近的版本中已经更改,否则它应该在访问集合时初始化User实例的所有属性,而不仅仅是填充了标识符的代理.但是,这可能不再起作用.
>首先尝试通过迭代器从集合中获取该user1实例.看看你是如何不进行显式属性访问的,我强烈怀疑在集合上获取迭代器并从中获取元素也会强制初始化该元素.例如,包含List的Java实现调用indexOf,它只是通过内部数组,但不调用任何可能触发初始化的get方法.
>尝试使用equals方法中的getter而不是直接字段访问.我发现在处理JPA时,最好始终使用getter和setter,即使是类本身的方法,也要避免这样的问题.作为一种实际的解决方案,这可能是最强大的方式.但请确保处理电子邮件可能为空的情况.

JPA在你的背后做了一些疯狂的魔法,并试图使它对你来说几乎看不见,但有时它又回来咬你.如果我有时间的话,我会在Hibernate源代码中挖掘更多内容并运行一些实验,但我可能会在稍后再次访问以验证上述声明.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读