【问题标题】:Why does this @JsonIgnore fix my infinite loop?为什么这个@JsonIgnore 修复了我的无限循环?
【发布时间】:2019-07-19 14:33:40
【问题描述】:

我试图创建相同实体类型的父子关系。我面临的一个问题是,当尝试执行 GET 请求以检索子项不为空的实体时,我会收到一个堆栈溢出错误,看起来像是一个无限循环。在环顾四周后,我通过简单地将 @JsonIgnore 添加到 Entity 类的父字段中找到了解决方法。我的问题是为什么这能解决这个问题?开始的问题是什么?

@Entity
@Table(name = "trash")
public class Trash {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    private String username;

    @NotBlank
    private String message;

    private Long likes;

    @ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    @JsonIgnore
    private Trash parent;

    @OneToMany(cascade = CascadeType.ALL, mappedBy="parent", orphanRemoval=true, fetch = FetchType.LAZY)
    private Collection<Trash> children;

这是我收到的错误

2019-07-19 10:29:55.867 ERROR 14156 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.example.litter.model.Trash["parent"]->com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"]->com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"]->com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"]->com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"]->com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"]->com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"]-
repeats for awhile....
>com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]->org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"])] with root cause

java.lang.StackOverflowError: null

【问题讨论】:

    标签: java mysql spring hibernate jpa


    【解决方案1】:

    异常说明:

    无法写入 JSON:无限递归(StackOverflowError);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (通过参考链: >com.example.litter.model.Trash["parent"]- >com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]- >org.hibernate.collection.internal.PersistentBag[0]->com.example.litter.model.Trash["parent"]- >com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]- >org.hibernate.collection.internal.PersistentBag[0]- >com.example.litter.model.Trash["parent"]- >com.example.litter.model.Trash$HibernateProxy$lBPNIlf1["children"]- >org.hibernate.collection.internal.PersistentBag[0]- >com.example.litter.model.Trash["parent"]- 等等……

    parent 字段序列化会触发 children 字段序列化本身 触发parent 字段序列化,等等...
    这可能看起来很抽象,所以这里有一个具体的例子。

    假设这三个Trash 实例:一个父母有两个孩子。

    Trash-1(孩子 = 2,3)

    Trash-2(父级:1)

    Trash-3(父级:1)

    假设您要序列化 ​​Trash-1(父级)。
    以下是杰克逊的进展方式:

    1) Jackson 尝试序列化 Trash-1 字段,这是我们感兴趣的 children 字段(Trash-2Trash-3)。
    所以它启动了children 序列化。
    2)但是要序列化Trash-2,杰克逊还需要序列化它的字段,其中parent字段是Trash-1
    3) 我们回到第一步。

    它一直持续到递归太重要而杰克逊阻止你。

    通过使用@JsonIgnore 注释parentchildren,您要求Jackson 跳过两个序列化中的一个,因此它会中断递归。
    例如在parent,它会给出:

    1) Jackson 尝试序列化 Trash-1 字段,这是我们感兴趣的 children 字段(Trash-2Trash-3)。
    所以它开始了children 序列化。
    2) Trash-2 已序列化(不需要parent 字段序列化)。
    2) Trash-3 被序列化(不需要parent 字段序列化)。

    它已经完成了。

    【讨论】:

    • 谢谢!现在知道了这一点,我是否可以使用此模型获取带有 get 请求的垃圾箱的父级和子级数据? “垃圾箱”本质上与推文相同,因此我希望能够显示对垃圾箱的回复以及导致该垃圾箱的对话
    • 当然。您有多种方法:在使用 JSON 的客户端重新关联它们。对于每个父母,你都知道它的孩子。因此,通过迭代父级的子级,您可以设置它们的父级。另一种方法是使用 JSON 自定义序列化程序,它将parent 字段序列化为整数或表示其 id 的字符串(@JsonSerialize 在字段上)
    【解决方案2】:

    您有一个无限循环,因为您的类的每个实例都引用了它的父级和子级。

    所以,为了序列化一个子元素,你必须序列化它的父元素,而为了序列化父元素,你必须序列化它的子元素。包括你开始的孩子。

    您添加的标签@JsonIgnore 解决了这个问题,因为现在要序列化一个孩子,您不需要序列化它的父级。不再有无限循环。

    【讨论】:

      【解决方案3】:

      您的父对象和子对象之间存在循环引用:每个对象相互引用。由于 JSON 是通过扩展任何引用的对象(无引用)生成的,因此您将通过父/子字段获得无限的层次结构。

      当您忽略其中一个字段(在您的情况下为父项)时,遍历只针对子项,而不是返回,因此它不再是循环的。

      【讨论】:

        【解决方案4】:

        这是一个已知问题。它被称为:“杰克逊无限递归问题”。使用@JsonIgnore 是解决无限递归问题的可能解决方案之一。人们在这篇文章中留下了很好的答案,为什么@JsonIgnore 解决了无限循环;但是,如果您想了解更多信息或查看此问题的其他解决方案,请访问此site

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-13
          • 2020-08-08
          • 2015-12-24
          • 1970-01-01
          • 2021-05-04
          • 1970-01-01
          相关资源
          最近更新 更多