私は概念的な問題を抱えており、誰かが答えてくれることを願っています。
私はすでに何を持っていますか?
単一タイプのエンティティに対して 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.persist
EntityManager.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
。このサービスには複数のクライアント ( Client1とClient2 ) があります。
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"
}
}
何が問題ですか?
この解決策には深刻な問題 があります。エンティティの古い表現を使用しているクライアントは、不明なフィールドを誤って削除してしまいます。
基本的に、それは私にとって大きな問題である前方互換性を壊します!
考えられる解決策はいくつかありますが、それぞれに重大な欠点があります。
- エンティティの進化ごとに新しいバージョンのサービスを展開する: 簡単で確実に機能しますが、同時に維持する必要がある多数のバージョンが発生します。
- Use a specific media type for each version : これにより、クライアントは参照するエンティティのバージョンを指定できます。ただし、受信したエンティティ (どのフィールドがどのバージョンに存在するかを考慮して) とデータベース内のエンティティとの違いを手動でマージする必要があります。
- 部分的な更新を伴うパッチ: 問題を 2 つの問題に分けることができ
ます。
- パッチ (クライアント) を作成します。変更前と変更後のエンティティを手動で比較する必要があります。
- パッチを適用する (サーバー): データベースからエンティティを選択し、パッチを手動で適用する必要があります。
あなたの助けが欲しいところ
これはかなり一般的な問題だと思いますので、すでにこのようなことをしている方もいらっしゃると思います。具体的な質問があります: 1. 任意のオブジェクト ツリーにパッチを作成して適用できる Java ライブラリを知っている人はいますか (ソリューション 2 と 3 で必要)。2. 誰かがより簡単な解決策を持っていますか?
テキストが長くなって申し訳ありませんが、関連するすべての詳細を含めたかったのです。お時間をいただきありがとうございました!