为什么使用此@JsonIgnore修复无限循环?

时间:2019-07-19 14:33:40

标签: java mysql spring hibernate jpa

我试图创建相同实体类型的父子关系。我面临的一个问题是,当尝试执行GET请求以检索子级不为空的实体时,我会从看起来像无限循环的状态中得到堆栈溢出错误。环顾四周后,我发现了一个解决方法,只需在实体类的父字段中添加一个@JsonIgnore即可。我的问题是,为什么这可以解决该问题?首先是什么问题?

@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

4 个答案:

答案 0 :(得分:2)

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

因此,要序列化一个子级,您必须序列化其父级,而要序列化父级,则必须序列化其子级。包括您开始的孩子。

您添加的标签@JsonIgnore解决了此问题,因为现在无需序列化其父级即可序列化子级。再也没有无限循环了。

答案 1 :(得分:2)

该异常说明:

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"]-
and so for...

Function字段序列化触发本身的([^=]+)=([^=]+)(?:\||$) 字段序列化 触发$re = '/([^=]+)=([^=]+)(?:\||$)/m'; $str = 'txn_status=0399|txn_msg=failure|txn_err_msg=Transaction Cancelled : ERROR CODE TPPGE161|clnt_txn_ref=9178|tpsl_bank_cd=NA|tpsl_txn_id=T245107|txn_amt=121.00|clnt_rqst_meta={mob:9937253528}{custname:pawan}|tpsl_txn_time=NA|tpsl_rfnd_id=NA|bal_amt=NA|rqst_token=cd3f6f55-5990-4c3b-bb12-238eede827a0|hash=3cf25909ec73865d3200bc267119d3fcc21df463'; preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0); var_dump($matches); 字段序列化,因此...
这看起来可能足够抽象,所以这里是一个具体示例。

假设这三个parent实例:一个父级有两个孩子。

  

Trash-1(孩子= 2,3)

     

Trash-2(父母:1位)

     

Trash-3(父母:1位)

假设您要序列化children(父级)。
这是杰克逊前进的方式:

1)Jackson尝试序列化parent字段,我们感兴趣的Trash字段(Trash-1Trash-1)。
因此,它开始children序列化。
2)但是要序列化Trash-2,Jackson还需要序列化其字段,即Trash-3的{​​{1}}字段!
3)我们回到第一步。

一直进行到递归变得太重要而杰克逊阻止了您为止。

通过用children注释Trash-2parent,您要求Jackson跳过这两个序列化之一,从而破坏了递归。
例如,在Trash-1上,它将显示:

1)Jackson尝试序列化parent字段,我们感兴趣的children字段(@JsonIgnoreparent)。
因此,它开始Trash-1序列化。
2)children已序列化(不需要Trash-2字段序列化)。
2)Trash-3被序列化(不需要children字段序列化)。

就完成了。

答案 2 :(得分:0)

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

当您忽略其中一个字段(在您的情况下为父级)时,遍历仅针对子对象,而不是向后遍历,因此不再循环。

答案 3 :(得分:0)

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