【问题标题】:Hibernate @OneToMany Relationship Causes Infinite Loop Or Empty Entries in JSON ResultHibernate @OneToMany 关系导致无限循环或 JSON 结果中的空条目
【发布时间】:2023-03-09 15:51:01
【问题描述】:

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

我的代码如下:

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"}

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

【问题讨论】:

    标签: json hibernate one-to-many hibernate-onetomany


    【解决方案1】:

    解决方案:

    使用

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

    【讨论】:

    • 谢谢,这对我来说似乎是最好的解决方案。
    • 很好的解决方案,这正是它应该这样做的方式,不像这里的其他人,hacky 的
    • 优雅的解决方案
    • 这是正确的解决方案。每个人都应该首先尝试这个解决方案。这个问题一直困扰着我,我无法解决。放弃了几个月。再次尝试,找到了这个答案,它成功了!
    • 这是正确的答案。其他人正在解决。
    【解决方案2】:

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

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

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

    Jackson feature: prevent serialization of nulls, default values

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

    【讨论】:

    • 我不太明白这里的意思,如果我们不需要返回的集合/列表或返回的JSON,我们为什么要做所有这些复杂的关系映射?只是为了更新连接表?
    • @JohnSmith 自从我在这里发布我的答案已经有一段时间了,但我记得这是一个不允许更改架构的遗留系统。
    • 谢谢你..它真的帮助了我。
    • 谢谢兄弟。这个问题让我发疯了。
    • 对于 Jackson 2.x,您可以使用 @JsonInclude(JsonInclude.Include.NON_NULL)
    【解决方案3】:

    首先,让我告诉你为什么将 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>();
    

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

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

    【讨论】:

    • 我一直在使用@JsonIgnoreProperties,但这并没有成功。改用 @JsonIgnoreProperties("clips") ,它就像一个魅力。非常感谢!! :D
    • 这和删除龙目岛的@Data 对我有用
    • 这是最好的答案,拯救了我的一天:))
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        需要在子类中添加@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;
        }
        

        【讨论】:

          【解决方案7】:

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

          当你得到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 方法 getMovie() 我们不需要使用这些信息

          【讨论】:

            【解决方案8】:

            基本上 JsonIgnore 或 JsonBackrefrencing 会从一端移除链接。

            要进行正确的链接以及正确的 Json 输出,请按照以下代码 sn-p 进行操作:-

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

            【讨论】:

            • @JsonIgnore 为我工作。非常感谢。
            【解决方案9】:

            Dino Tw 说的简单方法:

            Clip 类中删除getMovie() 函数。

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

            【讨论】:

            • 这不是新答案,请按原样添加此文本,作为对其他答案的评论。
            猜你喜欢
            • 2016-01-18
            • 1970-01-01
            • 1970-01-01
            • 2017-01-09
            • 1970-01-01
            • 2015-02-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多