JPA多对多关系导致无限递归和堆栈溢出错误。

10
我正在开发一个EclipseLink项目,其中一个用户可以像社交媒体网站上一样“关注”另一个用户。我已经使用一个名为“User”的实体(引用了一个名为“users”的表)来设置这个功能,该实体具有“followers”列表(关注该用户的用户)和“following”列表(该用户正在关注哪些用户)。关系在一个名为“followers”的单独表中定义,该表包含已关注用户的ID(“user_id”)和已关注用户的ID(“follower_id”)的列。

我的用户模型如下所示:

@Entity
@Table(name = "users")
@NamedQuery(name = "User.findAll", query = "SELECT u FROM USER u")
public class User {
    // other attributes
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "follower", joinColumns = @JoinColumn(
        name = "user_id", referencedColumnName = "id"),
    inverseJoinColumns = @JoinColumn(
        name = "follower_id", referencedColumnName = "id"))
    private List<User> followers;

    @ManyToMany(mappedBy = "followers")
    private List<User> following;

    // other getters and setters
    public List<User> getFollowers() {
        return this.followers;
    }

    public List<User> getFollowing() {
        return this.following;
    }
}

getFollowers()方法似乎正常工作,但当调用getFollowing()时,我得到一堆控制台垃圾信息,最终导致StackOverflowException:

com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion 
(StackOverflowError) (through reference chain: 
org.eclipse.persistence.indirection.IndirectList[0]-
>org.myproject.model.User["followers"]-
>org.eclipse.persistence.indirection.IndirectList[0]-
>org.myproject.model.User["following"]-
...
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase
.serializeFields(BeanSerializerBase.java:518)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize
(BeanSerializer.java:117)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer
.serializeContents(IndexedListSerializer.java:94)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer
.serializeContents(IndexedListSerializer.java:21)
...

请告诉我是否应该提供更多的堆栈跟踪信息。有什么提示吗?


2
@JacksonIgnore 可以解决你的集合问题。 - ketrox
确实是这样(假设你的意思是@JsonIgnore)。你真是救星! - Ecliptica
你能在不丢失信息的情况下进行反序列化吗? - efekctive
我不需要通过JSON发送关注者信息,因此失去这些信息对我来说不是问题。 - Ecliptica
5个回答

19

对于多对多关系,@JsonIdentityInfo 可以很好地工作。 - MertTheGreat

1
public class A {
  private String name;
  
  @JsonIgnoreProperties(value = "linkToA") // remove field to avoid loop
  private B linkToB;
}

public class B {
  private String name;
  
  @JsonIgnoreProperties(value = "linkToB") // remove field to avoid loop
  private A linkToA;
}

1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

1
我尝试了网络上所有找到的东西,但没有任何批注都不能解决问题。但在与这个问题的激烈斗争中,我找到了一个解决方案。
首先,您需要向两个实体(而不是关系实体)添加此批注:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "relationClass"})
public class YourClass { ...
}

其中"relationClass"是您的类关系列表/集合的名称:

例如:

  @OneToMany(mappedBy = "yourClass", cascade = CascadeType.ALL, fetch = FetchType.LAZY,  orphanRemoval = true)
    private Set<RelationClass> relationClass;

你还需要在注解中指定 "hibernateLazyInitializer" 和 "handler",否则会导致序列化问题。

然后,如果你看到定义关系表的表格,并且在其中看到行,在你的 JSON 响应中将不再有任何循环。因此,我认为最好的解决方案也是为关系表创建一个存储库,并从那里访问数据。

希望它能帮助到某些人!


0

我认为之前的答案中问题在于注解的包。在这篇文章 http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion 中,@JsonManagedReference 和 @JsonBackReference 可以正常工作。

但是包应该是 com.fasterxml.jackson.annotation。

有时候导入其他包可能无法解决问题。

此外,添加

import com.fasterxml.jackson.annotation.JsonIdentityInfo;

@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id")

在模型类定义之前是可以正常工作的。


0

你也可以像下面这样使用lombok的EqualsAndHashcode注解。

@EqualsAndHashcode(exclude ={"following"}) public class User{}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接