原:hibernate延迟加载延伸的一个问题

hibernate配置延迟加载报:failed to lazily initialize a collection of role: com.ln.jtf.biz.dal.dao.pojos.Customer.cusaccounts, no session or session was closed。原因是web上get子表时session已经关闭。针对这个问题spring提供了解决方案,有两种方式:

一种是过滤器:OpenSessionInViewFilter,在web.xml文件中增加配置如下:

	<filter>
		<filter-name>hibernateFilter</filter-name>
		<filter-class>
	org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
		</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>hibernateFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

看一下代码实现:

protected void doFilterInternal(
			HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		SessionFactory sessionFactory = lookupSessionFactory(request);
		boolean participate = false;

		if (isSingleSession()) {
			// single session mode
			if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
				// Do not modify the Session: just set the participate flag.
				participate = true;
			}
			else {
				logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
				Session session = getSession(sessionFactory);
				TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
			}
		}
		else {
			// deferred close mode
			if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
				// Do not modify deferred close: just set the participate flag.
				participate = true;
			}
			else {
				SessionFactoryUtils.initDeferredClose(sessionFactory);
			}
		}

		//.....
	}

 通过ROOT WebApplicationContext已获取一个普通spring bean的方式来获取SessionFactory 

protected SessionFactory lookupSessionFactory() {
		if (logger.isDebugEnabled()) {
			logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
		}
		WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
		return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
	}

 再看如何获取到WebApplicationContext:

public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
		Assert.notNull(sc, "ServletContext must not be null");
		Object attr = sc.getAttribute(attrName);
		if (attr == null) {
			return null;
		}
		if (attr instanceof RuntimeException) {
			throw (RuntimeException) attr;
		}
		if (attr instanceof Error) {
			throw (Error) attr;
		}
		if (attr instanceof Exception) {
			throw new IllegalStateException((Exception) attr);
		}
		if (!(attr instanceof WebApplicationContext)) {
			throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
		}
		return (WebApplicationContext) attr;
	}

 其中attrName为:String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

在某些配置下(?)由于根据这个attrName找不到对应的attr造成 sc.getAttribute(attrName)这个方法得到的是null,而报No WebApplicationContext found: no ContextLoaderListener registered?这个错误。

一种是配置拦截器:OpenSessionInViewInterceptor,在spring配置文件中增加如下(我使用的是Schema-based XML方式):

	<mvc:interceptors>
		<bean id="openSessionInViewInterceptor"
	           class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
	        <property name="sessionFactory" ref="sessionFactory"/>
	    </bean>
    </mvc:interceptors>

 这样配置之后session将会保持。

看一下代码实现:

public void preHandle(WebRequest request) throws DataAccessException {
		if ((isSingleSession() && TransactionSynchronizationManager.hasResource(getSessionFactory())) ||
		    SessionFactoryUtils.isDeferredCloseActive(getSessionFactory())) {
			// Do not modify the Session: just mark the request accordingly.
			String participateAttributeName = getParticipateAttributeName();
			Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
			int newCount = (count != null ? count + 1 : 1);
			request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
		}
		else {
			if (isSingleSession()) {
				// single session mode
				logger.debug("Opening single Hibernate Session in OpenSessionInViewInterceptor");
				Session session = SessionFactoryUtils.getSession(
						getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
				applyFlushMode(session, false);
				TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
			}
			else {
				// deferred close mode
				SessionFactoryUtils.initDeferredClose(getSessionFactory());
			}
		}
	}

可以看到在这里spring做了判断,如果当前请求线程中没有绑定session,那么就新创建一个,并且绑定到当前线程中。特别要提一下这里是通过HibernateAccessor这个InitializingBean来获取到SessionFactory的,和过滤器的方式不同。 

猜你喜欢

转载自liu-g927.iteye.com/blog/1570297