简介
OpenSessionInViewFilter 是 Spring 框架中用于解决懒加载(Lazy Loading)问题的一个过滤器。在 Hibernate 或 JPA 等 ORM 框架中,当你从一个实体中访问一个未加载的关联实体时,如果启用了懒加载,那么该关联实体会在首次访问时从数据库中加载。这通常是通过在内部开启一个新的数据库会话来完成的。
然而,在 web 应用中,如果数据库会话在请求处理完成之前被关闭,那么在视图渲染阶段尝试访问懒加载的关联实体时,就会抛出异常,因为此时数据库会话已经关闭,无法再进行数据加载。
OpenSessionInViewFilter 的作用就是确保在一个 web 请求的整个生命周期中,数据库会话都是开启的。这样,即使在视图渲染阶段,也可以安全地访问懒加载的关联实体,而不会抛出异常。
具体来说,OpenSessionInViewFilter 是一个 Servlet 过滤器,它会在请求开始时开启一个数据库会话,并在请求结束时关闭它。这确保了在整个请求处理过程中,包括控制器逻辑和视图渲染阶段,都可以使用同一个数据库会话。
缺点
- 性能问题:由于在整个请求过程中都保持数据库会话开启,这可能会导致不必要的数据库连接占用,特别是在高并发的场景下,可能会对数据库服务器造成压力。
- 事务管理:OpenSessionInViewFilter 可能会干扰 Spring 的事务管理。如果事务在控制器逻辑完成后就被提交,但在视图渲染阶段又发生了异常,那么这个异常不会被事务捕获并回滚,因为此时事务已经提交。
源码
public class OpenSessionInViewFilter extends OncePerRequestFilter {//其他省略......@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {//获取hibernate中的SessionFactorySessionFactory sessionFactory = lookupSessionFactory(request);//标志位,表示当前请求是否参与现有的Session。boolean participate = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);String key = getAlreadyFilteredAttributeName();//检查当前线程是否已经有与sessionFactory关联的sessionif (TransactionSynchronizationManager.hasResource(sessionFactory)) {// Do not modify the Session: just set the participate flag.participate = true;}else {//没有绑定的sessionboolean isFirstRequest = !isAsyncDispatch(request);if (isFirstRequest || !applySessionBindingInterceptor(asyncManager, key)) {logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");//则打开一个新的sessionSession session = openSession(sessionFactory);//将session进行封装SessionHolder sessionHolder = new SessionHolder(session);//将session与当前线程进行绑定TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(sessionFactory, sessionHolder);asyncManager.registerCallableInterceptor(key, interceptor);asyncManager.registerDeferredResultInterceptor(key, interceptor);}}try {//继续向下执行filterChain.doFilter(request, response);}finally {if (!participate) {//最后将session与当前线程进行解绑SessionHolder sessionHolder =(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);if (!isAsyncStarted(request)) {logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");SessionFactoryUtils.closeSession(sessionHolder.getSession());}}}}//其他省略......
}