OpenSessionInViewFilter 的配置及替代方案
Spring 为我们提供了一个叫做 OpenSessionInViewFilter 的过滤器,他是标准的 Servlet Filter 所以我们把它按照规范配置到 web.xml 中方可使用。使用中我们必须配合使用 Spring 的 HibernateDaoSupport 来进行开发,也就是说,我们的dao层的类都要继承于 HibernateDaoSupport,从中由 Spring 来控制 Hibernate 的 Session 在请求来的时候开启,走的时候关闭,保证了我们访问数据对象时的稳定性。
1. 在 web.xml 中加入对应过滤器配置文件
- <!-- Spring的OpenSessionInView实现 -->
- <filter>
- <filter-name>openSessionInViewFilter</filter-name>
- <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
- </filter-class>
- <!-- singleSession默认为true,若设为false则等于没用OpenSessionInView 。所以默认可以不写-->
- <init-param>
- <param-name>singleSession</param-name>
- <param-value>true</param-value>
- </init-param>
- <!--
- 指定org.springframework.orm.hibernate3.LocalSessionFactoryBean在spring配置文件中的名称,默认值为sessionFactory。 如果LocalSessionFactoryBean在spring中的名称不是sessionFactory,该参数一定要指定,否则会出现找不到sessionFactory的例外。所以默认可以不写
- -->
- <init-param>
- <param-name>sessionFactoryBean</param-name>
- <param-value>sessionFactory</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>openSessionInViewFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
2. 在我们访问持久层数据是使用 Spring 为我们的 HibernateDaoSupport 的支持,并使用其中的对应方法操作我们的持久层数据
- import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
- public class XxxDAO extends HibernateDaoSupport {
- public void save(Xxx transientInstance) {
- try {
- getHibernateTemplate().save(transientInstance);
- } catch (RuntimeException re) {
- throw re;
- }
- }
- }
OpenSessionInViewFilter的主要功能是用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。Open Session In View在request把session绑定到当前thread期间一直保持hibernate session在open状态,使session在request的整个期间都可以使用,如在View层里PO也可以lazy loading数据,如 ${ company.employees }。当View 层逻辑完成后,才会通过Filter的doFilter方法或Interceptor的postHandle方法自动关闭session。
很多人在使用OpenSessionInView过程中提及一个错误:
org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER) – turn your Session into FlushMode.AUTO or remove ‘readOnly’ marker from transaction definition
看看OpenSessionInViewFilter里的几个方法:
- protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)
- throws ServletException, IOException {
- SessionFactory sessionFactory = lookupSessionFactory();
- logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
- Session session = getSession(sessionFactory);
- TransactionSynchronizationManager.bindResource(
- sessionFactory, new SessionHolder(session));
- try {
- filterChain.doFilter(request, response);
- }
- finally {
- TransactionSynchronizationManager.unbindResource(sessionFactory);
- logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
- closeSession(session, sessionFactory);
- }
- }
- protected Session getSession(SessionFactory sessionFactory)
- throws DataAccessResourceFailureException {
- Session session = SessionFactoryUtils.getSession(sessionFactory, true);
- session.setFlushMode(FlushMode.NEVER);
- return session;
- }
- protected void closeSession(Session session, SessionFactory sessionFactory)
- throws CleanupFailureDataAccessException {
- SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);
- }
可以看到OpenSessionInViewFilter在getSession的时候,会把获取回来的session的flush mode 设为FlushMode.NEVER。然后把该sessionFactory绑定到 TransactionSynchronizationManager,使request的整个过程都使用同一个session,在请求过后再解除该 sessionFactory的绑定,最后closeSessionIfNecessary根据该 session是否已和transaction绑定来决定是否关闭session。在这个过程中,若HibernateTemplate 发现自当前session有不是readOnly的transaction,就会获取到FlushMode.AUTO Session,使方法拥有写权限。
- public static void closeSessionIfNecessary(Session session, SessionFactory sessionFactory)
- throws CleanupFailureDataAccessException {
- if (session == null ||
- TransactionSynchronizationManager.hasResource(sessionFactory)) {
- return;
- }
- logger.debug("Closing Hibernate session");
- try {
- session.close();
- }
- catch (JDBCException ex) {
- // SQLException underneath
- throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex.getSQLException());
- }
- catch (HibernateException ex) {
- throw new CleanupFailureDataAccessException("Could not close Hibernate session", ex);
- }
- }
也即是,如果有不是readOnly的transaction就可以由Flush.NEVER转为Flush.AUTO,拥有 insert,update,delete操作权限,如果没有transaction,并且没有另外人为地设flush model的话,则doFilter的整个过程都是Flush.NEVER。所以受transaction保护的方法有写权限,没受保护的则没有。
解决:
采用spring的事务声明,使方法受transaction控制
- <bean id="baseTransaction"
- class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
- abstract="true">
- <property name="transactionManager" ref="transactionManager"/>
- <property name="proxyTargetClass" value="true"/>
- <property name="transactionAttributes">
- <props>
- <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
- <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
- <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
- <prop key="save*">PROPAGATION_REQUIRED</prop>
- <prop key="add*">PROPAGATION_REQUIRED</prop>
- <prop key="update*">PROPAGATION_REQUIRED</prop>
- <prop key="remove*">PROPAGATION_REQUIRED</prop>
- </props>
- </property>
- </bean>
- <bean id="userService" parent="baseTransaction">
- <property name="target">
- <bean class="com.phopesoft.security.service.impl.UserServiceImpl"/>
- </property>
- </bean>
对于上例,则以save,add,update,remove开头的方法拥有可写的事务,如果当前有某个方法,如命名为importExcel(),则因没有transaction而没有写权限,这时若方法内有insert,update,delete操作的话,则需要手动设置flush model为Flush.AUTO,如
1 session.setFlushMode(FlushMode.AUTO);
2 session.save(user);
3 session.flush();
或可以
在web.xml中过滤器openSession修改初始参数:
- <!-- 在项目中使用Spring+Hibernate的时候,会开启OpenSessionInViewFilter来阻止延迟加载的错误,但是在我们开启OpenSessionInViewFilter这个过滤器的时候FlushMode就已经被默认设置为了MANUAL,如果FlushMode是MANUAL或NEVEL,在操作过程中 hibernate会将事务设置为readonly,所以在增加、删除或修改操作过程中-->
- lt;init-param>
- <param-name>flushMode</param-name>
- <param-value>AUTO</param-value>
- </init-param>
从上述代码其实可以得到一些对我们的开发有帮助的结论:
1)如果使用了OpenSessionInView模式,那么Spring会帮助你管理Session的开和关,从而你在你的DAO中通过HibernateDaoSupport拿到的getSession()方法,都是绑定到当前线程的线程安全的Session,即拿即用,最后会由Filter统一关闭。
2)由于拿到的Hibernate的Session被设置了session.setFlushMode(FlushMode.NEVER);
所以,除非你直接调用session.flush(),否则Hibernate
session无论何时也不会flush任何的状态变化到数据库。因此,数据库事务的配置非常重要。(我们知道,在调用org.hibernate.Transaction.commit()的时候会触发session.flush())我曾经见过很多人在使用OpenSessionInView模式时,都因为没有正确配置事务,导致了底层会抛出有关FlushMode.NEVER的异常。
总结:
OpenSessionInView这个模式使用比较简单,也成为了大家在Web开发中经常使用的方法,不过它有时候会带来一些意想不到的问题,这也是在开发中需要注意的。
1. 在Struts+Spring+Hibernate环境中,由于配置的问题导致的模式失效这个问题以前论坛曾经讨论过,可以参考一下下面这个帖子:http://www.javaeye.com/topic/15057
2. OpenSessionInView的效率问题
这个问题也有人在论坛提出过,Robbin曾经做过具体的测试,可以具体参考一下下面这个帖子: http://www.javaeye.com/topic/17501
3. 由于使用了OpenSessionInView模式后造成了内存和数据库连接问题
这个问题是我在生产环境中碰到的一个问题。由于使用了OpenSessionInView模式,Session的生命周期变得非常长。虽然解决了Lazy
Load的问题,但是带来的问题就是Hibernate的一级缓存,也就是Session级别的缓存的生命周期会变得非常长,那么如果你在你的Service层做大批量的数据操作时,其实这些数据会在缓存中保留一份,这是非常耗费内存的。还有一个数据库连接的问题,存在的原因在于由于数据库的Connection是和Session绑在一起的,所以,Connection也会得不到及时的释放。因而当系统出现业务非常繁忙,而计算量又非常大的时候,往往数据连接池的连接数会不够。这个问题我至今非常头痛,因为有很多客户对数据连接池的数量会有限制,不会给你无限制的增加下去。
4. 使用了OpenSessionInView模式以后取数据的事务问题
在使用了OpenSessionInView以后,其实事务的生命周期比Session的生命周期来得短,就以为着,其实有相当一部分的查询是不被纳入到事务的范围内的,此时是否会读到脏数据?这个问题我至今不敢确认,有经验的朋友请指教一下。
最后提一下OpenSessionInView模式的一些替代方案,
1
可以使用OpenSessionInViewInterceptor来代替这个Filter,此时可以使用Spring的AOP配置,将这个Interceptor配置到你所需要的层次上去。
在application.xml配置
- <!-- Spring的OpenSessionInView实现 -->
- <filter>
- <filter-name>openSessionInViewFilter</filter-name>
- <filter-class>
- org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
- </filter-class>
- </filter>
- <filter-mapping>
- <filter-name>openSessionInViewFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
2
使用最古老的Hibernate.initialize()方法进行初始化了。
|
检测语言 世界语 中文简体 中文繁体 丹麦语 乌克兰语 乌兹别克语 乌尔都语 亚美尼亚语 伊博语 俄语 保加利亚语 僧伽罗语 克罗地亚语 冰岛语 加利西亚语 加泰罗尼亚语 匈牙利语 南非祖鲁语 卡纳达语 印地语 印尼巽他语 印尼爪哇语 印尼语 古吉拉特语 哈萨克语 土耳其语 塔吉克语 塞尔维亚语 塞索托语 威尔士语 孟加拉语 宿务语 尼泊尔语 巴斯克语 布尔语(南非荷兰语) 希伯来语 希腊语 德语 意大利语 意第绪语 拉丁语 拉脱维亚语 挪威语 捷克语 斯洛伐克语 斯洛文尼亚语 斯瓦希里语 旁遮普语 日语 格鲁吉亚语 毛利语 法语 波兰语 波斯尼亚语 波斯语 泰卢固语 泰米尔语 泰语 海地克里奥尔语 爱尔兰语 爱沙尼亚语 瑞典语 白俄罗斯语 立陶宛语 索马里语 约鲁巴语 缅甸语 罗马尼亚语 老挝语 芬兰语 苗语 英语 荷兰语 菲律宾语 葡萄牙语 蒙古语 西班牙语 豪萨语 越南语 阿塞拜疆语 阿尔巴尼亚语 阿拉伯语 韩语 马其顿语 马尔加什语 马拉地语 马拉雅拉姆语 马来语 马耳他语 高棉语 齐切瓦语 |
世界语 中文简体 中文繁体 丹麦语 乌克兰语 乌兹别克语 乌尔都语 亚美尼亚语 伊博语 俄语 保加利亚语 僧伽罗语 克罗地亚语 冰岛语 加利西亚语 加泰罗尼亚语 匈牙利语 南非祖鲁语 卡纳达语 印地语 印尼巽他语 印尼爪哇语 印尼语 古吉拉特语 哈萨克语 土耳其语 塔吉克语 塞尔维亚语 塞索托语 威尔士语 孟加拉语 宿务语 尼泊尔语 巴斯克语 布尔语(南非荷兰语) 希伯来语 希腊语 德语 意大利语 意第绪语 拉丁语 拉脱维亚语 挪威语 捷克语 斯洛伐克语 斯洛文尼亚语 斯瓦希里语 旁遮普语 日语 格鲁吉亚语 毛利语 法语 波兰语 波斯尼亚语 波斯语 泰卢固语 泰米尔语 泰语 海地克里奥尔语 爱尔兰语 爱沙尼亚语 瑞典语 白俄罗斯语 立陶宛语 索马里语 约鲁巴语 缅甸语 罗马尼亚语 老挝语 芬兰语 苗语 英语 荷兰语 菲律宾语 葡萄牙语 蒙古语 西班牙语 豪萨语 越南语 阿塞拜疆语 阿尔巴尼亚语 阿拉伯语 韩语 马其顿语 马尔加什语 马拉地语 马拉雅拉姆语 马来语 马耳他语 高棉语 齐切瓦语 |
OpenSessionInViewFilter 的配置及替代方案的更多相关文章
- OpenSessionInViewFilter 的配置及替代方案(转)
鸣谢:http://justsee.iteye.com/blog/1174999,http://blog.csdn.net/sunsea08/article/details/4545186 Sprin ...
- Spring实战第七章————SpringMVC配置的替代方案
SpringMVC配置的替代方案 自定义DispatherServlet配置 我们之前在SpittrWebAppInitializer所编写的三个方法仅仅是必须要重载的abstract方法.但还有更多 ...
- OpenSessionInViewFilter的配置
OpenSessionInViewFilter是用来处理懒加载异常的. 懒加载异常的意思的就是:还用不到的东西,就先不加载,等需要的时候再来加载. 所以懒加载对性能有一定的提升,但是,这也会出现一些问 ...
- OpenSessionInViewFilter 的配置及作用
spring为我们解决hibernate的Session的关闭与开启问题. Hibernate 允许对关联对象.属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Sessio ...
- OpenSessionInViewFilter 的配置及作用(原文地址: http://blog.csdn.net/sunsea08/article/details/4545186)
spring为我们解决hibernate的Session的关闭与开启问题. Hibernate 允许对关联对象.属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Sessio ...
- 笔记33 Spring MVC的高级技术——Spring MVC配置的替代方案
一.自定义DispatcherServlet配置 AbstractAnnotationConfigDispatcherServletInitializer所完成 的事情其实比看上去要多.在Spitt ...
- opensessioninviewFilter导致org.hibernate.NonUniqueObjectException
起因: 公司业务需求,增加了一个新的数据源,增加之后,起初一切正常,但是发现后台管理系统所有Ajax请求获取信息没有问题,但是涉及到保存操作就抛出异常. 异常: org.hibernate.NonUn ...
- 浅析 SpringMVC 原理和配置.
一.原理 Spring MVC基于模型-视图-控制器(Model-View-Controller,MVC)模式实现,它能够帮你构建像Spring框架那样灵活和松耦合的Web应用程序,将请求处理的逻辑和 ...
- 使用OpenSessionInViewFilter的注意事项
假设在你的应用中Hibernate是通过spring 来管理它的session.如果在你的应用中没有使用OpenSessionInViewFilter或者OpenSessionInViewInterc ...
随机推荐
- 【转】Redis为什么用跳表而不用平衡树?
Redis里面使用skiplist是为了实现sorted set这种对外的数据结构.sorted set提供的操作非常丰富,可以满足非常多的应用场景.这也意味着,sorted set相对来说实现比较复 ...
- Android Studio 之 控件基础知识
1. TextView 和 EditText 控件常用属性 android:layout_width="match_parent" 宽度与父控件一样宽 android:layou ...
- 【c# 学习笔记】c#委托是什么
法庭上律师为当事人辩护,他真正执行的是当事人的陈词,律师就相当于一个委托对象,而当事人则委托律师对象为自己辩护. c#中的委托概念也就好比律师对象,它是一个类(“委托是类类型”这个事实将在“委托本质” ...
- sql优化(原理,方法,特点,实例)
整理的有点多,做好心理准备...... 1.资源优化理解: 不同设备,io不同.每种设备都有两个指标:延时(响应时间):表示硬件的突发处理能力:带宽(吞吐量):代表硬件持续处理能力. 每种硬件主要的工 ...
- 深入理解AQS
前记 在看JUC中并发相关的源码时经常看到AQS的身影,这到底是个什么鬼?必须要一探究竟. 一. AQS背景了解 JUC包中的锁,包括: Lock接口,ReadWriteLock接口,LockSupp ...
- Go在windows下执行命令行指令
需要在Go写的服务里面调用命令行或者批处理,并根据返回的结果做处理. 在网上搜索了一翻,验证成功,现记录如下: cmd := exec.Command("cmd") // cmd ...
- Java学习:可变参数
可变参数 可变参数:是JDK1.5 之后出现的新特性 使用前提: 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数. 使用格式:定义方法时使用 修饰符 返回值类型 方法名(数 ...
- JS获取当前时间戳及时间戳的转换
获取现在的Unix时间戳(Unix timestamp) Math.round(new Date().getTime()/1000) //getTime()返回数值的单位是毫秒 Unix时间戳(Un ...
- 同级frame之间的通信与跳转
项目最近需求两个同级的frame,通过点击一个frame里面的btn按钮来实现另一个frame的跳转(注意这俩个frame在同一个frameset下面): 原理:通过该frame找到父frameset ...
- 防止用iframe调用网页dom元素
<system.webServer> <httpProtocol> <customHeaders> <add name="X-Frame-Optio ...