我的问题是: JPA merge
在无状态网络应用程序中是否有角色?
关于JPA中的merge
操作,有很多关于SO的讨论。还有一个great article on the subject与JPA合并形成鲜明对比,它通过更加手动的自己动手过程(通过实体管理器找到实体并进行更改)。
我的应用程序有一个丰富的域模型(ala域驱动设计),它使用@Version
注释来利用乐观锁定。作为RESTful Web服务的一部分,我们还创建了DTO以通过线路发送。创建这个DTO层还允许我们向客户端发送它需要的一切,而不是它没有。
到目前为止,我知道这是一个相当典型的架构。我的问题是关于需要UPDATE(即HTTP PUT)现有对象的服务方法。在这种情况下,我们有这两种方法1)JPA Merge,和2)DIY。
我不明白JPA合并如何被视为处理更新的选项。这是我的想法,我想知道是否有一些我不理解的东西:
1)为了从有线DTO正确创建分离的JPA实体,必须正确设置版本号...否则抛出OptimisticLockException。但JPA规范说:
实体可以访问其版本字段或属性的状态或 导出应用程序使用的方法来访问该版本,但是 不得修改版本值[30]。只有持久性提供者 允许设置或更新版本属性的值 对象。
2)Merge不处理双向关系......后向字段总是以null结尾。
3)如果DTO中缺少任何字段或数据(由于部分更新),则JPA合并将删除这些关系或使这些字段无效。 Hibernate可以处理部分更新,但不能处理JPA合并。 DIY可以处理部分更新。
4)合并方法要做的第一件事就是在数据库中查询实体ID,因此没有DIY的性能优势。
5)在DYI更新中,我们加载实体并根据DTO进行更改 - 没有调用merge
或persist
因为JPA上下文实现了开箱即用的工作单元模式。
我有这个吗?
修改
6)关于延迟加载关系的合并行为可以differ amongst providers。
答案 0 :(得分:13)
使用Merge确实需要您发送和接收实体的完整表示,或者维护服务器端状态。对于琐碎的CRUD-y型操作,它简单方便。我在无状态网络应用程序中使用了很多,其中没有任何有意义的安全隐患让客户看到整个实体。
但是,如果您已将操作简化为仅传递相关信息,则还需要手动编写相应的服务。
请记住,在进行“DIY”更新时,您仍需要在DTO上传递版本号,并手动将其与数据库中的版本号进行比较。否则,如果您使用更简单的方法进行合并,则不会获得跨越“用户思考时间”的乐观锁定。
您无法更改提供程序创建的实体的版本,但是当您使用new
关键字创建自己的实体类实例时,它很好,并且可以设置版本在它上面。
它将使持久表示与您提供的内存中表示匹配,这可以包括使事物为空。请记住,当合并一个对象时,该对象应该被丢弃并替换为merge返回的对象。您不应该合并对象然后继续使用它。它的状态不是由规范定义的。
真。
最有可能的是,只要你的DIY解决方案也使用实体ID而不是任意查询。 (在查询中使用'find'方法还有其他好处。)
真。
答案 1 :(得分:4)
我想补充一下:
7)Merge转换为插入或更新,具体取决于DB上记录的存在,因此它无法正确处理update-vs-delete乐观并发。也就是说,如果另一个用户同时删除记录并更新它,它必须(1)抛出一个并发异常......但它没有,它只是将记录作为新记录插入。
(1)至少,在大多数情况下,在我看来,它应该。我可以想象一些我希望这个用例触发新插入的情况,但它们远非平常。至少,我希望开发人员能够三思而后行,而不仅仅是接受" merge()== updateWithConcurrencyControl()",因为它不是。