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