1. 环境:
- spring4
- hibernate4
- mysql 5.6
2. 原始问题现象:
假设有两个表student和teacher,teachet是student的ManyToOne外键字段,设置如下属性:
@OneToOne(fetch = FetchType.LAZY, mappedBy = "student")
当没有在spring配置的transaction中直接如此:student.getTeacher()获取teacher时,会发生spring4 org.hibernate.LazyInitializationException: could not initialize proxy - no Session异常;
3. 原因分析:
spirng整合hibernate时,由于数据库的session完全交由spirng负责,而spring对于session的管理是以transaction为原子操作的,即在一个transaction事务操作完毕后session被spring关闭,所以在transaction的方法以外直接读取lazy加载字段时,session必然已经关闭了,进而抛出以上异常;
4. 解决方法:
- 使用OpenSessionInViewFilter过滤器,在web.xml中配置如下:
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate4.support.OpenSessionInViewFilter<!--这边是hibernate4,其他版本对应好版本号即可-->
</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>yourdbrSessionFactory</param-value><!--这个sessionFactory是Spring配置文件中配置的bean-->
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
该过滤器可以保证在一次http会话过程中,hibernate的session不会关闭,一直到会话结束;
该方法的弊端:
(1). 和http会话过于耦合,容易受http请求影响,例如如果请求数据很大或者时间过程,该hibernate的connection一直不能释放,如果服务器请求频繁的话,JDBC的线程池有可能耗光;
(2). 只能用于http请求;
在hibernate配置的transaction的相关method中,使用静态方法Hibernte.initialize(Object);加载需要使用的LAZY关联字段,参数Object为关联字段,例如student拥有关联外键teacher,并且teacher的fetchtype配置为lazy,那么在service中获取student的方法中最后可以执行Hibernte.initialize(student.getTeacher())操作,则teacher将被加载;
在entity中配置相关字段为:
@OneToOne(fetch = FetchType.EAGER, mappedBy = "student")
该方法将保证每次读取student时都会自动加载teacher字段,但是缺点就是如果teacher字段是集合时会降低读取student的速度;
以上方法各有优越点,在实际开发中需要和具体需求结合考虑,综合使用