spring配置文件中声明式事务和OpenSessionInViewFilter深入研究和汇总

来源:互联网 发布:java酒店需求分析 编辑:程序博客网 时间:2024/05/21 17:20

 spring中配置文件可以配置声明式事务管理以及OpenSessionInViewFilter,

一、1、对于声明式事务的配置作用是在配置有事务的方法上起作用,spring接管此方法内的数据库事务管理其原理是在声明的方法开始前通过sessionFactory获取session并且设置其flushmod为auto(所以即使没有使用事务起码也不会报错),并将此session绑定到当前线程上,用户需要通过getCurrentSession来获取此线程,
方法结束之后事务进行commit或者回滚,然后解除session与线程的绑定并关闭session。
   2、对于OpenSessionInViewFilter如果配置了此项或者配置了相应interceptor。这会延长session的生命周期,保证request期间获取并维护一个session并绑定到当前线程,问题是此session的flushmod默认为never或者manual,由于拿到的Hibernate的Session被设置了session.setFlushMode(FlushMode.NEVER)并且所有事务被设置为只读事务(所以事务最好加上必要readonly即使在没用OpenSessionInView的情况下); 所以,除非你直接调用session.flush(),否则Hibernate session无论何时也不会flush任何的状态变化到数据库。因此,数据库事务的配置非常重要(因为我们知道,在调用org.hibernate.Transaction.commit()的时候会触发session.flush())不然的话在使用OpenSessionInView模式时,都因为没有正确配置事务,导致了底层会抛出有关FlushMode.NEVER的异常。当然解决方法还有其他的

二、尽管OpenSessionInView看起来还不错,其实副作用不少。看上面OpenSessionInViewFilter的doFilterInternal方法代码,这个方法实际上是被父类的doFilter调用的,因此,我们可以大约了解的OpenSessionInViewFilter调用流程: request(请求)->open session并开始transaction->controller->View(Jsp)->结束transaction并 close session. 一切看起来很正确,尤其是在本地开发测试的时候没出现问题,但试想下如果流程中的某一步被阻塞的话,那在这期间connection就一直被占用而不释放。最有可能被阻塞的就是在写Jsp这步,一方面可能是页面内容大,response.write的时间长,另一方面可能是网速慢,服务器与用户间传输时间久。当大量这样的情况出现时,就有连接池连接不足,造成页面假死现象。 Open Session In View是个双刃剑,放在公网上内容多流量大的网站请慎用。另外:这样会产生一点危险性,毕竟把数据库访问的环境放到了表现层。当然这也并不是全部的缺点。

三、对于上面的一些知识理解的知识储备

  1、flushmod

这里面的清理缓存可以理解为hibernate自动执行了一次 session.flush();
选择什么样的flush mode就是采取对hibernate session采取什么样的数据刷新的策略。
Session.setFlushMode()用于设定清理缓存的时间点。

对比一下几种flush mode:

FlushMode.AUTO: 
调用Session的查询方法时,清理缓存,注意:这条规则必须保证显式开启的事务中
调用Session.commit()时,清理缓存
调用Session.flush()时,清理缓存

FlushMode.COMMIT:
调用Session的查询方法时,不清理缓存
调用Session.commit()时,清理缓存
调用Session.flush()时,清理缓存

FlushMode.NEVER(MANUAL):
调用Session的查询方法时,不清理缓存
调用Session.commit()时,不清理缓存
调用Session.flush()时,清理缓存

FlushMode.ALWAYS:测试未发现和auto有什么区别。
 调用Session的查询方法时,清理缓存,注意:这条规则必须保证显式开启的事务中
调用Session.commit()时,清理缓存
调用Session.flush()时,清理缓存

   2、readonly

对已readonly的理解(默认查询语句无需事务支持readonly=true表示开启只读事务)
如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性; 
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持
read-only="true"表示该事务为只读事务,比如上面说的多条查询的这种情况可以使用只读事务,
由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段,例如Oracle对于只读事务,不启动回滚段,不记录回滚log。
(1)在JDBC中,指定只读事务的办法为: connection.setReadOnly(true);
(2)在Hibernate中,指定只读事务的办法为: session.setFlushMode(FlushMode.NEVER); 
此时,Hibernate也会为只读事务提供Session方面的一些优化手段
(3)在Spring的Hibernate封装中,指定只读事务的办法为: bean配置文件中,prop属性增加“read-Only”
或者用注解方式@Transactional(readOnly=true)
Spring中设置只读事务是利用上面两种方式(根据实际情况)
  只读事务的深层理解
 Oracle默认情况下(没有事务) 保证了SQL语句级别的读一致性,即在一条SQL语句执行期间,它只会看到执行前点的数据状态,而不会看到执行期间数据被其他SQL改变的状态。
所以如果执行多条SQL的时候呢?比如你做一个报表查询,在执行完第一条sql的时候,执行第二条查询SQL,而这个之间有数据被改变了,第二条数据查询就有可能不一致
而Oracle的只读查询则保证了事务级别的读一致性,即在该事务范围内执行的多条SQL都只会看到执行前点的数据状态,而不会看到事务期间的任何被其他 SQL改变的状态。
 
没有事务的时候,在执行一条sql语句看到执行前点的数据状态,保证数据一致性
只读事务,在执行多条sql语句看到执行前点的数据状态,保证数据一致性
在显式提交或者回滚后、或执行ddl后,结束只读事务。


需要注意:因为只读事务的原理是读取数据库中数据的前镜像来实现一致性读的,所以,只读事务运行时间不能过长,否则会报0ra-01555。还有一点,只读事务下是不能对数据做修改的:

0 0
原创粉丝点击