Hibernate RESTful应用程序中的乐观锁定

Hibernate RESTful应用程序中的乐观锁定,hibernate,rest,dto,optimistic-locking,Hibernate,Rest,Dto,Optimistic Locking,在工作中,我们正在开发一个RESTful应用程序,其中数据层将由Hibernate处理。但我们不确定如何处理实体上的更新 我们计划做以下工作: 1) 客户端通过id请求实体 2) Hibernate加载实体,请求的字段(始终带有版本)被复制到DTO,DTO被转换为JSON并发送到客户端 3) 客户端管理一些字段并将实体(带有版本号)发送回服务器。 4) 服务器接收转换为DTO的JSON。 5) 从Hibernate加载相应的实体,并将DTO的道具复制到该实体 =>即使设置了客户端的版本号,实体也

在工作中,我们正在开发一个RESTful应用程序,其中数据层将由Hibernate处理。但我们不确定如何处理实体上的更新

我们计划做以下工作:

1) 客户端通过id请求实体
2) Hibernate加载实体,请求的字段(始终带有版本)被复制到DTO,DTO被转换为JSON并发送到客户端
3) 客户端管理一些字段并将实体(带有版本号)发送回服务器。
4) 服务器接收转换为DTO的JSON。
5) 从Hibernate加载相应的实体,并将DTO的道具复制到该实体

=>即使设置了客户端的版本号,实体也始终被覆盖。 这是否意味着我们必须自己对照加载实例的版本号检查客户端的版本号,而不是Hibernate这样做

在具有会话的常规应用程序中,分离的实例保存在HttpSession中。每当客户端更新实体时,就会从HttpSession检索实例,并更新某些属性。每当Hibernate提交更新时,如果版本号<当前版本号,则会引发ObjectStaleException

这里的问题是,我们没有任何Http会话,因为我们正在尝试RESTful


有没有一种通用的解决方案来处理RESTful应用程序中的乐观锁定,而不是自己检查版本号?

您的策略很好。只需将来自客户端的版本号复制到加载的实体中(或使用
merge()
,这将执行相同的操作),当Hibernate刷新实体时,如果版本号已增加,则会出现乐观锁异常


你不需要自己检查任何东西。Hibernate为您执行检查。

自己检查版本的替代方法是只构造实体对象并调用
entityManager.merge
,如果同时更改版本,它也会触发乐观锁异常,但如果同时删除对象,则不会抛出乐观锁异常。要正确处理,你必须亲自检查

您不需要单独加载实体状态来实现这一点,而是可以利用实体状态库在JPA之上开发DTO,该库还实现了对乐观锁定的支持。 您的用例应该已经得到支持,尽管我还没有一个很好的SpringWebMVC集成,所以您现在必须自己做一些管道工作。我有一些想法,不过,这只是一个时间问题和利益相关方,直到整合将更加顺利

可更新的实体视图允许映射实体的子集,并且只刷新该子集。由于使用了脏跟踪,它准确地知道发生了什么变化,从而允许细粒度的刷新

因此,补丁支持的想法,这似乎是您在这里想要的,就是只获取一个对象的id空引用。为空意味着它没有数据,即所有空值。脏跟踪假设初始状态全部为空。您可以简单地将一个请求负载映射到这个对象上,如果一个值为null,它将不会识别它正在被更改,因此忽略它。如果设置了任何非null值,它将确定此类字段为脏字段,并且在刷新时,仅刷新脏值

我自己还没试过,但你可以这样做

// Create reference for the id, the object is empty i.e. all null except for the id
CustomerDTO dto = entityViewManager.getReference(CustomerDTO.class, someId);
// Map the payload on the DTO which will call setFoo(null) but that's ok, because that isn't considered being dirty
jsonMapper.map(requestPayload, dto);
// Flush dirty changes i.e. non-null values
entityViewManager.update(entityManager, dto);
@EntityView(Customer.class)
@UpdatableEntityView(mode = FlushMode.PARTIAL)
public interface CustomerDTO {
  @IdMapping Integer getId();
  String getName();
  void setName(String name);
  String getDesc();
  void setDesc(String desc);
}
使用
PARTIAL
flush模式时执行的更新查询将仅包含具有非空值的属性的set子句。DTO看起来像这样

// Create reference for the id, the object is empty i.e. all null except for the id
CustomerDTO dto = entityViewManager.getReference(CustomerDTO.class, someId);
// Map the payload on the DTO which will call setFoo(null) but that's ok, because that isn't considered being dirty
jsonMapper.map(requestPayload, dto);
// Flush dirty changes i.e. non-null values
entityViewManager.update(entityManager, dto);
@EntityView(Customer.class)
@UpdatableEntityView(mode = FlushMode.PARTIAL)
public interface CustomerDTO {
  @IdMapping Integer getId();
  String getName();
  void setName(String name);
  String getDesc();
  void setDesc(String desc);
}

如果没有脏东西,它甚至不会执行查询。

根据Hibernate规范,您不能更改加载对象的版本号。因此,我想我必须创建一个新实例来表示已更改的实体(使用旧版本号),然后将其与加载的实体合并?但在部分更新的情况下,合并将使某些字段无效。。。我仍然不知道如何处理这个问题。合并就是这样做的:它将分离实体的状态复制到附加实体,包括版本。如果它不这样做,乐观的锁定将被完全打破。明确设置版本或使用合并将具有相同的效果。文档的意思是,您不能为了人为增加版本号的值而更改版本号(因为这会在不应该的时候导致异常,或者在应该的时候不会导致异常)。我是否也可以使用saveOrUpdate()进行此操作?是的,您可以。但是我不喜欢saveOrUpdate()。它会导致附加实体链接到分离实体,这会使事情变得混乱。