web-dev-qa-db-ja.com

Hibernate 1-> REST / JSONサービスを介した多くの関係を処理する最良の方法

問題: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;
}
6
BarrySW19

ジャクソンを使用しているので、これを見てください: 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で修正されることを期待しましょう。

5
Walfrat

JSONライブラリのオプションを確認してください。オブジェクトを再度シリアル化する代わりに、すでにシリアル化されているオブジェクトを識別して、後方参照を出力する方法が必要です。これはほぼ間違いなく、問題に対する最も簡単な解決策です。

0
Jules

すべてのデータを直接返そうとするのではなく、子を単に識別子としてシリアル化してみませんか?これは、ドキュメントデータベースが推奨する方法です(または少なくとも MongoDB )。

次に、要求されたときに完全なエンティティを含めることができます。 jsonapi.org仕様 はこの動作を説明しています。

したがって、デフォルトでは、WineCaseエンティティを識別子の配列とともにCaseContentに返すだけであり、クライアントがCaseContentを含めるように要求した場合にのみ、wineCaseフィールドのエンティティ全体を含めます。単なる識別子としてシリアル化されます。

0
David Perfors