如何在@ManyToOne JPA中停止重复对象?

时间:2017-08-07 16:28:03

标签: java json hibernate spring-boot jackson

我的家长班:

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

我的孩子班:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REF_ID")
private Parent parent;

public Parent getParent() {
    return parent;
}

public void setParent(Parent parent) {
    this.parent = parent;
}

错误:

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

在浏览器中:

enter image description here

它看起来像@OneToMany来自调用子对象的父实体。但是@ManyToOne在子实体中通过再次调用父对象并重复它来做同样的事情。如何避免这个问题?

3 个答案:

答案 0 :(得分:3)

我会这样使用@JsonManagedReference@JsonBackReference

@JsonBackReference
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Child> child;

@JsonManagedReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REF_ID")
private Parent parent;

发生了什么事?

这与JPA无关。您的@Entity模型提供了访问字段的方法。要在API中表示它们,请对它们进行序列化。序列化程序不知道该级别的实体(它将知道持久层的位置。这将违反SRP)。因此,您必须像在实体模型上使用@ManyToOne,@ OneToMany一样提供序列化程序提示。序列化程序遍历您的类上的get方法,并调用它们。如果没有提示,您最终会得到循环引用,因为您调用父项,然后获取子项。序列化孩子后,再次给父母打电话。

来自参考资料

@JsonManagedReference:用于表示带注释的属性是字段之间双向链接的一部分的注释;并且它的作用是“父”(或“前进”)链接。

@JsonBackReference:用于指示关联属性是字段之间双向链接的一部分的注释;并且它的作用是“child”(或“back”)链接

答案 1 :(得分:2)

在您的子课程中,忽略使用@JsonIgnore

序列化/绝望您的父级
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REF_ID")
private Parent parent;

public Parent getParent() {
   return parent;
}

public void setParent(Parent parent) {
    this.parent = parent;
 }

答案 2 :(得分:0)

从可接受的答案中提出的注释和其他注释基本上是在告诉Spring Boot跳过阅读它们的方法。是的,它能够解决循环问题,但不能完全解决返回预期的json输出的问题。

相反,我对这些实现做了一些改动,对我有用!

交换参考注释

  • 父实体-> @JsonManagedReference
  • 子实体-> @JsonBackReference

代码段示例:

Parent.java(类)

@JsonManagedReference
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parent", cascade = CascadeType.ALL) //"parent" -> is the name defined by child class
private List<Child> childs;

Child.java(类)

@JsonBackReference
@ManyToOne
@JoinColumn(name = "parent_id") //parent_id is the foreign key for child
private Parent parent; //object name defined from Parent class

上面的互换对我有用。