Issue
My Parent class :
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> child;
public List<Child> getChild() {
return child;
}
public void setChild(List<Child> child) {
this.child = child;
}
My Child class :
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REF_ID")
private Parent parent;
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
Error :
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:626) ~[tomcat-embed-core-8.5.15.jar:8.5.15]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:211) ~[tomcat-embed-core-8.5.15.jar:8.5.15]
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:109) ~[tomcat-embed-core-8.5.15.jar:8.5.15]
at org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView.render(ErrorMvcAutoConfiguration.java:227) ~[spring-boot-autoconfigure-1.5.4.RELEASE.jar:1.5.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1286) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1041) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:984) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.9.RELEASE.jar:4.3.9.RELEASE]
... 38 common frames omitted
In browser :
Its look like @OneToMany from the parent entity calling child object fine. But @ManyToOne in child entity doing the same by calling parent object again and its repeating. How to avoid this problem?
Solution
I would use @JsonManagedReference and @JsonBackReference like so:
Parent
@JsonBackReference
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> child;
Child
@JsonManagedReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REF_ID")
private Parent parent;
What is happening?
This has nothing to do with JPA. Your @Entity model provides methods to access the fields. To represent them in an API, you serialize them. The serializer is not aware of the entity at that level (to where it would have knowledge of the persistence layer. That would breach the SRP). Therefore, you must provide the serializer hints just as you do with @ManyToOne, @OneToMany on your entity model. The serializer iterates through the get methods on your class, and calls them. Without hints, you end up with a circular reference because you call the parent, then get the child. Once serializing the child, you call the parent again.
From the references
@JsonManagedReference: Annotation used to indicate that annotated property is part of two-way linkage between fields; and that its role is "parent" (or "forward") link.
@JsonBackReference: Annotation used to indicate that associated property is part of two-way linkage between fields; and that its role is "child" (or "back") link
Answered By - Brian