Books

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:

  1. Thank you! Simple and clear explanation.

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

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

    ReplyDelete
  4. Anonymous7:00 PM

    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.

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

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

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

    ReplyDelete
  8. good example... thanks ......

    ReplyDelete
  9. 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?

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

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

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

    ReplyDelete
  13. Thanks a lot.. i also faced this issue

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

    ReplyDelete
  15. Anonymous10:33 AM

    thank you this is worked for me :)

    ReplyDelete
  16. Thank you. I got an output for this solution.

    ReplyDelete
  17. thank you, simple and clear explanation

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

    ReplyDelete
  19. thank u man,it really worked'

    ReplyDelete
  20. Espinal8:34 PM


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

    ReplyDelete