Why does this @JsonIgnore fix my infinite loop?

Eric Tercasio :

I have attempted to create a parent-child relationship of the same entity type. A problem I was facing was that when trying to do a GET request to retrieve an entity where the children are not empty, I would get a stack overflow error, from what looks like an infinite loop. After some looking around I found a fix by simply adding a @JsonIgnore to the parent field in the Entity class. My question is why does this fix that issue? What was the issue to begin with?

@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;

Here is the error I was receiving

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
davidxxx :

The exception explains that :

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...

The parent field serialization triggers the children field serialization that itself triggers the parent field serialization, and so for...
That may look abstract enough, so here is a concrete example.

Suppose these three Trash instances : one parent that has two children.

Trash-1 (children = 2,3)

Trash-2 (parent : 1)

Trash-3 (parent : 1)

Suppose you want to serialize Trash-1 (the parent).
Here is how Jackson proceeds :

1) Jackson tries to serialize Trash-1 fields, which the children field (Trash-2 and Trash-3) that interest us.
So it starts the children serialization.
2) But to serialize Trash-2, Jackson also needs to serialize its fields, which the parent field that is Trash-1 !
3) We go back to the first step.

And it goes on until that the recursion be too important and Jackson stops you.

By annotating parent or children with @JsonIgnore, you ask to Jackson to skip one of both serialization and so it breaks the recursion.
For example on parent, it would give :

1) Jackson tries to serialize Trash-1 fields, which the children field (Trash-2 and Trash-3) that interest us.
So it starts the children serialization.
2) Trash-2 is serialized (no parent field serialization required).
2) Trash-3 is serialized (no parent field serialization required).

And it is done.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=310543&siteId=1