为什么Spring的HibernateTemplate一般情况下不支持数据的惰性加载的源码分析
2008-03-10 14:18
746 查看
1.
HibernateTemplate template = (MyHibernateTemplate) context.getBean("hibernateTemplate");
Emp emp = (Emp) template.load(Emp.class, new Long(7369));
long no = emp.getEmpno();
log.info("empno = " + no);
String name = emp.getEname();
log.info("Empname = " + name);
上面的代码在一般情况执行到String name=emp.getEname();的时候就会抛出org.hibernate.LazyInitializationException的异常,这个是容易理解的,因为Emp emp = (Emp) template.load(Emp.class, new Long(7369));默认是延迟数据的加载的,HibernateTemplate在执行完Emp emp = (Emp) template.load(Emp.class, new Long(7369));这句话后打开的session就关闭了。因而你在执行 String name = emp.getEname();这条语句时,这个时候才会向数据查询,但是这个时候session已经关闭,因而会出现上面的异常。为什么session会关闭呢?看一下Hibernate的源码就知道了,其核心代码是下面这段:
public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Session session = getSession();
boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());
if (existingTransaction) {
logger.debug("Found thread-bound Session for HibernateTemplate");
}
FlushMode previousFlushMode = null;
try {
previousFlushMode = applyFlushMode(session, existingTransaction);
enableFilters(session);
Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));
Object result = action.doInHibernate(sessionToExpose);
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// Callback code threw application exception...
throw ex;
}
finally {
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
disableFilters(session);
if (previousFlushMode != null) {
session.setFlushMode(previousFlushMode);
}
}
else {
// Never use deferred close for an explicitly new Session.
if (isAlwaysUseNewSession()) {
SessionFactoryUtils.closeSession(session);
}
else {
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
}
}
}
}
因为HibernateTemplate是通过方法的回调执行你所操作的方法的。对应的话你调用的方法对应的就是
Object result = action.doInHibernate(sessionToExpose);这一句当这些代码执行完后,最后看finally语句,应该可以看到,如果existingTransaction这个变量为false,也就是说,你所进行的操作没有进行事务管理的,最总就关闭session了。大家可以代码跟踪一下就知道existingTransaction一般情况下的值就是为false的。那么我们就可以考虑让existingTransaction为true,也就是让自己的操作进入事务管理,这个时候session就不会关闭了,除非你自己关闭。比如我们进行一个junit测试如下:
package jimmee.cn.edu.zju;
import java.util.Iterator;
import java.util.Set;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate3.MyHibernateTemplate;
import org.springframework.orm.hibernate3.MySessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class TestSession extends TestCase
...{
Logger log = Logger.getLogger(TestSession.class);
SessionFactory sessionFactory = null;
Session session = null;
ApplicationContext bf = null;
public void testSession()
...{
MyHibernateTemplate template = (MyHibernateTemplate) bf
.getBean("hibernateTemplate");
Emp emp = (Emp) template.load(Emp.class, new Long(7369));
long no = emp.getEmpno();
log.info("empno = " + no);
String name = emp.getEname();
log.info("Empname = " + name);
Dept dept = (Dept) template.load(Dept.class, new Long(30));
log.info("dept no =" + dept.getDeptno());
log.info("dept name=" + dept.getDname());
Set set = dept.getEmps();
for (Iterator iterator = set.iterator(); iterator.hasNext();)
...{
Emp emp3 = (Emp) iterator.next();
log.info("empname = " + emp3.getEname());
}
}
@Override
protected void setUp() throws Exception
...{
super.setUp();
bf = new ClassPathXmlApplicationContext("applicationContext.xml");
sessionFactory = (SessionFactory) bf.getBean("sessionFactory");
session = MySessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory,
new SessionHolder(session));
}
@Override
protected void tearDown() throws Exception
...{
super.tearDown();
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager
.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
MySessionFactoryUtils.closeSession(s);
sessionFactory.close();
}
}
为了明显的看到一些信息,我继承了HibernateTemplate了,在MyHibernateTemplate里重写了 public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException方法,自己添加了一些打印信息,最终输出结果如下:
2008-03-10 14:08:46,921 INFO [jimmee.cn.edu.zju.TestSession] - empno = 7369
Hibernate: select emp0_.EMPNO as EMPNO0_0_, emp0_.DEPTNO as DEPTNO0_0_, emp0_.ENAME as ENAME0_0_, emp0_.JOB as JOB0_0_, emp0_.MGR as MGR0_0_, emp0_.HIREDATE as HIREDATE0_0_, emp0_.SAL as SAL0_0_, emp0_.COMM as COMM0_0_ from SCOTT.EMP emp0_ where emp0_.EMPNO=?
2008-03-10 14:08:47,015 INFO [jimmee.cn.edu.zju.TestSession] - Empname = SMITH
2008-03-10 14:08:47,015 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] bound to thread [main]
2008-03-10 14:08:47,015 INFO [org.springframework.orm.hibernate3.MyHibernateTemplate] - session id=26578114
2008-03-10 14:08:47,015 INFO [org.springframework.orm.hibernate3.MyHibernateTemplate] - factory id=org.hibernate.impl.SessionFactoryImpl@18bbf55
2008-03-10 14:08:47,015 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] bound to thread [main]
2008-03-10 14:08:47,015 INFO [org.springframework.orm.hibernate3.MyHibernateTemplate] - existingTransaction = true
2008-03-10 14:08:47,015 DEBUG [org.springframework.orm.hibernate3.MyHibernateTemplate] - Found thread-bound Session for HibernateTemplate
2008-03-10 14:08:47,015 DEBUG [org.springframework.orm.hibernate3.MyHibernateTemplate] - Not closing pre-bound Hibernate Session after HibernateTemplate
2008-03-10 14:08:47,015 INFO [jimmee.cn.edu.zju.TestSession] - dept no =30
Hibernate: select dept0_.DEPTNO as DEPTNO1_0_, dept0_.DNAME as DNAME1_0_, dept0_.LOC as LOC1_0_ from SCOTT.DEPT dept0_ where dept0_.DEPTNO=?
2008-03-10 14:08:47,031 INFO [jimmee.cn.edu.zju.TestSession] - dept name=SALES
Hibernate: select emps0_.DEPTNO as DEPTNO1_, emps0_.EMPNO as EMPNO1_, emps0_.EMPNO as EMPNO0_0_, emps0_.DEPTNO as DEPTNO0_0_, emps0_.ENAME as ENAME0_0_, emps0_.JOB as JOB0_0_, emps0_.MGR as MGR0_0_, emps0_.HIREDATE as HIREDATE0_0_, emps0_.SAL as SAL0_0_, emps0_.COMM as COMM0_0_ from SCOTT.EMP emps0_ where emps0_.DEPTNO=?
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = BLAKE
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = WARD
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = TURNER
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = ALLEN
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = MARTIN
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = JAMES
2008-03-10 14:08:47,046 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] bound to thread [main]
2008-03-10 14:08:47,062 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Removed value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] from thread [main]
2008-03-10 14:08:47,062 INFO [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closing Hibernate Session
2008-03-10 14:08:47,062 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closing Hibernate Session
2008-03-10 14:08:47,062 INFO [org.springframework.orm.hibernate3.SessionFactoryUtils] - session id=26578114
2008-03-10 14:08:47,078 INFO [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closed Hibernate Session
2008-03-10 14:08:47,078 INFO [org.hibernate.impl.SessionFactoryImpl] - closing
HibernateTemplate template = (MyHibernateTemplate) context.getBean("hibernateTemplate");
Emp emp = (Emp) template.load(Emp.class, new Long(7369));
long no = emp.getEmpno();
log.info("empno = " + no);
String name = emp.getEname();
log.info("Empname = " + name);
上面的代码在一般情况执行到String name=emp.getEname();的时候就会抛出org.hibernate.LazyInitializationException的异常,这个是容易理解的,因为Emp emp = (Emp) template.load(Emp.class, new Long(7369));默认是延迟数据的加载的,HibernateTemplate在执行完Emp emp = (Emp) template.load(Emp.class, new Long(7369));这句话后打开的session就关闭了。因而你在执行 String name = emp.getEname();这条语句时,这个时候才会向数据查询,但是这个时候session已经关闭,因而会出现上面的异常。为什么session会关闭呢?看一下Hibernate的源码就知道了,其核心代码是下面这段:
public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Session session = getSession();
boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());
if (existingTransaction) {
logger.debug("Found thread-bound Session for HibernateTemplate");
}
FlushMode previousFlushMode = null;
try {
previousFlushMode = applyFlushMode(session, existingTransaction);
enableFilters(session);
Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));
Object result = action.doInHibernate(sessionToExpose);
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// Callback code threw application exception...
throw ex;
}
finally {
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
disableFilters(session);
if (previousFlushMode != null) {
session.setFlushMode(previousFlushMode);
}
}
else {
// Never use deferred close for an explicitly new Session.
if (isAlwaysUseNewSession()) {
SessionFactoryUtils.closeSession(session);
}
else {
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
}
}
}
}
因为HibernateTemplate是通过方法的回调执行你所操作的方法的。对应的话你调用的方法对应的就是
Object result = action.doInHibernate(sessionToExpose);这一句当这些代码执行完后,最后看finally语句,应该可以看到,如果existingTransaction这个变量为false,也就是说,你所进行的操作没有进行事务管理的,最总就关闭session了。大家可以代码跟踪一下就知道existingTransaction一般情况下的值就是为false的。那么我们就可以考虑让existingTransaction为true,也就是让自己的操作进入事务管理,这个时候session就不会关闭了,除非你自己关闭。比如我们进行一个junit测试如下:
package jimmee.cn.edu.zju;
import java.util.Iterator;
import java.util.Set;
import junit.framework.TestCase;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate3.MyHibernateTemplate;
import org.springframework.orm.hibernate3.MySessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
public class TestSession extends TestCase
...{
Logger log = Logger.getLogger(TestSession.class);
SessionFactory sessionFactory = null;
Session session = null;
ApplicationContext bf = null;
public void testSession()
...{
MyHibernateTemplate template = (MyHibernateTemplate) bf
.getBean("hibernateTemplate");
Emp emp = (Emp) template.load(Emp.class, new Long(7369));
long no = emp.getEmpno();
log.info("empno = " + no);
String name = emp.getEname();
log.info("Empname = " + name);
Dept dept = (Dept) template.load(Dept.class, new Long(30));
log.info("dept no =" + dept.getDeptno());
log.info("dept name=" + dept.getDname());
Set set = dept.getEmps();
for (Iterator iterator = set.iterator(); iterator.hasNext();)
...{
Emp emp3 = (Emp) iterator.next();
log.info("empname = " + emp3.getEname());
}
}
@Override
protected void setUp() throws Exception
...{
super.setUp();
bf = new ClassPathXmlApplicationContext("applicationContext.xml");
sessionFactory = (SessionFactory) bf.getBean("sessionFactory");
session = MySessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory,
new SessionHolder(session));
}
@Override
protected void tearDown() throws Exception
...{
super.tearDown();
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager
.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
MySessionFactoryUtils.closeSession(s);
sessionFactory.close();
}
}
为了明显的看到一些信息,我继承了HibernateTemplate了,在MyHibernateTemplate里重写了 public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException方法,自己添加了一些打印信息,最终输出结果如下:
2008-03-10 14:08:46,921 INFO [jimmee.cn.edu.zju.TestSession] - empno = 7369
Hibernate: select emp0_.EMPNO as EMPNO0_0_, emp0_.DEPTNO as DEPTNO0_0_, emp0_.ENAME as ENAME0_0_, emp0_.JOB as JOB0_0_, emp0_.MGR as MGR0_0_, emp0_.HIREDATE as HIREDATE0_0_, emp0_.SAL as SAL0_0_, emp0_.COMM as COMM0_0_ from SCOTT.EMP emp0_ where emp0_.EMPNO=?
2008-03-10 14:08:47,015 INFO [jimmee.cn.edu.zju.TestSession] - Empname = SMITH
2008-03-10 14:08:47,015 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] bound to thread [main]
2008-03-10 14:08:47,015 INFO [org.springframework.orm.hibernate3.MyHibernateTemplate] - session id=26578114
2008-03-10 14:08:47,015 INFO [org.springframework.orm.hibernate3.MyHibernateTemplate] - factory id=org.hibernate.impl.SessionFactoryImpl@18bbf55
2008-03-10 14:08:47,015 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] bound to thread [main]
2008-03-10 14:08:47,015 INFO [org.springframework.orm.hibernate3.MyHibernateTemplate] - existingTransaction = true
2008-03-10 14:08:47,015 DEBUG [org.springframework.orm.hibernate3.MyHibernateTemplate] - Found thread-bound Session for HibernateTemplate
2008-03-10 14:08:47,015 DEBUG [org.springframework.orm.hibernate3.MyHibernateTemplate] - Not closing pre-bound Hibernate Session after HibernateTemplate
2008-03-10 14:08:47,015 INFO [jimmee.cn.edu.zju.TestSession] - dept no =30
Hibernate: select dept0_.DEPTNO as DEPTNO1_0_, dept0_.DNAME as DNAME1_0_, dept0_.LOC as LOC1_0_ from SCOTT.DEPT dept0_ where dept0_.DEPTNO=?
2008-03-10 14:08:47,031 INFO [jimmee.cn.edu.zju.TestSession] - dept name=SALES
Hibernate: select emps0_.DEPTNO as DEPTNO1_, emps0_.EMPNO as EMPNO1_, emps0_.EMPNO as EMPNO0_0_, emps0_.DEPTNO as DEPTNO0_0_, emps0_.ENAME as ENAME0_0_, emps0_.JOB as JOB0_0_, emps0_.MGR as MGR0_0_, emps0_.HIREDATE as HIREDATE0_0_, emps0_.SAL as SAL0_0_, emps0_.COMM as COMM0_0_ from SCOTT.EMP emps0_ where emps0_.DEPTNO=?
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = BLAKE
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = WARD
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = TURNER
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = ALLEN
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = MARTIN
2008-03-10 14:08:47,046 INFO [jimmee.cn.edu.zju.TestSession] - empname = JAMES
2008-03-10 14:08:47,046 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] bound to thread [main]
2008-03-10 14:08:47,062 DEBUG [org.springframework.transaction.support.TransactionSynchronizationManager] - Removed value [org.springframework.orm.hibernate3.SessionHolder@2bccb2] for key [org.hibernate.impl.SessionFactoryImpl@18bbf55] from thread [main]
2008-03-10 14:08:47,062 INFO [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closing Hibernate Session
2008-03-10 14:08:47,062 DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closing Hibernate Session
2008-03-10 14:08:47,062 INFO [org.springframework.orm.hibernate3.SessionFactoryUtils] - session id=26578114
2008-03-10 14:08:47,078 INFO [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closed Hibernate Session
2008-03-10 14:08:47,078 INFO [org.hibernate.impl.SessionFactoryImpl] - closing
相关文章推荐
- Launcher3源码分析 — 加载Workspace的数据
- Dubbo源码分析(三):自定义Schema--基于Spring可扩展Schema提供自定义配置支持(spring配置文件中 配置标签支持)
- spring启动component-scan类扫描加载过程---源码分析
- spring boot实战(第十篇)Spring boot Bean加载源码分析
- Launcher3源码分析 — 所有应用页面的数据加载和绑定
- spring启动component-scan类扫描加载过程---源码分析
- Spring源码分析:Bean加载流程概览及配置文件读取
- Spring 源码分析:Bean 加载流程概览及配置文件读取
- 【Spring源码分析】Bean加载流程概览
- Spring源码分析:非懒加载的单例Bean初始化过程(上)
- 【Spring源码分析系列】bean的加载
- spring源码分析-配置文件加载过程
- ViewPager部分源码分析一:加载数据
- Spring 支持 Restful风格 源码分析
- 【Spring源码分析系列】启动component-scan类扫描加载过程
- 城市公交数据下载(续)分析过程及源码(支持全国440个城市)
- Android 7.0 Gallery图库源码分析3 - 数据加载及显示流程
- Launcher3源码分析 — 数据加载过程
- Spring源码分析-BeanDefinition加载、解析和注册
- Spring源码分析:非懒加载的单例Bean初始化过程(下)