为什么不使用Spring的OpenEntityManagerInViewFilter

时间:2009-10-08 14:36:47

标签: hibernate spring lazy-loading

虽然很多帖子都是关于Spring的OpenSession / EntityManagerInViewFilter的主题写的,但我找不到任何提到它的缺陷。根据我的理解,并假设使用@Transactional服务层的典型分层Web应用程序架构,过滤器的工作方式如下:

  1. 过滤器拦截servlet请求
  2. 过滤器打开EntityManager并将其绑定到当前线程
  3. 称为Web控制器
  4. Web控制器调用服务
  5. 事务拦截器开始一个新事务,检索线程绑定的EntityManager并将其绑定到事务
  6. 调用服务,使用EntityManager做一些事情,然后返回
  7. 事务拦截器刷新EntityManager然后提交事务
  8. Web控制器准备视图,然后返回
  9. 构建视图
  10. 过滤器关闭EntityManager并从当前线程取消绑定
  11. 在步骤8和9中,仍然管理由线程的EntityManager加载的对象。因此,如果在这些步骤中触及了惰性关联,则将使用仍然打开的EntityManager从数据库加载它们。据我所知,每次这样的访问都要求数据库打开一个事务。 Spring的事务管理将不会意识到这一点,因此我将其称为“隐式事务”。

    我发现有两个问题:

    1. 加载多个惰性关联将导致多个数据库事务,这可能会影响性能
    2. 根对象及其延迟关联被加载到不同的数据库事务中,因此数据可能是陈旧的(例如,由线程1加载的根,由线程2更新的根关联,由线程1加载的根关联)
    3. 一方面,这两个问题似乎足以拒绝使用此过滤器(性能损失,数据不一致)。另一方面,这个解决方案非常方便,避免编写几行代码,问题1可能不那么明显,问题2可能是纯粹的偏执狂。

      您怎么看?

      谢谢!

4 个答案:

答案 0 :(得分:9)

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

  

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

是的,进入数据库通常可能会导致性能问题。理想情况下,您希望在一次旅行中获取所需的所有数据。考虑使用Hibernate join-fetch。但是从DB中获取太多数据也会很慢。我使用的经验法则是每次绘制视图时都需要使用连接提取;如果在大多数情况下不需要数据,我会在需要时让Hibernate懒惰地获取它 - threadlocal open session会帮助它。

  

2)根对象及其惰性关联被加载到不同的数据库事务中,因此数据可能是陈旧的(例如,线程1加载根,线程2更新根关联,线程1加载根关联)。 / p>

想象一下在JDBC中编写这个应用程序 - 如果应用程序的一致性要求要求root和leaves都应该加载到同一个txn中,请使用join fetching。如果不是,通常就是这种情况,延迟抓取不会导致任何一致性问题。

恕我直言,OpenSessionInView更重要的缺点是你希望你的服务层在非网络环境中重用。根据您的描述,您似乎没有这个问题。

答案 1 :(得分:4)

我听到的反对OpenSessionInView和延迟加载的主要论点是过多的事务和对性能的负面影响。在使用率较低的应用程序上使用非常方便,但在大型应用程序中,我建议使用老式的完全填充的DTO(数据传输对象)。

答案 2 :(得分:4)

我遇到的OpenSessionInViewFilter的一个主要问题是使用AJAX应用程序和javascript。如果您使用javascript来呈现网格或某些UI组件;有一个懒惰的负载(考虑到你打开过滤器);并抛出异常。您的应用程序UI渲染需要折腾。数据可能永远不会显示,页面开始抛出奇怪的javascript异常,为此你需要编写额外的js代码来处理。而且您正在向用户公开db异常(不是一个好主意)。

在常规应用程序中,可以捕获这些异常并引发有效的用户异常。

答案 3 :(得分:1)

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