1

最近、@ManyToOne / @OneToMany リレーションの JPA とクエリで問題が発生し、スタックオーバーフローが発生しました。ただし、これは、エンティティが作成されてからアプリケーション サーバーが再起動された場合にのみ発生します。

問題: 通常、war ファイルをデプロイしてアプリケーション サーバーを起動すると、データベースに何らかのコンテンツを入力して、何があってもクエリを実行できます。ただし、データベースをクリアせずにサーバーを再起動し、同じクエリを実行しようとすると、奇妙な動作が発生します。

Stackoverflow 例外が発生します。

java.lang.StackOverflowError

at java.lang.StringBuffer.append(StringBuffer.java:224)

at java.io.StringWriter.write(StringWriter.java:84)

at java.io.StringWriter.append(StringWriter.java:126)

at java.io.StringWriter.append(StringWriter.java:24)

at com.google.gson.stream.JsonWriter.beforeValue(JsonWriter.java:610)

at com.google.gson.stream.JsonWriter.open(JsonWriter.java:317)

at com.google.gson.stream.JsonWriter.beginObject(JsonWriter.java:300)

at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:190)

at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:879)

at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)

at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)

at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)

[...]

完全を期すために: 私のエンティティには次の関係があります。

ミッション エンティティ:

@OneToMany(mappedBy = "mission", cascade=CascadeType.ALL)  
private Collection<Mission2Mission> children;

@OneToMany(mappedBy = "parent", cascade=CascadeType.ALL)
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD)
private Collection<Mission2Mission> parents;

Mission2Mission エンティティ:

@ManyToOne  
@Retention(RetentionPolicy.RUNTIME)   
@Target(ElementType.FIELD)  
private Mission parent;  

@ManyToOne   
private Mission mission;

これは、親が自分の子を知っていること、またはその逆であることを意味しますが、親が除外されるため、retentionPolicy を使用して少なくとも典型的な GSON Stackoverflow を回避する必要があります。

この問題全体が JPA と GSON のどちらに関係しているのかはわかりませんが、本当に疑問に思うのは、なぜこれがサーバーの再起動後にのみ発生するのかということです。これはある種のセッションの問題を示しています。私には理解できず、この特定の問題に関する他のスレッドや問題が見つからなかったので、ここで質問します。

lazy およびeager fetch タイプがあることは知っていますが、それらを使用しても、サーバーの再起動時にのみこの問題が発生する理由は説明できません。

どうもありがとう、ケイ

4

3 に答える 3

2

GSON はサイクルをサポートしていないため、おそらくそれが原因です。GSON でサイクルを回避する通常の方法は、変数を一時的にすることだと思います。 @Retention(RetentionPolicy.RUNTIME) が機能しているとは聞いたことがありません。

双方向の関係を維持していない可能性があるため (これは非常に間違っています)、共有キャッシュをクリアするまで循環しないでください。

キャッシングを無効にするか、ウィービングを無効にして問題を絞り込むこともできます。

JAXB アノテーションを使用し、サイクルをサポートする EclipseLink Moxy などの他の JSON シリアライザーを試すこともできます。

ここに例がありますhttp://java-persistence-performance.blogspot.com/2013/08/optimizing-java-serialization-java-vs.html

于 2013-08-29T14:36:19.627 に答える
1

わかりました、ついに答えを見つけました。それを見つけるのは大変でした:

Mission Entity では、子と親を Collections に維持しました。ただし、GSON によってシリアル化されるのはそのうちの 1 つだけでした。この「親」は除外されました (@GsonExclude = @Retention(RetentionPolicy.RUNTIME)& @Target(ElementType.FIELD)。明確にするために、先ほど明示的に言及しました)。

以下のコードは、Mission-Entity の「間違った」バージョンです。

@OneToMany(mappedBy = "mission", fetch=FetchType.LAZY, cascade=CascadeType.ALL)@XmlTransient 
private Collection<Mission2Mission> children;

@OneToMany(mappedBy = "parent", fetch=FetchType.LAZY, cascade=CascadeType.ALL)@GsonExclude@XmlTransient
private Collection<Mission2Mission> parents;

他に問題が見られる人はいますか? 「mappedBy」は間違った参照をマップします。もちろん、ミッションの子を取得しようとするときは、指定されたミッションが親であるすべてのエントリを見つけるために、それを「親」フィールドにマップする必要があります。「親」も同様です。「ミッション」(子) がこれである Mission2Mission エンティティをマップする必要があるすべての親を取得します。

上記のコードはその逆を行い、これは可能性のある inf ループにつながります。Mission2Mission エンティティ (上記を参照) が逆の方法で実行し、(子ではなく親を実際に除外したため)。

マッピング フィールド (ミッションと親) を交換するのは魅力的でした。理解するのに十分な時間がかかりました。

とにかく入力していただきありがとうございます。キャッシングヒントは、ここでのデバッグ速度を大幅に改善しました:)

于 2013-08-30T14:10:14.620 に答える