OpenSessionInViewFilter的使用

一、为什么需要使用OpenSessionInViewFilter
作用:

Spring为我们解决Hibernate的Session的关闭与开启问题。 
Hibernate 允许对关联对象、属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。如果 Service 层返回一个启用了延迟加载功能的领域对象给 Web 层,当 Web 层访问到那些需要延迟加载的数据时,由于加载领域对象的 Hibernate Session 已经关闭,这些导致延迟加载数据的访问异常

(eg: org.hibernate.LazyInitializationException:(LazyInitializationException.java:42) 
- failed to lazily initialize a collection of role: cn.easyjava.bean.product.ProductType.childtypes, no session or session was closed 
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cn.easyjava.bean.product.ProductType.childtypes, no session or session was closed)

用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open Session in View"的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象。

1.我们先看个例子,接口省略

DeptImpl.java

public class DeptImpl extends BaseDao<Dept> implements IDeptDao{
	public Dept findDeptById(Integer id) {
		// TODO Auto-generated method stub	
		System.out.println(this.getSf().getCurrentSession().hashCode());//输出hashCode,如果hashCode相同,则这两个对象就相同
		Dept d=super.get_object(id);
		return d;
	}
	
	public void save(Dept d) {
		// TODO Auto-generated method stub
		System.out.println(this.getSf().getCurrentSession().hashCode());
		super.create(d);
	}
}
DeptService.java

public class DeptService implements IDeptService{
	private IDeptDao ideptdao;
	
	public IDeptDao getIdeptdao() {
		return ideptdao;
	}

	public void setIdeptdao(IDeptDao ideptdao) {
		this.ideptdao = ideptdao;
	}

	public Dept getDept(Integer id) {
		// TODO Auto-generated method stub
		return ideptdao.findDeptById(id);
	}
	
	public void saveDept(Dept d){
		// TODO Auto-generated method stub
		ideptdao.save(d);	
	}
	public void handle_findDept_saveDept(Integer id){
		Dept da=getDept(id);
		saveDept(da);
	}
}

dataSource.xml(配置事务部分)

	<bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="MySf" />
    </bean>
    
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    	<tx:attributes>
    		<tx:method name="*" propagation="REQUIRED"/>
    	</tx:attributes>
    </tx:advice>
    
    <aop:config>
    	<aop:pointcut id="target" expression="execution(* org.hzy.services.impl.DeptService.*(..))"/>
    	<aop:advisor advice-ref="txAdvice" pointcut-ref="target"/>
    </aop:config>
loginaction.java

	public String login(){
		System.out.println("login!!!!!!!!!!!!!!!!!!!!!!!");
//		is.handle_findDept_saveDept(10);
		Dept d=is.getDept(10);
		is.saveDept(d);
		return null;
	}

在DeptImpl中,我们在方法里面输出Session的哈希码,如果是同一个则是同一个Session,反之,则不是。

在DeptService中,我们有三个方法,findDept(),saveDept(),handle_findDept_saveDept(),第三个方法是前两个方法的组合,在dataSource.xml中,我们配置的Aop事务,切入点是

DeptService.java中的所有方法,将会在这个类中的任何一个方法中插入事务,以前我们是

DeptImpl中的findDeptById()----------->DeptService中的getDept(),开session,Aop事务拦截,在Service中插入事务

DeptImpl中的save()----------->DeptService中的saveDept(),和上面方法一样

DeptService的handle_findDept_saveDept()方法时上面两个方法的组合,当调用handle_findDpet_saveDept()方法时会开个Session和插入事务,而后进入方法里面是调用第一个findDeptById()方法,则不会去开session了,HibernateTransactionManager很好的管理了这一点,当走findDeptById()方法时,会先判断有没有Session存在,如果有HibernateTransactionManager将会返回刚才那个给你,同理后面的saveDept方法也是。

:在action里面调用handle_findDpet_saveDept()的结果是:


可以看出Session是同一个,

