为什么不使用Spring的OpenEntityManagerInViewFilter

37 spring hibernate lazy-loading

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

  1. 过滤器拦截servlet请求
  2. 过滤器打开EntityManager并将其绑定到当前线程
  3. 调用Web控制器
  4. Web控制器调用服务
  5. 事务拦截器开始一个新事务,检索线程绑定的EntityManager并将其绑定到事务
  6. 调用Service,用EntityManager做一些东西,然后返回
  7. 事务拦截器刷新EntityManager然后提交事务
  8. Web控制器准备视图,然后返回
  9. 视图已构建
  10. Filter关闭EntityManager并将其与当前线程解除绑定

在步骤8和9中,仍然管理由线程的EntityManager加载的对象.因此,如果在这些步骤中触及了惰性关联,则将使用仍然打开的EntityManager从数据库加载它们.据我所知,每次这样的访问都要求数据库打开一个事务.Spring的事务管理将不会意识到这一点,因此我将其称为"隐式事务".

我看到2个问题:

  1. 加载多个惰性关联将导致多个数据库事务,这可能会影响性能
  2. 根对象及其惰性关联被加载到不同的数据库事务中,因此数据可能是陈旧的(例如,由线程1加载的根,由线程2更新的根关联,由线程1加载的根关联)

一方面,这两个问题似乎足以拒绝使用此过滤器(性能损失,数据不一致).另一方面,这个解决方案非常方便,避免编写几行代码,问题1可能不那么引人注意,问题2可能是纯粹的偏执狂.

你怎么看?

谢谢!

Bin*_*mas 9

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

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

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

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

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

恕我直言,OpenSessionInView更重要的缺点是当你希望你的服务层在非web上下文中重用时.根据您的描述,您似乎没有这个问题.