在工作中,我们正在开发一个RESTful应用程序,其中数据层将由Hibernate处理。但我们不确定如何处理实体更新。
我们计划执行以下操作:
1)客户通过身份请求实体
2)Hibernate加载实体,请求的字段(始终带有版本)被复制到转换为JSON并发送到客户端的DTO
3)客户端管理一些字段并将实体(带有版本号)发送回服务器
4)服务器接收转换为DTO的JSON
5)从Hibernate加载相应的实体,并将DTO的道具复制到实体。
=>即使设置了客户端的版本号,也始终会覆盖该实体。 这是否意味着我们总是必须自己检查客户端的版本号与已加载实例的版本号,而不是Hibernate这样做?
在具有会话的常规应用程序中,分离的实例保存在HttpSession中。每当客户端更新实体时,都会从HttpSession中检索实例,并更新一些属性。每当Hibernate提交更新时,如果版本号为<,则抛出ObjectStaleException。当前的版本号。
这里的问题是我们没有任何Http会话,因为我们正试图成为RESTful。
是否有一个通用的解决方案来处理RESTful应用程序中的乐观锁定,而不是自己检查版本号?
答案 0 :(得分:3)
你的策略很好。只需将来自客户端的版本号复制到加载的实体中(或使用merge()
,这将执行相同的操作),当Hibernate刷新实体时,如果版本号已增加,您将会乐观锁定异常。
您不需要自己检查任何内容。 Hibernate为你做检查。
答案 1 :(得分:1)
您自己检查版本的另一种方法是只构造实体对象并调用entityManager.merge
,如果同时更改版本,它也应该触发乐观锁异常,但是如果删除该对象则不会抛出异常同时。要正确处理此问题,您必须自己检查一下。
您可以使用Blaze-Persistence Updatable Entity Views来代替通过分别加载实体状态来实现此目的,它是在JPA之上用于开发DTO的库,该库还实现了对乐观锁定的支持。 您的用例应该已经得到支持,尽管我还没有与Spring WebMvc很好的集成,所以您现在必须自己做一些工作。不过,我有一些想法,在整合变得更加顺利之前,这只是时间问题和感兴趣的参与者。
可更新的实体视图允许映射实体的子集,并且也只能刷新该子集。由于使用了脏跟踪,它可以准确地知道发生了什么变化,从而可以进行细粒度的冲洗。
因此 PATCH支持的想法似乎就是您想要的,只是通过对象的id获得一个空引用。为空表示没有数据,即所有空值。脏跟踪假定初始状态全为空。您可以简单地将请求有效负载映射到该对象,如果值为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);
在使用PARTIAL
刷新模式时执行的更新查询将仅包含具有非空值的属性的set子句。 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);
}
如果没有脏东西,它甚至不会执行查询。