Hibernate @OneToMany 关系导致 JSON 结果中的无限循环或空条目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16577907/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 19:46:42  来源:igfitidea点击:

Hibernate @OneToMany Relationship Causes Infinite Loop Or Empty Entries in JSON Result

jsonhibernateone-to-manyhibernate-onetomany

提问by Goot

I have two entities, an entity "movie" and an entity "Clip" each clip belongs to one movie and a movie can have multiple clips.

我有两个实体,一个实体“电影”和一个实体“剪辑”,每个剪辑属于一部电影,一部电影可以有多个剪辑。

My code looks like:

我的代码看起来像:

Movie.java
    @OneToMany(mappedBy = "movie", targetEntity = Clip.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Clip> clips = new HashSet<Clip>();



 Clip.java

    @ManyToOne
        @JoinColumn(name="movie_id")
        private Movie movie;

The tables are being generated and each Clip has a column "movie_id" but this causes my application to end up in an infinite loop when I'm requesting Data

正在生成表格,每个剪辑都有一个“movie_id”列,但这会导致我的应用程序在我请求数据时陷入无限循环

    @Path("/{id:[0-9][0-9]*}")
        @GET
        @Produces(MediaType.APPLICATION_JSON)
        public Movie lookupMovieById(@PathParam("id") long id) {
            return em.find(Movie.class, id);
        }


result:
{"id":1,"version":1,"name":"MGS Walkthrough","filename":"video.mp4","movieCategories":[{"id":1,"version":1,"name":"Walkthrough"}],"clips":[{"id":1,"version":1,"name":"MGS Walkthrough P1","keywords":null,"movie":{"id":1,"version":1,"name":"MGS Walkthrough","filename":"video.mp4","movieCategories":[{"id":1,"version":1,"name":"Walkthrough"}],"clips":[{"id":1,"version":1,"name":"MGS Walkthrough P1","keywords":null,"movie":{"id":1,"version":1,"name":"MGS Walkthrough","filename":"video.mp4","movieCategories":[{"id":1,"version":1,"name":"Walkthrough"}],"clips":[{"id":1,"version":1,"name":"M...

It's the same result when I'm requesting a clip.

当我请求剪辑时,结果相同。

When I change it to a @ManyToMany relationship there won't be any problems like that, but that's not what I need here. Can you help me? Setting fetchType to Lazy didn't work.

当我将其更改为 @ManyToMany 关系时,不会有任何类似的问题,但这不是我在这里需要的。你能帮助我吗?将 fetchType 设置为 Lazy 不起作用。

Edit: I'm using the current JBoss development studio

编辑:我正在使用当前的 JBoss 开发工作室

Edit:

编辑:

I "solved" this, by reading this article:

我通过阅读这篇文章“解决”了这个问题:

http://blog.jonasbandi.net/2009/02/help-needed-mapping-bidirectional-list.html

http://blog.jonasbandi.net/2009/02/help-needed-mapping-bidirectional-list.html

"To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove the mappedBy element and set the many to one @JoinColumn as insertable and updatable to false. This solution is obviously not optimized and will produce some additional UPDATE statements."

“要映射一个双向的一对多,以一对多的一方作为拥有方,你必须删除mappedBy元素并将多对一的@JoinColumn设置为可插入和可更新为false。这个解决方案显然没有优化并将产生一些额外的 UPDATE 语句。”

when I request a movie I get the following answer:

当我请求电影时,我得到以下答案:

{"id":1,"version":1,"name":"MGS Walkthrough","filename":"video.mp4","movieCategories":[{"id":1,"version":1,"name":"Walkthrough"}],"clips":[],"description":"Trailer zu mgs4"}

{"id":1,"version":1,"name":"MGS Walkthrough","filename":"video.mp4","movieCategories":[{"id":1,"version":1, "name":"Walkthrough"}],"clips":[],"description":"Trailer zu mgs4"}

the entry "clips" still appears. Is this still the wrong solution or do I just have to live with this?

条目“剪辑”仍然出现。这仍然是错误的解决方案还是我必须忍受这个?

回答by btd1337

Solution:

解决方案:

Use

@JsonManagedReferenceannotation for the first objects instantiated

@JsonManagedReference实例化的第一个对象的注释

@JsonBackReferenceannotation for the second objects instantiated

@JsonBackReference实例化的第二个对象的注释

Movie.java

电影.java

@JsonManagedReference
@OneToMany(mappedBy = "movie", targetEntity = Clip.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Clip> clips = new HashSet<Clip>();

Clip.java

剪辑程序

@JsonBackReference
@ManyToOne
    @JoinColumn(name="movie_id")
    private Movie movie;

回答by Dino Tw

I ran into exactly the same problem. I tried the solution from the quoted paragraph, it did not work for me.

我遇到了完全相同的问题。我尝试了引用段落中的解决方案,它对我不起作用。

What I did is to return null for getMovie() in Clip class, then the infinite loop issue is gone. The data returned in JSON format will look like {"movieId":1 ... clips:["clipId":1, "movie":"null", ..]}.

我所做的是在 Clip 类中为 getMovie() 返回 null,然后无限循环问题就消失了。以 JSON 格式返回的数据将类似于 {"movieId":1 ... clips:["clipId":1, "movie":"null", ..]}。

If you also want further remove the movie property in JSON, add the class-level annotation to Clip class @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)

如果您还想进一步删除 JSON 中的电影属性,请将类级别注释添加到 Clip 类 @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)

Hymanson feature: prevent serialization of nulls, default values

Hymanson 功能:防止序列化空值、默认值

Update: The easier way I found is to simply remove the getter of movie in Clip class.

更新:我发现的更简单的方法是简单地删除 Clip 类中的电影 getter。

回答by Prometheus

As you are saying "the entry clips still appears".

正如您所说的“条目剪辑仍然出现”。

To avoid the relationship data in the db response change fetch = FetchType.EAGERto fetch = FetchType.Lazy.

为避免将 db 响应中的关系数据更改fetch = FetchType.EAGERfetch = FetchType.Lazy.

回答by rvazquezglez

Instead of returning an Entity object, I suggest return a DTO object only with the data you need. You can get one directly from a Hibernate query/criteria results, using result transformers.

我建议不要返回一个实体对象,而是只返回一个包含您需要的数据的 DTO 对象。您可以使用结果转换器直接从 Hibernate 查询/标准结果中获得一个。

回答by lety_20391

I got the main problem of this situation.

我得到了这种情况的主要问题。

When you get Movie, system will load a list of relation Clips, but in CLip class you have a property Movie, when you have getter of this property, system will load Movie again.

当你得到 Movie 时,系统会加载一个关系 Clips 的列表,但是在 CLip 类中你有一个属性 Movie,当你有这个属性的 getter 时,系统会再次加载 Movie。

Movie.java
    @OneToMany(mappedBy = "movie", targetEntity = Clip.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Clip> clips = new HashSet<Clip>();

//the below getter will look for data of related Clip
    public Set<Clip> getClips(){ return this.clips}


 Clip.java

    @ManyToOne
        @JoinColumn(name="movie_id")
        private Movie movie;

//the below getter will look for related Movie again
   public Movie getMovie() { return this.movie }

For example: You get Movie 01, this movie has relation with Clip 01 and Clip 02, when system load data of Movie 01 it also fetch data of Clip 01 and Clip 02 via your getter method.

例如:你得到电影 01,这部电影与剪辑 01 和剪辑 02 有关系,当系统加载电影 01 的数据时,它也通过你的 getter 方法获取剪辑 01 和剪辑 02 的数据。

But in Clip class you also has property Movie and a getter getMovie(). So when system looks for data of CLip 01, it also get data of relation Movie, in this situation is Movie 01... and Movie 01 will get data of CLip 01 => So this is exactly reason why we have a loop

但是在 Clip 类中,您还有属性 Movie 和 getMovie()。所以当系统查找 CLip 01 的数据时,它也会得到关系 Movie 的数据,在这种情况下是 Movie 01...而 Movie 01 将获取 CLip 01 的数据 => 所以这正是我们有循环的原因

So that the exactly solution for this situation is Delete Getter Method getMovie() in Clip.javawe dont need to use this information

所以这种情况的确切解决方案是 在 Clip.java 中删除 Getter 方法 getMovie()我们不需要使用此信息

回答by Dai Niu

First, let me show you why setting fetch type to lazy doesn't help. When you try to serialize your pojo, your serializer (maybe Hymanson) would call every getter of this pojo, and use the returned value of getter as the properties in json data. So it calls the getter explicitly, which calls hibernate to load your associated entities ( movie for Clip and clips for Movie ). So you need to use @JsonIgnoreProperties to get rid of this weird infinite loop, the code goes like this:

首先,让我向您展示为什么将 fetch type 设置为 lazy 没有帮助。当您尝试序列化您的 pojo 时,您的序列化程序(可能是 Hymanson)会调用此 pojo 的每个 getter,并将 getter 的返回值用作 json 数据中的属性。所以它显式调用 getter,它调用 hibernate 来加载关联的实体(用于 Clip 的 movie 和用于 Movie 的 clips)。所以你需要使用@JsonIgnoreProperties来摆脱这个奇怪的无限循环,代码是这样的:

Clip.java

    @ManyToOne
    @JoinColumn(name="movie_id")
    @JsonIgnoreProperties("clips")
    private Movie movie;

Movie.java
    @OneToMany(mappedBy = "movie", targetEntity = Clip.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JsonIgnoreProperties("movie")
    private Set<Clip> clips = new HashSet<Clip>();

That way, you would find the nested movie in clip json object has no "clips" inside movie, and the nested clips in movie has no "movie" child neither.

这样,您会发现剪辑 json 对象中的嵌套电影在电影中没有“剪辑”,电影中的嵌套剪辑也没有“电影”子级。

I guess this is the best way to deal with this problem, and also the best practice to develop java web app.

我想这是解决这个问题的最好方法,也是开发java web app的最佳实践。

回答by Vasu Roy

Basically JsonIgnore or JsonBackrefrencing will remove linking from one end.

基本上 JsonIgnore 或 JsonBackrefrencing 将从一端删除链接。

To make proper linking as well as correct Json output, please follow below code snippet :-

要进行正确的链接以及正确的 Json 输出,请遵循以下代码片段:-

@Entity  
@Table(name="Movie")
public class Movie{

@JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property="@id")
@OneToMany(mappedBy="movie",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Set<Clip> clips = new HashSet<Clip>();

}


@Entity  
@Table(name="Clip")
public class Clip{ 

@JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property="@id")  
@ManyToOne
@JoinColumn(name="movie_id")
@JsonIgnore
private Movie movie;
}

refer below link for further details :- https://www.toptal.com/javascript/bidirectional-relationship-in-json

有关详细信息,请参阅以下链接:- https://www.toptal.com/javascript/bidirectional-relationship-in-json

import com.fasterxml.Hymanson.annotation.ObjectIdGenerators;

回答by Eduardo Toural

Easy way as Dino Tw said:

Dino Tw 说的简单方法:

Remove the getMovie()function from the Clipclass.

getMovie()Clip类中删除该函数。

That will help you to retrieve the Cliplist for each Moviewhich is associated by movie_idin the Clipentity/table.

这将有助于您检索Clip列表中为每个Movie这是由相关movie_idClip实体/表。

回答by Rasmi Ranjan Pradhan

Need to add @JsonIgnore in child class to avoid such exception. Be careful not to add this annotation in Parent class

需要在子类中添加 @JsonIgnore 以避免此类异常。注意不要在Parent类中添加这个注解

@Entity  
@Table(name="Movie")
public class Movie implements Serializable{

@OneToMany(mappedBy="movie",targetEntity=Clip.class,cascade=CascadeType.ALL,
fetch=FetchType.EAGER)
private Set<Clip> clips = new HashSet<Clip>();

}

@Entity  
@Table(name="Clip")
public class Clip implements Serializable{   
    @ManyToOne
    @JoinColumn(name="movie_id")
    @JsonIgnore
    private Movie movie;
}