8

I'm developing a RESTful webservice with spring-data as its data access layer, backed by JPA/Hibernate. It is very common to have relationships between domain entities. For example, imagine an entity Product which has a Category entity.

Now, when the client POSTs a Product representation to a JAX-RS method. That method is annotated with @Transactional to wrap every repository operation in a transaction. Of course, the client only sends the id of an already existing Category, not the whole representation, just a reference (the foreign key).

In that method, if I do this:

entity = repository.save(entity);

the variable entity now has a Category with only the id field set. This didn't surprise me. I wasn't expecting a save (SQL insert) to retrieve information on related objects. But I need the whole Product object and related entities to be able to return to the user.

Then I did this:

entity = repository.save(entity);
entity = repository.findOne(entity.getId());

that is, retrieve the object after persisting it, within the same transaction/session.

To my surprise, the variable entity didn't change anything. Actually, the database didn't even get a single select query. This is related with Hibernate's cache. For some reason, when in the same transaction, a find does not retrieve the whole object graph if that object was previously persisted.

With Hibernate, the solution appears to be to use session.refresh(entity) (see this and this). Makes sense.

But how can I achieve this with spring data?

I would like to avoid to create repetitive custom repositories. I think that this functionality should be a part of spring data itslef (Some people already reported this in spring data's forum: thread1, thread2).

4

1 に答える 1

1

tl;dr

Web レイヤー内のエンティティ間の参照は、リンクを使用して明示的にする必要があり、部分的に入力されたオブジェクト インスタンスの背後に隠れてはなりません。永続層の参照は、オブジェクト参照によって表されます。そのため、一方 (リンク) を他方 (完全に設定されたオブジェクト参照) に変換する専用のステップが必要です。

詳細

バックエンド ID をそのまま渡し、マーシャリング バインディングが正しいことを行うと想定することは、アンチ パターンです。したがって、クライアントはむしろリンクを操作し、それらをサーバーに渡して、既存のリソースと作成しようとしているリソースとの間の接続を確立したいことを示す必要があります。

Categoryを介して公開されている既存のものがあると仮定する/categories/4711と、サーバーに投稿できます。

POST /products
{ links : [ { rel : "category", href : "/categories/4711" } ],
  // further product data
}

サーバーは新しいProductインスタンスをインスタンス化し、追加のデータを入力して、最終的に次のように関連付けを設定します。

  1. リンク関係のタイプを検索して、入力するプロパティを特定します (たとえば、categoryここのプロパティ.
  2. 指定された URI からバックエンド識別子を抽出します
  3. 対応するリポジトリを使用して、関連するエンティティ インスタンスを検索します
  4. ルート エンティティに設定する

したがって、あなたの例では、次のように要約されます。

Product product = new Product();
// populate primitive properties
product.setCategory(categoryRepository.findOne(4711));
productRepository.save(product);

次のようなものをサーバーに投稿するだけです。

POST /products
{ category : {
    id : 1, … },
  … 
}

多くの理由で最適ではありません:

  1. 永続化プロバイダーにProductインスタンスを暗黙的に永続化させ、同時に、Category参照されているインスタンス (実際には id のみで構成されている) が永続化されることを意図したものではなく、既存のデータで更新されることを「認識する」必要がありますCategoryか? それは私が主張するかなりの魔法です。
  2. 基本的に、サーバーへの POST に使用するデータ構造を、POST を実行することにした方法で透過的に処理することを期待して、永続化レイヤーに課します。これは永続層の責任ではなく、Web 層の責任です。Web レイヤーの全体的な目的は、表現とバックエンド サービスへのリンクを使用して、HTTP ベースのプロトコルの特性を緩和することです。
于 2013-01-29T11:52:03.717 に答える