但是很多情况下我们会分开调用,好少组合起来调用,所以我们在action中先调用getDept()再调用saveDept()时会发现结果:


从结果可以看出是开启了两个不同的Session,这就是问题所在。

二、解决上面的开启了多个Session的问题

Spring为我们提供的OpenSessionInViewFilter过滤器为我们很好的解决了这个问题,OpenSessionInViewFilter的主要功能是用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open Session in View"的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象。就是一个请求对应一个Session,当请求结束,Session也就关闭。

配置OpenSessionInViewFilter过滤器:

   	<filter>
  		<filter-name>open</filter-name>
  		<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
  		<init-param>
  			<param-name>singleSession</param-name>
  			<param-value>true</param-value> 
  		</init-param>
  		<init-param>
  			<param-name>sessionFactoryBeanName</param-name>
  			<param-value>MySf</param-value> 
  		</init-param>
  		<init-param>
  			<param-name>flushMode</param-name>
  			<param-value>AUTO</param-value> 
  		</init-param>
  	</filter>
  
  <filter-mapping>
  	<filter-name>open</filter-name>
  	<url-pattern>*.action</url-pattern>
  </filter-mapping>

注:这个过滤器必须放在struts2过滤器的前面,web.xml中的配置要注意先后顺序,OpenSessionInViewFilter要在struts2的filter前面,否则系统会报错。


配置中的sessionFactoryBeanName是配置SessionFactory,默认是sessionFactory。

配置中的singleSession 设置为true是当页面跳转的时候是不是单例的,同一个Session。还可以配置flushMode,控制Session模式。

spring3.x中的opensessioninviewfilter已经将默认的FlushMode设置为MANUAL了;
如果FlushMode是MANUAL或NEVEL,在操作过程中hibernate会将事务设置为readonly,所以在增加、删除或修改操作过程中会出现如下错误
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;
配置事务,spring会读取事务中的各种配置来覆盖hibernate的session中的FlushMode,所以我们在配置文件中配置flush为AUTO

Open Session In View在request把session绑定到当前thread期间一直保持hibernate session在open状态,直到请求结束。

在上面配置中我们要注意以下几点:

1、这个filter一定要配置在struts的过滤器的前面,因为过滤器是“先进后出”原则,如果你配置在struts的后面的话,你的openSessionInView过滤器都执行完了,怎么在去在管理action的转向页面啊。

2Opensessioninview也需要sessionfactorybean的注入,他默认的去找beanidsessionfactorybean,如果sessionfactorybeanid不是这个名字的话,要记得给这个过滤器配置一个参数,参数名为sessionfactoryBeanName,把他的value设置为你的sessionfactorybeanid值。

3、在用opensessioninview的时候一定要注意,如果你不配置transaction的话,在用opensessioninview时,他默认的把事务配置为only-read只读,这样的话,如果你进行增删改的时候,他就会报一个在只读事务中不能进行增删改的操作。如果把opensessioninview去掉,他默认的事务的开始边界就锁定在dao层操作上,daohibernatetempt提供了事务的开始和提交,所以一般设为AUTO

4、OpenSessionInView模式的一些替代方案,可以使用OpenSessionInViewInterceptor来代替这个Filter,此时可以使用Spring的AOP配置,将这个Interceptor配置到你所需要的层次上去。另外就是只能使用最古老的Hibernate.initialize()方法进行初始化了。

OpenSessionInView的副作用(内存和数据连接问题)

         了解了上面几个问题之后,那么也就可以大概知道OpenSessionInView的副作用 – 资源占用严重,配置不当,影响系统性能。使用OpenSessionInView后,在request发出和response返回的流程中,如果有任何一步被阻塞,那在这期间connection就会被一直占用而不释放。比如页面较大,显示需要时间 或者 网速太慢,服务器与用户间传输的时间太长,这都会导致资源占用,最直接的表现就是连接池连接不够用,而导致最终服务器无法提供服务。





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值