Hibernate @OneToMany Связь вызывает бесконечную петлю или пустые записи в результате JSON

У меня есть две сущности, сущность "фильм" и сущность "Клип", каждый клип принадлежит одному фильму, а фильм может иметь несколько клипов.

Мой код выглядит так:

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, "имя": "Прохождение" }], "клипы": [], "описание": "Трейлер zu mgs4" }

запись "клипы" по-прежнему появляется. Является ли это еще неправильным решением или мне просто нужно жить с этим?

Ответ 1

Я столкнулся с той же проблемой. Я попробовал решение из приведенного параграфа, это не сработало для меня.

Что я сделал, так это вернуть null для getMovie() в классе Clip, тогда проблема с бесконечным циклом исчезнет. Данные, возвращаемые в формате JSON, будут выглядеть как { "movieId": 1... клипы: [ "clipId": 1, "movie": "null",..]}.

Если вы также хотите удалить свойство фильма в JSON, добавьте аннотацию уровня класса к классу Clip @JsonSerialize (в том числе = JsonSerialize.Inclusion.NON_NULL)

Функция Джексона: предотвратить сериализацию нулей, значения по умолчанию

Обновление: Более простой способ - просто удалить получателя фильма в классе Clip.

Ответ 2

Решение:

Использование

@JsonManagedReference аннотация для первых экземпляров объектов

@JsonBackReference аннотация для второстепенных объектов

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;

Ответ 3

Как вы говорите, "клипы для записи все еще появляются".

Чтобы избежать данных отношений в ответе ответа db fetch = FetchType.EAGER на fetch = FetchType.Lazy.

Ответ 4

Вместо того, чтобы возвращать объект Entity, я предлагаю вернуть объект DTO только с данными, которые вам нужны. Вы можете получить его непосредственно из результатов запроса/критериев Hibernate, используя трансформаторы результатов.

Ответ 5

Я получил основную проблему в этой ситуации.

Когда вы получаете Movie, система загрузит список относящихся к нему клипов, но в классе CLip у вас есть свойство Movie, когда у вас есть получатель этого свойства, система снова загрузит 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, она также извлекает данные Clip 01 и Clip 02 через ваш метод получения.

Но в классе Clip у вас также есть свойство Movie и метод getMovie(). Поэтому, когда система ищет данные клипа 01, она также получает данные отношения Movie, в этой ситуации это Movie 01... и Movie 01 получит данные CLip 01 => Так что это как раз та причина, по которой у нас есть цикл

Таким образом, для решения этой ситуации именно метод Get Getter GetMovie() в Clip.java, нам не нужно использовать эту информацию.

Ответ 6

Во-первых, позвольте мне показать вам, почему установка типа выборки на lazy не помогает. Когда вы пытаетесь сериализовать ваше pojo, ваш сериализатор (возможно, Джексон) будет вызывать каждый получатель этого pojo и использовать возвращаемое значение getter как свойства в данных json. Поэтому он явно вызывает метод получения, который вызывает hibernate для загрузки связанных объектов (фильм для клипа и клипы для фильма). Поэтому вам нужно использовать @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.

Ответ 7

Простой способ, как сказал Дино Тв:

Удалите getMovie() из класса Clip.

Это поможет вам получить список Clip для каждого Movie который связан с movie_id в объекте/таблице Clip.

Ответ 8

Нужно добавить @JsonIgnore в дочерний класс, чтобы избежать такого исключения. Будьте осторожны, чтобы не добавлять эту аннотацию в родительский класс

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