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

7 comments:

Ravi Bhatia said...

Thank you! Simple and clear explanation.

sishir shrestha said...

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

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 Cahya Aditya said...

Solved, thx

Sajan Monga said...

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

upendra sahu said...

thanks a lot ...