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

Posted

技术标签:

【中文标题】如何在@ManyToOne JPA 中停止重复对象?【英文标题】:How to stop repeating objects in @ManyToOne JPA? 【发布时间】:2018-01-15 00:55:26 【问题描述】:

我的父类:

@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

在浏览器中:

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

【问题讨论】:

您没有在 JPA 中重复任何内容。您在生成 JSON 时重复,这与 JPA API 无关。 是的,尼尔知道了。谢谢。 【参考方案1】:

在您的子类中忽略使用@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;
 

【讨论】:

这是我认为最好的解决方案。 有人知道上层解决方案和这个解决方案的区别吗? brian 的解决方案和 Amer 的解决方案是哪个?【参考方案2】:

我会像这样使用@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:用于表示关联属性是字段之间双向链接的一部分的注解;并且它的作用是“子”(或“后”)链接

【讨论】:

没问题。如果这有效,请记住将其标记为答案。 有疑问我们不能在 JPA 本身中执行此操作吗? 与JPA无关。 JPA 代表资源。序列化时,默认情况下会调用方法。您必须在序列化程序上“提示”,以便它具有有关如何序列化的其他详细信息。 我添加了解释以便更好地解释。希望对您有所帮助。【参考方案3】:

从可接受的答案和其他人提出的注释基本上是告诉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

上面的交换对我有用。

【讨论】:

以上是关于如何在@ManyToOne JPA 中停止重复对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用主键作为JPA和Hibernate的外键引用?

Java JPA双向ManyToOne映射不起作用

JPA。 FetchType.Lazy 引起了奇怪的行为@ManyToOne

JPA OneToMany,如何在控制台或webapp中打印时忽略字段ManyToOne

JPA @ManyToOne 用于休眠

ManyToOne关系中的Jpa ***异常