JPA在具有DTO和乐观锁定的RESTful Web应用程序中合并?

2022-09-03 02:05:07

我的问题是:在无状态Web应用程序中,JPA合并是否有角色?

关于JPA中的操作,有很多关于SO的讨论。还有一篇关于这个主题的很棒的文章,它通过更手动的DI-Yourself流程(通过实体管理器找到实体并进行更改)对比了JPA合并。merge

我的应用程序具有丰富的域模型(ala 域驱动设计),该模型使用注释来利用乐观锁定。我们还创建了 DTO,作为 RESTful Web 服务的一部分,通过网络发送。创建此DTO层还允许我们将所需的所有内容发送给客户端,而不需要的任何内容。@Version

到目前为止,我知道这是一个相当典型的架构。我的问题是关于需要更新(即HTTP PUT)现有对象的服务方法。在这种情况下,我们有这两种方法1)JPA合并和2)DIY。

我不明白的是,JPA合并如何被视为处理更新的选项。这是我的想法,我想知道是否有我不明白的东西:

1) 为了正确地从有线 DTO 创建分离的 JPA 实体,必须正确设置版本号...否则就会抛出一个 OptimisticLockException。但是JPA规范说:

实体可以访问其版本字段或属性的状态,或者导出一个方法供应用程序访问该版本,但不得修改版本值[30]。仅允许持久性提供程序在对象中设置或更新 version 属性的值。

2)合并不处理双向关系...反向指向字段始终以 null 结尾。

3) 如果 DTO 中缺少任何字段或数据(由于部分更新),则 JPA 合并将删除这些关系或将这些字段清空。休眠可以处理部分更新,但不能处理 JPA 合并。DIY可以处理部分更新。

4)合并方法要做的第一件事是查询数据库中的实体ID,因此与DIY相比没有性能优势。

5)在DYI更新中,我们加载实体并根据DTO进行更改 - 没有调用或调用,因为JPA上下文实现了开箱即用的工作单元模式。mergepersist

我有这个直吗?

编辑:

6) 与延迟加载关系相关的合并行为在提供程序之间可能有所不同


答案 1

使用 Merge 确实需要您发送和接收实体的完整表示形式,或维护服务器端状态。对于琐碎的CRUD-y型操作,它既简单又方便。我已经在无状态Web应用程序中大量使用它,在这些应用程序中,让客户端看到整个实体没有有意义的安全隐患。

但是,如果您已经将操作减少到仅传递直接相关的信息,则还需要手动编写相应的服务。

请记住,在进行“DIY”更新时,您仍然需要在DTO上传递版本号,并手动将其与数据库中的版本号进行比较。否则,您将无法获得跨越“用户思考时间”的乐观锁定,如果您使用更简单的合并方法,则会有这种锁定。

  1. 您无法更改提供程序创建的实体上的版本,但是当您使用关键字创建自己的实体类实例时,就可以了,并且应该在其上设置版本。new

  2. 它将使持久表示与你提供的内存中表示形式匹配,这可能包括使事物为空。请记住,当一个对象被合并时,该对象应该被丢弃并替换为合并返回的对象。您不应该合并一个对象,然后继续使用它。其状态不是由规范定义的。

  3. 真。

  4. 最有可能的是,只要您的DIY解决方案也使用实体ID而不是任意查询。(对查询使用“查找”方法还有其他好处。

  5. 真。


答案 2

我要补充一点:

7) 合并根据 DB 上是否存在记录转换为插入或更新,因此它不能正确处理更新与删除的乐观并发。也就是说,如果另一个用户同时删除了该记录,并且您对其进行了更新,则它必须 (1) 引发并发异常...但事实并非如此,它只是将记录作为新记录插入。

(1)至少,在大多数情况下,在我看来,它应该。我可以想象在某些情况下,我希望这个用例触发新的插入,但它们远非通常。至少,我希望开发人员三思而后行,而不仅仅是接受“merge() == updateWithConcurrencyControl()”,因为它不是。