hibernate延迟加载导致no session

来源:互联网 发布:策略为王 源码下载 编辑:程序博客网 时间:2024/06/10 09:41
hibernate的get和load的区别相信大家都知道,load是通过代理加载实体,如果只访问id是不会读库将所有属性加载进来的,这个就是一个延迟加载的简单场景。

如果按照标准的spring+hibernate的整合,当使用load加载一个实体是,会报no session的错误

这是因为,延迟加载需要在同一个session中,如果按照标签配置,session在load后就已经关闭,因此页面上显示实体属性时通过代理延迟加载便会报no session的错误了。

解决办法,spring中提供了一个OpenSessionInViewFilter的filter用来在一个http请求中保持hibernate的session


 

配置如下:

<filter>
     <filter-name>hibernateLazyFilter</filter-name>
     <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
 <filter-mapping>
     <filter-name>hibernateLazyFilter</filter-name>
     <url-pattern>/*</url-pattern>
 </filter-mapping>


 

二、此时查询数据库均没问题,但是当update一个实体,会报Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into 错误

这是因为,使用了OpenSessionInViewFilter后,spring会把hibernate的FlushMode改为FlushMode.NEVER,FlushMode.NEVER是不允许更新修改数据库操作的。

解决办法:

1.使用事务控制修改FlushMode

<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>

这里不具体解释了,声明式事务的概念说起来也很多,需要注意的是HibernatTemplate的flushMode要设置为commit。

2.修改OpenSessionInViewFilter的属性,改为AUTO

<filter>
     <filter-name>hibernateLazyFilter</filter-name>
  <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
   <init-param>  
   <param-name>flushMode</param-name>
   <param-value>AUTO</param-value>
  </init-param>  
 </filter>

这里我们使用的是第二种方式。

三、到此查询修改操作不会再报错了,是不是已经配置好了呢?仔细一看,修改是没报错了,但是修改的结果并没有写入数据库

这是因为,spring在flush时会判断一下,请详见spring的源代码:

HibernateTemplate.doExecute{

......

flushIfNecessary(session, existingTransaction);

......

}

 

protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
  if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
   logger.debug("Eagerly flushing Hibernate session");
   session.flush();
  }
 }

意思就是,spring的flushmode为FLUSH_EAGER || (flushmode不是FLUSH_NEVER && 不是事务)

很遗憾使,用了OpenSessionInViewFilter后,existingTransaction=true,也就是说此时spring认为是一个事务

 

解决办法:

1.将HibernateTemplate的flushmode设置为FLUSH_EAGER

2.既然spring不能自动flush,咱们就自己来吧,在update()后flush()

 

到此,延迟加载已经可以正常使用了。

 

 

 

附:在调试是还遇到过这样一个错误:

a different object with the same identifier value was already associated with the session。

这是因为在session的生命周期中如果存在多个实体,hibernate不知道以哪个为准,可以用evict、clear、merg解决

 

@NotFound(action=NotFoundAction.IGNORE)

many-to-one加这个注释延时加载就失效

0 0