Monday, September 16, 2013

Fixing org.springframework.http.converter.HttpMessageNotWritableException Error

A couple of days back, I ran into an interesting error while retrieving persisted entities as JSON  in SpringMVC:

[Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)...


I had to do some poking around and reading up on documentation to figure out how to fix it.

As it turned out, the fix wasn't that complex and the culprit was Jackson: the Java-JSON processor being used to transform the retrieved Java entity to a JSON representation.

The error occurs when you try to retrieve an entity that has a bi-directional @OneToMany relationship with another entity.


For example if we have a Parent to Child entity with the Parent entity having a one to many relationship with Child. i.e:
@Entity
class Parent {

     @Id
     @Column(name="parent_id")
     @GeneratedValue(strategy = GenerationType.AUTO)
     private Long id;

     private String name;
     private Parent wife;

     @OneToMany(mappedBy="parent" cascade = CascadeType.ALL)
     private Collection<Child>children = new ArrayList<>();
...
}

and

@Entity
class Child {
     private String name;

    @ManyToOne
    @JoinColumn(name="parent_id", referencedColumn="parent_id")
    private Parent parent;
...
}


Retrieving Parent entity in your SpringMVC controller might look like this:

@Controller
@RequestMapping("/parents.json")
public class ParentListController {
   @Autowired
   private ParentDAO parentDAO;

   @RequestMapping(method = RequestMethod.GET)
   @ResponseBody
   public List getParents() {
      return parentDAO.getAll();
   }
}

And the above code would give the error:

nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)

The fix is to get Jackson to be able to handle bi-directional references. And this is done by using two Annotations: @JsonManagedReference and @JsonBackReference.

@JsonManagedReference is used to annotate the inverse side while @JsonBackReference maps the owning side of the relationship.

And updated version of the Parent and Child entity that would work would then be:

@Entity
class Parent {

     @Id
     @Column(name="parent_id")
     @GeneratedValue(strategy = GenerationType.AUTO)
     private Long id;

     private String name;
     private Parent wife;

     @OneToMany(mappedBy="parent" cascade = CascadeType.ALL)
     @JsonManagedReference
     private Collection<Child> children = new ArrayList<>();
...
}

and

@Entity
class Child {
    private String name;

    @ManyToOne
    @JoinColumn(name="parent_id", referencedColumn="parent_id")
    @JsonBackReference
    private Parent parent;
...
}

22 comments:

Unknown said...

Thank you! Simple and clear explanation.

Unknown said...

superb! exactly what i was looking for. thanks :)

Unknown said...

If i used Annotations like this only Child Object is able to retrieve ,

and i am facing new issue i.e

com.fasterxml.jackson.databind.JsonMappingException: Multiple back-reference properties with name 'defaultReference'

How to resolve this issue

Anonymous said...

In this example, I cannot find a "referencedColumn", only a "referencedColumnName". Also, when I use @JsonBackReference and @JsonManagedReference, I am prompted to import either "..fasterxml.Jackson.." or "..codehaus.Jackson..". Does the import make any difference? And also, when you say "parent_id", what is the convention for that? Is it "class_id", "class_", or just ""? Sorry I have so many questions, but this post seems like it will solve my problem if you can help me out.

Rully said...

also superb! this is exactly what i was looking for. thank u very much :)

Stace said...

2 years later and this is still a good piece of help.
Thanks

Achmad said...

Solved, thx

Unknown said...

thanks
but when i use this annotation, i cant get the value of propertie that i use it for

Unknown said...

good example... thanks ......

Unknown said...

thanks a lot ...

Zubair said...

Thanks for the great simple solution
In the meantime, I have to load a parent object while I am fetching the child from a child service exposed. In this case, I am not getting any parent object populated. Any suggestion there?

Unknown said...

Thank you. I was looking for the solution for a while. My problem is solved now.

Unknown said...

Y:2017 and this still happens (needs annotation)... thanks !

sushil khera said...

Thanks a lot it worked for me as well...

sushil khera said...

Thanks a lot.. i also faced this issue

Unknown said...

Thank You very much.
This solution worked for me !!

Anonymous said...

thank you this is worked for me :)

SArat said...

Thank you. I got an output for this solution.

Environnement de développement JAVA said...

thank you, simple and clear explanation

deepa said...

thank you it is worked. but it is not retrieving child class attribute values. any suggestion

Unknown said...

thank u man,it really worked'

Espinal said...


Could someone please reclarify why this is needed, I have seen MANY video tutorials on @OneToMany and @ManyToOne and none of those used these annotations. I was getting desperate because I was having this error for about 6 hours. It was resolved using these two annotations. THANKS A LOT to the author!!