17

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

merge关于JPA 中的操作,有很多关于 SO 的讨论。还有一篇关于该主题的精彩文章,通过更手动的 Do-It-Yourself 流程(您可以通过实体管理器找到实体并进行更改)对比 JPA 合并。

我的应用程序有一个丰富的域模型(ala 域驱动设计),它使用@Version注释来利用乐观锁定。我们还创建了 DTO,以作为 RESTful Web 服务的一部分通过网络发送。这个 DTO 层的创建还允许我们向客户端发送它需要的所有东西,而不是它不需要的东西。

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

我不明白的是,JPA 合并甚至可以被视为处理更新的选项。这是我的想法,我想知道是否有我不明白的地方:

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

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

2) 合并不处理双向关系……反向字段总是以空值结束。

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

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

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

我有这个直吗?

编辑:

6)关于延迟加载关系的合并行为可能因提供者而异

4

2 回答 2

14

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

但是,如果您已经将操作简化为只传递直接相关的信息,那么您还需要手动编写相应的服务。

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

  1. 您不能更改提供者创建的实体的版本,但是当您使用new关键字创建自己的实体类实例时,它很好,并且可以在其上设置版本。

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

  3. 真的。

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

  5. 真的。

于 2013-01-03T15:16:52.900 回答
4

我要补充:

7) Merge 转换为插入或更新取决于 DB 上记录的存在,因此它不能正确处理 update-vs-delete 乐观并发。也就是说,如果另一个用户同时删除记录并且您更新它,它必须(1)抛出并发异常......但它没有,它只是将记录作为新记录插入。

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

于 2014-12-08T17:29:17.843 回答