为什么不使用Spring的OpenEntityManagerInViewFilter

虽然在Spring的OpenSession / EntityManagerInViewFilter的主题上写了很多post,但是我找不到任何提及它的缺陷。 据我所知,并假设使用@Transactional服务层的典型分层Web应用程序体系结构,filter的工作原理如下:

  1. filter拦截一个servlet请求
  2. filter打开一个EntityManager并将其绑定到当前线程
  3. networking控制器被调用
  4. Web控制器调用服务
  5. 事务拦截器开始一个新的事务,检索线程绑定的EntityManager并将其绑定到事务
  6. 服务被调用,用EntityManager做一些东西,然后返回
  7. 事务拦截器刷新EntityManager然后提交事务
  8. networking控制器准备视图,然后返回
  9. 视图被build立
  10. filterclosuresEntityManager并从当前线程中解除绑定

在步骤8和9中,由线程的EntityManager加载的对象仍然被pipe理。 因此,如果在这些步骤中使用了惰性关联,那么将使用仍然打开的EntityManager从数据库加载它们。 据我所知,每个这样的访问都要求数据库打开一个事务。 Spring的事务pipe理将不知道这一点,因此我称之为“隐式事务”。

我看到2个问题:

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

一方面,这两个问题似乎足以拒绝使用这个filter(性能命中,数据不一致)。 另一方面,这个解决scheme非常方便,避免写几行代码,问题1可能不那么明显,问题2可能是纯粹的偏执狂。

你怎么看?

谢谢!

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

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

是的,去DB往往会导致性能问题。 理想情况下,您想要在一次旅行中获取所有需要的数据。 考虑使用Hibernate的join-fetch。 但是从数据库中获取太多的数据也会很慢。 我使用的经验法则是如果每次绘制视图时都需要数据,则使用连接抓取; 如果在大多数情况下不需要数据,我会让Hibernate在需要的时候懒惰地获取它 – threadlocal打开会话然后帮助。

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

想象一下在JDBC中编写这个应用程序 – 如果应用程序的一致性要求要求根和叶两者都应该加载在同一个txn中,则使用联接提取。 如果不是的话,通常情况下,延迟提取不会导致任何一致性问题。

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

我听说过OpenSessionInView和延迟加载的主要参数是事务的过量和对性能的负面影响。 在使用率低的应用程序上使用非常方便,但在高规模应用程序中,我build议使用旧式的完全填充的DTO(数据传输对象)。

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

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

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