使用旧版本的实体类

时间:2016-08-16 15:47:18

标签: rest jpa jax-rs forward-compatibility

我有一个概念性的问题,我希望有人可能有答案。

我已经拥有什么?

我有一个RESTful服务,它为单一类型的实体提供类似CRUD的操作。实体(我们称之为订单)使用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"
  }
}

我想做什么?

现在,我想在实体Order中添加字段 field2 。该服务有多个客户端 - 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提供给服务:

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

或者, Client1 决定再次从field2中删除该值。因为该值现在为空,并且根据Jackson注释,不包括空值,以下JSON正在被PUT:

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

有什么问题?

此解决方案提供了一个严重的问题: 使用旧实体代表的客户将意外删除其未知的字段。

所以基本上它会破坏向前兼容性,这对我来说是个大问题!

我有一些可能的解决方案,但每个都有严重的缺点:

  1. 为实体的每次演变部署新版本的服务:Easy并且肯定会起作用,但会产生大量必须同时维护的版本。 / LI>
  2. 为每个版本使用特定媒体类型:这将允许客户端指定它所引用的实体版本。但是,我需要手动合并收到的实体之间的差异(考虑到哪个版本存在于哪个版本中)和数据库中的差异。
  3. 部分更新补丁:我可以将问题分成两个问题:
    • 创建补丁(客户端):我必须在修改之前和之后手动比较实体。
    • 应用补丁(服务器):我必须从数据库中选择实体并手动应用补丁。
  4. 我希望得到你的帮助

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

    很抱歉文本变得太长了,但我想要包含所有相关细节。非常感谢你的时间!

0 个答案:

没有答案