6

私は概念的な問題を抱えており、誰かが答えてくれることを願っています。

私はすでに何を持っていますか?

単一タイプのエンティティに対して CRUD のような操作を提供する RESTful サービスがあります。エンティティ ( orderと呼びましょう) は、Jackson を使用して JSON として表されます。Jackson アノテーションはエンティティ クラスに直接アタッチされており、明確なデータ トランスポート レイヤーはありません。エンティティ クラスには、1 対 1 および 1 対多の関係など、多数の属性が含まれています。

技術的には、Wildfly 10、JAX-RS 2.0、および JPA 2.1 (Hibernate 5 を使用) に基づいています。

現在サポートされている操作は次のとおりです。

  • GETを使用してエンティティをorders/123 読み取りますEntityManager.find
  • PUTは、またはを使用してエンティティorders/123 を作成または置換します。EntityManager.persistEntityManager.merge

コードサンプル

エンティティの簡易バージョンは次のようになります。

@Entity
@Table(name = "order")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_EMPTY)
public class Order {

    @Id
    @SequenceGenerator(name = "order_id", sequenceName = "order_id_seq", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="order_id")
    @Column(name = "id")
    private long id;

    @Version
    private long version;

    @Column(name = "correlation_id", unique = true)
    private long correlationId;

    @OneToOne(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JsonManagedReference
    private SubOrder subOrder;
}

@Entity
@Table(name = "suborder")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_EMPTY)
public class SubOrder {

    @Id
    @SequenceGenerator(name = "suborder_id", sequenceName = "suborder_id_seq", allocationSize=1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="suborder_id")
    @Column(name = "id")
    private long id;

    @Version
    private long version;

    @OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id", foreignKey = @ForeignKey(name = "suborder_order_FK1"))
    @JsonBackReference
    private Order order;

    @Column(name = "field1", length = CommonConstants.LENGTH_4, nullable = true)
    private String field1;
}

JSON 表現は次のようになります。

{
  "id": 6000000197444,
  "version": 358,
  "correlationId": 4711,
  "suborder": {
    "id": 1000000005025,
    "version": 40,
    "field1": "value"
  }
}

私は何をしたいですか?

ここで、フィールドfield2をエンティティに追加しますOrder。このサービスには複数のクライアント ( Client1Client2 ) があります。

Client1はエンティティの更新を実行し、新しいフィールドに値newValueを与えます。

{
  "id": 6000000197444,
  "version": 358,
  "correlationId": 4711,
  "field2": "newValue",
  "suborder": {
    "id": 1000000005025,
    "version": 40,
    "field1": "value"
  }
}

Client2は、新しいフィールドには関心がありません。したがって、Client2の新しいバージョンは開発されません。field1ここで、の値をanotherNewValueの値に変更したいと考えています。新しいフィールドがわからないためfield2、次の JSON をサービスに PUT します。

{
  "id": 6000000197444,
  "version": 358,
  "correlationId": 4711,
  "suborder": {
    "id": 1000000005025,
    "version": 40,
    "field1": "newValue"
  }
}

あるいは、Client1field2はもう一度値を削除することを決定します。値が空になり、Jackson アノテーションによると空の値が含まれていないため、次の JSON が PUT されています。

{
  "id": 6000000197444,
  "version": 358,
  "correlationId": 4711,
  "suborder": {
    "id": 1000000005025,
    "version": 40,
    "field1": "newValue"
  }
}

何が問題ですか?

この解決策には深刻な問題 があります。エンティティの古い表現を使用しているクライアントは、不明なフィールドを誤って削除してしまいます。

基本的に、それは私にとって大きな問題である前方互換性を壊します!

考えられる解決策はいくつかありますが、それぞれに重大な欠点があります。

  1. エンティティの進化ごとに新しいバージョンのサービスを展開する: 簡単で確実に機能しますが、同時に維持する必要がある多数のバージョンが発生します。
  2. Use a specific media type for each version : これにより、クライアントは参照するエンティティのバージョンを指定できます。ただし、受信したエンティティ (どのフィールドがどのバージョンに存在するかを考慮して) とデータベース内のエンティティとの違いを手動でマージする必要があります。
  3. 部分的な更新を伴うパッチ: 問題を 2 つの問題に分けることができ ます。
    • パッチ (クライアント) を作成します。変更前と変更後のエンティティを手動で比較する必要があります。
    • パッチを適用する (サーバー): データベースからエンティティを選択し、パッチを手動で適用する必要があります。

あなたの助けが欲しいところ

これはかなり一般的な問題だと思いますので、すでにこのようなことをしている方もいらっしゃると思います。具体的な質問があります: 1. 任意のオブジェクト ツリーにパッチを作成して適用できる Java ライブラリを知っている人はいますか (ソリューション 2 と 3 で必要)。2. 誰かがより簡単な解決策を持っていますか?

テキストが長くなって申し訳ありませんが、関連するすべての詳細を含めたかったのです。お時間をいただきありがとうございました!

4

0 に答える 0