24

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

我的代码如下所示:

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;

正在生成表格,每个剪辑都有一列“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...

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

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

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

编辑:

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

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

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

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

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

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

4

9 回答 9

56

解决方案:

利用

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

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

电影.java

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

剪辑.java

@JsonBackReference
@ManyToOne
    @JoinColumn(name="movie_id")
    private Movie movie;
于 2017-11-05T05:14:12.553 回答
22

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

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

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

Jackson 特性:防止空值、默认值的序列化

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

于 2013-08-08T19:53:06.560 回答
7

首先,让我向您展示为什么将 fetch type 设置为 lazy 没有帮助。当您尝试序列化您的 pojo 时,您的序列化程序(可能是 jackson)将调用此 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>();

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

我想这是处理这个问题的最佳方法,也是开发 java web 应用程序的最佳实践。

于 2019-05-20T12:12:22.587 回答
2

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

于 2013-09-20T16:42:20.493 回答
2

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

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

于 2015-10-19T11:27:48.517 回答
1

需要在子类中添加@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;
}
于 2018-10-21T21:02:55.997 回答
1

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

当你得到 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 }

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

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

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

于 2019-04-22T16:31:55.717 回答
1

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

要进行正确的链接以及正确的 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;
}

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

import com.fasterxml.jackson.annotation.ObjectIdGenerators;
于 2019-11-26T18:16:21.857 回答
0

Dino Tw 说的简单方法:

从类中删除getMovie()函数Clip

这将帮助您检索实体/表Clip中每个Movie关联的movie_id列表。Clip

于 2018-06-01T15:09:26.707 回答