JPA:扩展持久化上下文 vs. 分离实体

11

实现跨多个HTTP请求的业务事务,JPA似乎有两种模式:

  1. 每个请求一个实体管理器,并使用脱离的实体
  2. 扩展持久性上下文

这些模式各自有哪些优点?应该在什么情况下选择哪种模式?

到目前为止,我想到了以下几点:

  • 扩展持久性上下文确保对象标识等同于数据库标识,简化编程模型,可能消除了实现实体equals方法的需要
  • 与扩展持久性上下文相比,脱离的实体需要更少的内存,因为持久性上下文还必须存储实体的先前状态以进行更改检测
  • 不再被引用的脱离实体可以进行垃圾回收;持久对象必须首先显式地脱离

然而,由于我没有使用过JPA,所以我肯定错过了一些重要的东西,因此提出了这个问题。

如果有影响的话:我们打算使用由Hibernate 3.6支持的JPA 2.0。

编辑:我们的视图技术是JSF 2.0,在EJB 3.1容器中使用CDI,可能还有Seam 3。

1个回答

18

嗯,我可以列举在Web环境中尝试使用扩展持久化上下文所面临的挑战。一些问题也取决于你的视图技术以及它是否绑定实体或视图级中间件。

  1. EntityManager不是线程安全的。您不需要每个用户会话一个EntityManager,而是需要每个用户会话每个浏览器选项卡一个EntityManager。
  2. 当异常从EntityManager中抛出时,它被认为是无效的,并且需要关闭并替换。如果您计划编写自己的框架扩展来管理扩展生命周期,则需要实现此功能。通常,在每个请求的EntityManager设置中,异常会转到某种错误页面,然后加载下一个页面会像它本来应该做的那样创建一个新的EntityManager。
  3. 对象相等性不会100%自动安全。如上所述,异常可能已经使先前加载的对象关联的上下文无效,因此现在获取的对象将不相等。这种假设还假定使用它的开发人员具有极高的JPA工作原理和EM处理方式的技能和理解水平。例如,当不需要时意外使用合并操作将返回一个新对象,它与其具有字段相同的前任对象不满足==(将合并视为SQL“更新”是一个极其常见的JPA新手“错误”,特别是因为它大多数时间都是一个无操作命令,所以就这样过去了)。
  4. 如果您正在使用绑定POJO(例如SpringMVC)的视图技术,并且计划直接将Web表单数据绑定到实体上,则会很快遇到麻烦。附加实体的更改将在下一次flush/commit时变为持久化状态,而不管它们是否在事务中完成。常见的错误是,Web表单传入并将一些无效数据绑定到实体上,验证失败并尝试返回一个屏幕以通知用户。构建错误屏幕涉及运行查询。查询触发持久化上下文的flush/commit。已绑定到附加实体的更改被刷新到数据库中。
希望引起SQL异常,但可能只是持久化了损坏的数据。

(如果编程不规范,会发生问题4,即使使用每个请求一个会话的方式,但您不必积极努力避免它。)


谢谢,这些是很有启发性的想法。我已经在问题中添加了关于我的技术观点的细节。不过,我不确定我完全理解你的第一点:使用分离实体时,我不会遇到同样的问题吗? - meriton
1
是的,如果你正在服务器端存储状态,并且支持多标签使用,你需要一些对话管理来告诉哪个标签的状态应该与哪个请求一起发送。我指出这一点更多是为了提高服务器端内存/资源消耗的差异。 - Affe
@Affe,我面临的问题与您在第4点中提到的相同。我的刷新模式为MANUAL,更新是长时间对话的一部分。第一次保存表单时,数据正确保存。但是当在刚刚呈现的页面上更改表单数据时,与您所述的情况完全相同。是否有方法可以避免这种情况。现在,我无法将此用例分解为基于请求的具有分离实体的内容。 - Amit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接