为什么不使用Spring的OpenEntityManagerInViewFilter

39
虽然已经有很多文章讨论了Spring的OpenSession/EntityManagerInViewFilter,但我找不到任何一篇提到它的缺点。根据我的理解,假设使用@Transactional服务层的典型分层Web应用程序架构,该过滤器的工作方式如下:
  1. 过滤器拦截Servlet请求
  2. 过滤器打开一个EntityManager并将其绑定到当前线程
  3. 调用Web控制器
  4. Web控制器调用服务
  5. 事务拦截器开始一个新事务,检索与线程绑定的EntityManager并将其绑定到事务
  6. 调用服务,使用EntityManager执行一些操作,然后返回
  7. 事务拦截器刷新EntityManager,然后提交事务
  8. Web控制器准备视图,然后返回
  9. 构建视图
  10. 过滤器关闭EntityManager并将其从当前线程解绑
在步骤8和9中,由线程的EntityManager加载的对象仍然受到管理。因此,如果在这些步骤中触及惰性关联,则将使用仍处于打开状态的EntityManager从数据库中加载它们。据我所知,每个这样的访问都需要数据库开启一个事务。Spring的事务管理将不会意识到这一点,因此我称之为“隐式事务”。
我认为这有两个问题:
  1. 加载多个惰性关联将导致多个数据库事务,可能会影响性能
  2. 根对象及其惰性关联在不同的数据库事务中加载,因此数据可能过时(例如,由线程1加载的根,在线程2更新后由线程1加载它的关联)
一方面,这两个问题似乎足以拒绝使用该过滤器(性能下降,数据不一致)。另一方面,这个解决方案非常方便,避免了编写几行代码,问题1可能并不那么明显,问题2可能只是多虑。
你怎么看?
谢谢!
4个回答

10

正如您所说,OpenSessionInView 过滤器在Web应用程序中非常方便。关于您提到的限制:

  

1)加载多个延迟关联将导致多个数据库事务,可能会影响性能。

是的,经常访问数据库可能会导致性能问题。理想情况下,您希望在一次查询中获取所需的所有数据。考虑使用 Hibernate 的 join-fetch。但是从数据库中获取过多的数据也会很慢。我使用的经验法则是:如果每次绘制视图都需要数据,则使用 join-fetch;如果大多数情况下不需要数据,则让 Hibernate 在需要时进行延迟获取-线程本地打开会话有助于此。

  

2)根对象及其延迟关联在不同的数据库事务中加载,因此数据可能过期(例如,由线程1加载的根,被线程2更新了根的关联,然后再由线程1加载根的关联)。

想象一下在 JDBC 中编写此应用程序——如果应用程序的一致性要求是根和叶子节点都应在同一个事务中加载,请使用 join-fetch。否则,通常情况下,延迟获取不会引起任何一致性问题。

在我看来,OpenSessionInView 更重要的缺点是当您希望在非 Web 上下文中重用服务层时。从您的描述中,似乎您没有这个问题。


4
我遇到的OpenSessionInViewFilter的主要问题之一是与AJAX应用程序和javascript的使用有关。如果您正在使用javascript来渲染网格或某些UI组件,则会出现懒加载(考虑打开过滤器)并抛出异常。您的应用程序UI渲染将受到影响。数据可能永远不会显示,页面开始抛出奇怪的javascript异常,您需要编写额外的js代码来处理。而且,您会向用户公开数据库异常(这不是一个好主意)。
在常规应用程序中,可以捕获这些异常并抛出有效的用户异常。

4

我听到关于OpenSessionInView和懒加载的主要反对意见是会增加事务量并对性能产生负面影响。在低使用需求的应用上使用它非常方便,但在高规模应用上,我建议使用传统的完全填充DTO(数据传输对象)。


1
如果您的应用程序是多层架构(视图层部署在不同的JVM上,服务层将部署在不同的VM上),那么保持会话处于打开状态就没有意义了。如果您的服务层独立于应用程序层,则不使用任何OpenSessionViewFilter。

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