問題:Hibernateの双方向の1対多の関係がJSONに簡単にマップされません。デフォルト(Jackson)マッピングが使用されている場合、親には子への参照が含まれる子が含まれるため、無限再帰の問題があります。これを修正するために見つけた最も簡単な方法は、Many end属性を@JsonIgnoreとしてマークすることです。これは、JSONが期待どおりに生成されることを意味します。ただし、オブジェクトがREST POST(たとえば)を介して送信される場合、これは、ManyToOne参照が失われることを意味します。現在、RESTサービスはデータを永続化する前に参照を再構築しますが、これを自動的に行う推奨方法があるかどうか疑問に思います(Spring RESTアーキテクチャを使用)。
説明のために、いくつかのコードスニペットを次に示します。
親エンティティ:
@Entity
@Table(name = "wine_case")
public class WineCase {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "wineCase")
private List<CaseContent> caseContents;
}
子エンティティ:
@Entity
@Table(name = "case_content")
public class CaseContent {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "wine_case_id", nullable = false)
@JsonIgnore
private WineCase wineCase;
}
RESTコントローラーのリクエストを受信し、CaseContentからWineCaseへの関係を再構築する必要があるメソッド:
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody
WineCase createWineCase(@RequestBody WineCase wineCase, HttpServletResponse response) {
wineCase.setId(null);
wineCase.getCaseContents().forEach((cc) -> cc.setWineCase(wineCase));
entityManager.persist(wineCase);
response.setHeader("Location", "/winecases/" + wineCase.getId());
return wineCase;
}
ジャクソンを使用しているので、これを見てください: https://github.com/FasterXML/jackson-datatype-hibernate
このモジュールでは、初期化しなかった場合、JacksonはLAZYフィールドをシリアル化しません。そのため、両側にLAZYフィールドがあっても問題はありません。
このソリューションがあなたのニーズに合わない場合(たぶんあなたはいくつかの熱望したいでしょう)。次に、@ JsonIgnoreを使用する代わりに、次のように@JsonIgnorePropertiesを使用します。
@Entity
@Table(name = "wine_case")
public class WineCase {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "wineCase")
@JsonIngoreProperties("wineCase", allowSetters=true)
private List<CaseContent> caseContents;
}
@Entity
@Table(name = "case_content")
public class CaseContent {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "wine_case_id", nullable = false)
@JsonIgnoreProperties("caseContents", allowSetters=true)
private WineCase wineCase;
}
プロパティの除外により、開始した場所(つまり、子または親から)でサイクルをシリアル化しないようにします。 allowSettersはプロパティの逆シリアル化を許可します。シリアル化のみがアノテーションで定義されたプロパティを無視します(allowSetters/Gettersは2.6以降に存在します)。
Jsonignorepropertiesと遅延オブジェクトには現在バグがあることに注意してください: https://github.com/FasterXML/jackson-datatype-hibernate/issues/78 。 2.8で修正されることを期待しましょう。
JSONライブラリのオプションを確認してください。オブジェクトを再度シリアル化する代わりに、すでにシリアル化されているオブジェクトを識別して、後方参照を出力する方法が必要です。これはほぼ間違いなく、問題に対する最も簡単な解決策です。
すべてのデータを直接返そうとするのではなく、子を単に識別子としてシリアル化してみませんか?これは、ドキュメントデータベースが推奨する方法です(または少なくとも MongoDB )。
次に、要求されたときに完全なエンティティを含めることができます。 jsonapi.org仕様 はこの動作を説明しています。
したがって、デフォルトでは、WineCase
エンティティを識別子の配列とともにCaseContent
に返すだけであり、クライアントがCaseContent
を含めるように要求した場合にのみ、wineCaseフィールドのエンティティ全体を含めます。単なる識別子としてシリアル化されます。