我试图创建相同实体类型的父子关系。我面临的一个问题是,当尝试执行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
答案 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-1
和Trash-1
)。
因此,它开始children
序列化。
2)但是要序列化Trash-2
,Jackson还需要序列化其字段,即Trash-3
的{{1}}字段!
3)我们回到第一步。
一直进行到递归变得太重要而杰克逊阻止了您为止。
通过用children
注释Trash-2
或parent
,您要求Jackson跳过这两个序列化之一,从而破坏了递归。
例如,在Trash-1
上,它将显示:
1)Jackson尝试序列化parent
字段,我们感兴趣的children
字段(@JsonIgnore
和parent
)。
因此,它开始Trash-1
序列化。
2)children
已序列化(不需要Trash-2
字段序列化)。
2)Trash-3
被序列化(不需要children
字段序列化)。
就完成了。
答案 2 :(得分:0)
您在父对象和子对象之间有一个循环引用:每个引用都是相互引用的。由于JSON是通过扩展任何引用的对象(没有引用)而产生的,因此您将通过父/子字段获得无限的层次结构。
当您忽略其中一个字段(在您的情况下为父级)时,遍历仅针对子对象,而不是向后遍历,因此不再循环。
答案 3 :(得分:0)
这是一个已知问题。它被称为:“杰克逊无限递归问题”。使用@JsonIgnore 是解决无限递归问题的可能解决方案之一。人们在这篇关于为什么@JsonIgnore 解决无限循环的帖子上留下了很好的答案;但是,如果您想了解更多信息或查看针对此问题的其他解决方案,请访问此 site。