我有一个概念问题,我希望有人能回答。
我已经拥有了什么?
我有一个 RESTful 服务,它为单一类型的实体提供类似 CRUD 的操作。实体(我们称之为order)使用 Jackson 表示为 JSON。Jackson 注释直接附加到实体类,没有不同的数据传输层。实体类包含相当多的属性,一对一和一对多的关系。
在技术上,它基于 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添加到 entity Order
。该服务有多个客户端 - Client1和Client2。
Client1执行实体更新并为新字段赋予值newValue:
{
"id": 6000000197444,
"version": 358,
"correlationId": 4711,
"field2": "newValue",
"suborder": {
"id": 1000000005025,
"version": 40,
"field1": "value"
}
}
Client2对新领域不感兴趣。因此,没有开发新版本的Client2。现在,它想将值更改为anotherNewValuefield1
值。由于它不知道新字段,因此它将以下 JSON 放入服务:field2
{
"id": 6000000197444,
"version": 358,
"correlationId": 4711,
"suborder": {
"id": 1000000005025,
"version": 40,
"field1": "newValue"
}
}
或者,Client1决定再次删除该值field2
。因为该值现在是空的,并且根据 Jackson 注释,不包括空值,所以正在 PUT 中:
{
"id": 6000000197444,
"version": 358,
"correlationId": 4711,
"suborder": {
"id": 1000000005025,
"version": 40,
"field1": "newValue"
}
}
问题是什么?
这个解决方案提供了一个严重的问题: 使用旧实体表示的客户端会意外删除它不知道的字段。
所以基本上它会破坏前向兼容性,这对我来说是一个大问题!
我有一些可能的解决方案,但每一个都有严重的缺点:
- 为实体的每次演变部署一个新版本的服务:简单且肯定会工作,但会导致必须同时维护大量版本。
- 为每个版本使用特定的媒体类型:这将允许客户端指定它引用的实体的版本。但是,我需要手动合并接收到的实体(考虑到哪些字段存在于哪个版本中)和数据库中的实体之间的差异。
- 带有部分更新的补丁:我可以将问题分为两个问题:
- 创建补丁(客户端):我必须手动比较修改前后的实体。
- 应用补丁(服务器):我必须从数据库中选择实体并手动应用补丁。
我在哪里需要你的帮助
我认为这是一个非常普遍的问题,所以我希望你们中的一些人已经做过这样的事情。我必须提出一些具体问题: 1. 有谁知道允许在任意对象树上创建和应用补丁的 Java 库(如解决方案 2 和 3 中所要求的)?2.有人有更简单的解决方案吗?
抱歉,文本变得这么长,但我想包括所有相关细节。非常感谢您的宝贵时间!