在一次请求内绑定一个Session到线程,防止Web资源加载时的懒加载问题。HibernateTransactionManager或JTATransactionManager将会使用此Session。非事务运行时(并非事务挂起)也可使用。
该类的flushMode设置为Manual,由于该类一般为事务层提供Session,当Session同步到事务,对于非只读事务(definition指定)由HibernateTransactionManager或SessionFactoryUtils改变flushMode为Auto。如果不在Spring提供事务环境下使用此类,则合理的设置flushMode
singleSession是否在整个请求中使用一个Session,还是每个事务块或操作快使用一个Session,true说明整个请求使用一个Session。对于单个Session来说它从分利用了一级缓存,缺点在于部分的事务失败或访问失败,整个Session必须被废弃;每个事务块或操作快使用一个Session,对事务回滚或方位失败都是可以由应用程序控制重新发起一次事务,同时开相应的Session,当然缺点在于增加了内存消耗。对非单个Session模式来说,延迟加载也是支持的,它使用延迟关闭Session策略。
具体实现上
单个Session:Filter或Interceptor负责开Session和绑定SessionHolder到当前线程供整个请求使用,渲染页面之后关闭Session
每个事务或操作块(HibernateTemplate,HibernateInterceptor)一个Session:Filter或Interceptor负责标识SessionFactory延迟,每个事务或操作块会在代理实现中打开一个Session同时处理具体代理操作之后将新开的Session添加到SessionFactory对应的延迟戴处理的集合中,渲染页面之后关闭该SessionFactory对应的Session集合。
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.io.IOException;
import java.util.concurrent.Callable;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Servlet 2.3 Filter that binds a Hibernate Session to the thread for the entire
* processing of the request. Intended for the "Open Session in View" pattern,
* i.e. to allow for lazy loading in web views despite the original transactions
* already being completed.
*
* <p>This filter makes Hibernate Sessions available via the current thread, which
* will be autodetected by transaction managers. It is suitable for service layer
* transactions via {@link org.springframework.orm.hibernate3.HibernateTransactionManager}
* or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
* as for non-transactional execution (if configured appropriately).
*
* <p><b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate Session,
* with the flush mode set to {@code FlushMode.NEVER}. It assumes to be used
* in combination with service layer transactions that care for the flushing: The
* active transaction manager will temporarily change the flush mode to
* {@code FlushMode.AUTO} during a read-write transaction, with the flush
* mode reset to {@code FlushMode.NEVER} at the end of each transaction.
* If you intend to use this filter without transactions, consider changing
* the default flush mode (through the "flushMode" property).
*
* <p><b>WARNING:</b> Applying this filter to existing logic can cause issues that
* have not appeared before, through the use of a single Hibernate Session for the
* processing of an entire request. In particular, the reassociation of persistent
* objects with a Hibernate Session has to occur at the very beginning of request
* processing, to avoid clashes with already loaded instances of the same objects.
*
* <p>Alternatively, turn this filter into deferred close mode, by specifying
* "singleSession"="false": It will not use a single session per request then,
* but rather let each data access operation or transaction use its own session
* (like without Open Session in View). Each of those sessions will be registered
* for deferred close, though, actually processed at request completion.
*
* <p>A single session per request allows for most efficient first-level caching,
* but can cause side effects, for example on {@code saveOrUpdate} or when
* continuing after a rolled-back transaction. The deferred close strategy is as safe
* as no Open Session in View in that respect, while still allowing for lazy loading
* in views (but not providing a first-level cache for the entire request).
*
* <p>Looks up the SessionFactory in Spring's root web application context.
* Supports a "sessionFactoryBeanName" filter init-param in {@code web.xml};
* the default bean name is "sessionFactory". Looks up the SessionFactory on each
* request, to avoid initialization order issues (when using ContextLoaderServlet,
* the root application context will get initialized <i>after</i> this filter).
*
* @author Juergen Hoeller
* @since 1.2
* @see #setSingleSession
* @see #setFlushMode
* @see #lookupSessionFactory
* @see OpenSessionInViewInterceptor
* @see org.springframework.orm.hibernate3.HibernateInterceptor
* @see org.springframework.orm.hibernate3.HibernateTransactionManager
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenSessionInViewFilter extends OncePerRequestFilter {
public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
private boolean singleSession = true;
private FlushMode flushMode = FlushMode.MANUAL;
/**
* Set the bean name of the SessionFactory to fetch from Spring's
* root application context. Default is "sessionFactory".
* @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
*/
public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
this.sessionFactoryBeanName = sessionFactoryBeanName;
}
/**
* Return the bean name of the SessionFactory to fetch from Spring's
* root application context.
*/
protected String getSessionFactoryBeanName() {
return this.sessionFactoryBeanName;
}
/**
* Set whether to use a single session for each request. Default is "true".
* <p>If set to "false", each data access operation or transaction will use
* its own session (like without Open Session in View). Each of those
* sessions will be registered for deferred close, though, actually
* processed at request completion.
* @see SessionFactoryUtils#initDeferredClose
* @see SessionFactoryUtils#processDeferredClose
*/
public void setSingleSession(boolean singleSession) {
this.singleSession = singleSession;
}
/**
* Return whether to use a single session for each request.
*/
protected boolean isSingleSession() {
return this.singleSession;
}
/**
* Specify the Hibernate FlushMode to apply to this filter's
* {@link org.hibernate.Session}. Only applied in single session mode.
* <p>Can be populated with the corresponding constant name in XML bean
* definitions: e.g. "AUTO".
* <p>The default is "MANUAL". Specify "AUTO" if you intend to use
* this filter without service layer transactions.
* @see org.hibernate.Session#setFlushMode
* @see org.hibernate.FlushMode#MANUAL
* @see org.hibernate.FlushMode#AUTO
*/
public void setFlushMode(FlushMode flushMode) {
this.flushMode = flushMode;
}
/**
* Return the Hibernate FlushMode that this filter applies to its
* {@link org.hibernate.Session} (in single session mode).
*/
protected FlushMode getFlushMode() {
return this.flushMode;
}
/**
* The default value is "false" so that the filter may re-bind the opened
* {@code Session} to each asynchronously dispatched thread and postpone
* closing it until the very last asynchronous dispatch.
*/
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
/**
* Returns "false" so that the filter may provide a Hibernate
* {@code Session} to each error dispatches.
*/
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
String key = getAlreadyFilteredAttributeName();
if (isSingleSession()) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
}
else {
boolean isFirstRequest = !isAsyncDispatch(request);
if (isFirstRequest || !applySessionBindingInterceptor(asyncManager, key)) {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
SessionHolder sessionHolder = new SessionHolder(session);
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
asyncManager.registerCallableInterceptor(key,
new SessionBindingCallableInterceptor(sessionFactory, sessionHolder));
}
}
}
else {
// deferred close mode
Assert.state(!isAsyncStarted(request), "Deferred close mode is not supported on async dispatches");
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
}
else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
try {
filterChain.doFilter(request, response);
}
finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
if (!isAsyncStarted(request)) {
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(sessionHolder.getSession(), sessionFactory);
}
}
else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
}
/**
* Look up the SessionFactory that this filter should use,
* taking the current HTTP request as argument.
* <p>The default implementation delegates to the {@link #lookupSessionFactory()}
* variant without arguments.
* @param request the current request
* @return the SessionFactory to use
*/
protected SessionFactory lookupSessionFactory(HttpServletRequest request) {
return lookupSessionFactory();
}
/**
* Look up the SessionFactory that this filter should use.
* <p>The default implementation looks for a bean with the specified name
* in Spring's root application context.
* @return the SessionFactory to use
* @see #getSessionFactoryBeanName
*/
protected SessionFactory lookupSessionFactory() {
if (logger.isDebugEnabled()) {
logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
}
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
}
/**
* Get a Session for the SessionFactory that this filter uses.
* Note that this just applies in single session mode!
* <p>The default implementation delegates to the
* {@code SessionFactoryUtils.getSession} method and
* sets the {@code Session}'s flush mode to "MANUAL".
* <p>Can be overridden in subclasses for creating a Session with a
* custom entity interceptor or JDBC exception translator.
* @param sessionFactory the SessionFactory that this filter uses
* @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
* @see org.hibernate.FlushMode#MANUAL
*/
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
FlushMode flushMode = getFlushMode();
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
/**
* Close the given Session.
* Note that this just applies in single session mode!
* <p>Can be overridden in subclasses, e.g. for flushing the Session before
* closing it. See class-level javadoc for a discussion of flush handling.
* Note that you should also override getSession accordingly, to set
* the flush mode to something else than NEVER.
* @param session the Session used for filtering
* @param sessionFactory the SessionFactory that this filter uses
*/
protected void closeSession(Session session, SessionFactory sessionFactory) {
SessionFactoryUtils.closeSession(session);
}
private boolean applySessionBindingInterceptor(WebAsyncManager asyncManager, String key) {
if (asyncManager.getCallableInterceptor(key) == null) {
return false;
}
((SessionBindingCallableInterceptor) asyncManager.getCallableInterceptor(key)).initializeThread();
return true;
}
/**
* Bind and unbind the Hibernate {@code Session} to the current thread.
*/
private static class SessionBindingCallableInterceptor extends CallableProcessingInterceptorAdapter {
private final SessionFactory sessionFactory;
private final SessionHolder sessionHolder;
public SessionBindingCallableInterceptor(SessionFactory sessionFactory, SessionHolder sessionHolder) {
this.sessionFactory = sessionFactory;
this.sessionHolder = sessionHolder;
}
@Override
public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
initializeThread();
}
@Override
public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
}
private void initializeThread() {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
}
}
分享到:
相关推荐
OpenSessionInViewFilter是Spring框架中一个非常重要的组件,主要用于解决数据访问层(DAO)与视图层(View)之间的事务管理问题。在Web应用中,由于HTTP请求的无状态性,一次请求通常由多个Servlet过滤器、控制器和...
- 在高并发场景下,需要注意OpenSessionInViewFilter可能会导致的性能问题,因为它使得Session的生命周期变长,增加了Session占用资源的时间。 - 如果项目中同时使用了其他Session管理机制,需要确保它们之间不会...
在探讨如何通过`OpenSessionInViewFilter`来有效管理Hibernate中的Session生命周期之前,我们需要先理解Session及其生命周期的基本概念。 #### Session与生命周期 在Hibernate框架中,`Session`是执行数据库操作的...
该模式的核心在于通过Spring提供的`OpenSessionInViewFilter`过滤器,在视图渲染过程中保持Hibernate Session的打开状态,从而简化了事务管理,并避免了一些常见的懒加载异常。 #### 一、OpenSessionInViewFilter...
在处理Web应用时,Spring提供了一些关键特性,如`CharacterEncodingFilter`和`OpenSessionInViewFilter`,它们对于解决特定问题至关重要。 首先,让我们深入了解一下`CharacterEncodingFilter`。在Web应用中,字符...
### Spring监听器与过滤器详解...- **Spring Web环境下的监听器和过滤器**:在Spring MVC环境中,除了上述提到的OpenSessionInViewFilter和CharacterEncodingFilter,还有多种其他类型的过滤器和监听器可以使用,例如`...
2. **OpenSessionInViewFilter**:通过Web容器的过滤器(filter)来实现。 #### 异常原因 当使用Open Session In View模式时,如果Session的Flush Mode被设置为NEVER,并且尝试执行写操作(如更新或删除),就会触发...
在`web.xml`中配置Spring监听器以初始化Spring容器,并添加`OpenSessionInViewFilter`以解决懒加载问题: ```xml <listener-class>org.springframework.web.context.ContextLoaderListener <filter-name>...
当请求处理完毕后,通过`OpenSessionInViewFilter`或`OpenSessionInViewInterceptor`,Session会在合适的时机被自动关闭,释放资源。 `OpenSessionInViewInterceptor`是在Spring的MVC环境中配置的拦截器,它需要在`...
在 OpenSessionInViewFilter 中,需要配置 Hibernate 的 Session 管理,以便实现 Hibernate 的 Session 的打开和关闭。 9. web.xml 配置 在 web.xml 文件中,需要配置 Servlet 监听器、上下文变量、Filter 等,...
在`web.xml`中,Struts2的配置通过`StrutsPrepareAndExecuteFilter`进行,而Spring与Hibernate的集成则通过`OpenSessionInViewFilter`实现。这些配置保证了请求到达时,Struts2能正确处理请求,同时Spring能够管理和...
这个案例提供了一个基础的登录功能,同时展示了S2SH集成的各个方面,包括OpenSessionInViewFilter的使用、声明式事务管理以及三层架构的设计。 首先,让我们从Struts2开始。Struts2作为控制层,负责处理HTTP请求,...
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter ``` - **`OpenSessionInViewFilter`**:开启Hibernate的Session,在视图渲染完成后关闭Session,确保每个HTTP请求都在同一个...
Spring框架还提供了一系列的过滤器来处理Web应用中的请求,例如OpenSessionInViewFilter用于解决Hibernate懒加载问题。 - **OpenSessionInViewFilter**:此过滤器在请求处理开始时打开一个Hibernate会话,在请求...
在这里,我们使用了 OpenSessionInViewFilter 来实现 Hibernate 的 Session 的管理。同时,我们还使用了 JQuery 库来实现 JSON 数据的处理。 五、CSS 样式表的实现 在 CSS 样式表中,我们使用了 cursor:pointer ...
- **Hibernate会话管理过滤器**:OpenSessionInViewFilter确保在每个请求的生命周期内保持一个打开的Hibernate Session,从而实现懒加载: ```xml <filter-name>OpenSessionInViewFilter <filter-class>org....
- 在`web.xml`中设置Spring的`ContextLoaderListener`监听器加载上下文,以及`OpenSessionInViewFilter`过滤器,确保每个请求都有一个打开的Session。 - 配置Struts2的Filter,确保请求匹配`.action`结尾的URL,...
2. **OpenSessionInView模式**: 为了防止Session在请求结束后关闭导致数据丢失,我们可以使用`OpenSessionInViewFilter`。这个过滤器在每次请求开始时打开Session,结束时关闭,保证在整个视图渲染过程中Session都是...
- **解决方案**:使用 OpenSessionInViewFilter 过滤器,在请求开始时打开 Session,并在请求结束时关闭 Session。 2. **DAO层**:使用 Hibernate 来实现数据访问操作。 3. **业务层**:利用 Spring 的依赖注入和...