6

我有一个概念问题,我希望有人能回答。

我已经拥有了什么?

我有一个 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.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添加到 entity Order。该服务有多个客户端 - Client1Client2

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. 为实体的每次演变部署一个新版本的服务:简单且肯定会工作,但会导致必须同时维护大量版本。
  2. 为每个版本使用特定的媒体类型:这将允许客户端指定它引用的实体的版本。但是,我需要手动合并接收到的实体(考虑到哪些字段存在于哪个版本中)和数据库中的实体之间的差异。
  3. 带有部分更新的补丁:我可以将问题分为两个问题:
    • 创建补丁(客户端):我必须手动比较修改前后的实体。
    • 应用补丁(服务器):我必须从数据库中选择实体并手动应用补丁。

我在哪里需要你的帮助

我认为这是一个非常普遍的问题,所以我希望你们中的一些人已经做过这样的事情。我必须提出一些具体问题: 1. 有谁知道允许在任意对象树上创建和应用补丁的 Java 库(如解决方案 2 和 3 中所要求的)?2.有人有更简单的解决方案吗?

抱歉,文本变得这么长,但我想包括所有相关细节。非常感谢您的宝贵时间!

4

0 回答 0