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

后端 未结 9 714
天涯浪人
天涯浪人 2020-12-06 05:07

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:



        
相关标签:
9条回答
  • 2020-12-06 05:10

    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.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.

    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

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

    0 讨论(0)
  • 2020-12-06 05:11

    First, let me show you why setting fetch type to lazy doesn't help. When you try to serialize your pojo, your serializer (maybe jackson) 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:

    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.

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

    0 讨论(0)
  • 2020-12-06 05:17

    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", ..]}.

    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)

    Jackson feature: prevent serialization of nulls, default values

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

    0 讨论(0)
  • 2020-12-06 05:20

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

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

    0 讨论(0)
  • 2020-12-06 05:23

    Solution:

    Use

    @JsonManagedReference annotation for the first objects instantiated

    @JsonBackReference annotation for the second objects instantiated

    Movie.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;
    
    0 讨论(0)
  • 2020-12-06 05:23

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

    @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;
    }
    
    0 讨论(0)
提交回复
热议问题