JPA 在带有 DTO 和乐观锁定的 RESTful Web 应用程序中合并?
Posted
技术标签:
【中文标题】JPA 在带有 DTO 和乐观锁定的 RESTful Web 应用程序中合并?【英文标题】:JPA merge in a RESTful web application with DTOs and Optimistic Locking? 【发布时间】:2012-12-17 22:45:03 【问题描述】:我的问题是:JPA merge
在无状态 Web 应用程序中是否有过角色?
关于 JPA 中的merge
操作,有很多关于 SO 的讨论。还有一个great article on the subject,它通过更手动的 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) 合并不处理双向关系...后向字段总是以 null 结束。
3) 如果 DTO 中缺少任何字段或数据(由于部分更新),则 JPA 合并将删除这些关系或使这些字段无效。 Hibernate 可以处理部分更新,但不能处理 JPA 合并。 DIY 可以处理部分更新。
4) 合并方法首先会在数据库中查询实体 ID,因此与 DIY 相比没有性能优势。
5) 在 DYI 更新中,我们加载实体并根据 DTO 进行更改——没有调用 merge
或 persist
因为 JPA 上下文实现了 unit-of-开箱即用的工作模式。
我有这个直吗?
编辑:
6) 延迟加载关系的合并行为可以differ amongst providers。
【问题讨论】:
【参考方案1】:使用 Merge 确实需要您发送和接收实体的完整表示,或维护服务器端状态。对于琐碎的 CRUD-y 类型的操作,它简单方便。我已经在无状态 Web 应用程序中大量使用它,在这些应用程序中,让客户端看到整个实体没有任何有意义的安全隐患。
但是,如果您已经将操作简化为只传递直接相关的信息,那么您还需要手动编写相应的服务。
请记住,在进行“DIY”更新时,您仍然需要在 DTO 上传递一个版本号,并手动将其与数据库中的版本号进行比较。否则,如果您使用更简单的合并方法,您将无法获得跨越“用户思考时间”的乐观锁定。
您无法更改提供者创建的实体上的版本,但是当您使用 new
关键字创建自己的实体类实例时,它很好,并且可以在其上设置版本.
它将使持久表示与您提供的内存中表示相匹配,这可能包括使事物为空。请记住,当一个对象被合并时,该对象应该被丢弃并替换为合并返回的对象。您不应该合并一个对象然后继续使用它。规范未定义其状态。
是的。
很可能,只要您的 DIY 解决方案也使用实体 ID 而不是任意查询。 (在查询中使用“查找”方法还有其他好处。)
是的。
【讨论】:
【参考方案2】:我会补充:
7) 合并转换为插入或更新取决于数据库上记录的存在,因此它不能正确处理更新与删除乐观并发。也就是说,如果另一个用户同时删除了记录并且您对其进行了更新,它必须 (1) 引发并发异常......但它不会,它只是将记录作为新记录插入。
(1) 至少,在我看来,在大多数情况下,它应该。我可以想象一些我希望这个用例触发新插入的情况,但它们与往常相去甚远。至少,我希望开发者三思而后行,而不是仅仅接受“merge() == updateWithConcurrencyControl()”,因为事实并非如此。
【讨论】:
您对 7) 的担忧可以通过尝试手动加载对象来解决,如果在执行更新之前它不存在则失败。由于对象已经加载,merge 不会发出 select 语句。以上是关于JPA 在带有 DTO 和乐观锁定的 RESTful Web 应用程序中合并?的主要内容,如果未能解决你的问题,请参考以下文章