web-dev-qa-db-ja.com

Hibernate @OneToMany関係により、JSON結果に無限ループまたは空のエントリが発生します

2つのエンティティがあります。エンティティ「movie」とエンティティ「Clip」です。各クリップは1つのムービーに属し、ムービーには複数のクリップを含めることができます。

私のコードは次のようになります:

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-bidirection-list.html

「1対多の側を所有側として双方向の1対多をマッピングするには、mappedBy要素を削除し、manyを1に設定して挿入可能および更新可能としてfalseにする必要があります。このソリューションは明らかに最適化されていませんそして、いくつかの追加のUPDATEステートメントを生成します。」

映画をリクエストすると、次の答えが返ってきます。

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

エントリ「クリップ」は引き続き表示されます。これはまだ間違った解決策ですか、それとも私はこれと一緒に暮らす必要がありますか?

16
Goot

私はまったく同じ問題に遭遇しました。引用した段落の解決策を試しましたが、うまくいきませんでした。

私がしたことは、ClipクラスのgetMovie()に対してnullを返すことです。そうすれば、無限ループの問題はなくなります。 JSON形式で返されるデータは、{"movieId":1 ... Clips:["clipId":1、 "movie": "null"、..]}のようになります。

JSONのmovieプロパティもさらに削除する場合は、クラスレベルのアノテーションをClipクラス@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)に追加します。

ジャクソン機能:nullのシリアル化を防止、デフォルト値

更新:私が見つけたより簡単な方法は、Clipクラスのムービーのゲッターを削除することです。

16
Dino Tw

解決:

使用する

@JsonManagedReferenceインスタンス化された最初のオブジェクトの注釈

@JsonBackReferenceインスタンス化された2番目のオブジェクトの注釈

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;
13
btd1337

あなたが言っているように、「エントリークリップはまだ表示されます」。

Db応答の関係データを回避するには、fetch = FetchType.EAGERfetch = FetchType.Lazyに変更します。

2
Prometheus

Entityオブジェクトを返す代わりに、必要なデータのみを含むDTOオブジェクトを返すことをお勧めします。結果トランスフォーマーを使用して、Hibernateクエリ/基準の結果から直接取得できます。

1
rvazquezglez

まず、フェッチタイプをレイジーに設定しても効果がない理由を説明します。 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アプリを開発するためのベストプラクティスでもあると思います。

1
Dai Niu

私はこの状況の主な問題を抱えています。

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を取得すると、このムービーはClip01およびClip02と関係があり、Movie 01のシステムロードデータが、getterメソッドを介してClip01およびClip02のデータもフェッチします。

ただし、Clipクラスには、プロパティMovieとgetter getMovie()もあります。したがって、システムがCLip 01のデータを検索すると、関係Movieのデータも取得します。この状況ではMovie 01 ...およびMovie01はCLip01のデータを取得します=>したがって、これがループがある理由です。

したがって、この状況の正確な解決策はClip.JavaのGetterメソッドgetMovie()を削除この情報を使用する必要はありません。

1
lety_20391

Dino Twが言ったように簡単な方法:

ClipクラスからgetMovie()関数を削除します。

これは、Clipエンティティ/テーブル内のmovie_idによって関連付けられている各MovieClipリストを取得するのに役立ちます。

0
Eduardo Toural

基本的に、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/bidirection-relationship-in-json

import com.fasterxml.jackson.annotation.ObjectIdGenerators;
0
Vasu Roy