ERROR] - could not initialize proxy - the owning Session was closed - [org.hibernate.LazyInitializationException.<init>(LazyInitializationException.java:19)]

用结合spring 使用hibernate都会遇到的问题, 为了避免多余的关联查询, lazy loading的引入, 但是通常持久层会把session关闭了, render view的时候PO 里面的session其实已经关闭了, 再load其他关联属性, 异常就出来了.

单独使用hibernate的事务管理吧. http://www.hibernate.org/42.html

以下讨论话题都是基于Spring + Hibernate配套使用.

1. session在哪里给关闭了?

(1) 假设没有配置OpenSessionInViewFilter, 以及HibernateTransactionManager,AOP任何事务。 我们只是使用HibernateTemplate 在DAO层里做若干次查询操作, 跟踪日志.

2009-03-11 14:28:51,759 [DEBUG] - #{genericFO.signIn} - [com....web.AccessControlActionListenerImpl.preProcessAction(AccessControlActionListenerImpl.java:66)]
2009-03-11 14:28:52,259 [DEBUG] - Opening Hibernate Session - [org.springframework.orm.hibernate3.SessionFactoryUtils.doGetSession(SessionFactoryUtils.java:316)]
2009-03-11 14:28:52,852 [DEBUG] - Eagerly flushing Hibernate session - [org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:389)]
2009-03-11 14:28:52,868 [DEBUG] - Closing Hibernate Session - [org.springframework.orm.hibernate3.SessionFactoryUtils.closeSession(SessionFactoryUtils.java:772)]
2009-03-11 14:28:52,899 [DEBUG] - Opening Hibernate Session - [org.springframework.orm.hibernate3.SessionFactoryUtils.doGetSession(SessionFactoryUtils.java:316)]
2009-03-11 14:28:53,040 [DEBUG] - Eagerly flushing Hibernate session - [org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:389)]
2009-03-11 14:28:53,040 [DEBUG] - Closing Hibernate Session - [org.springframework.orm.hibernate3.SessionFactoryUtils.closeSession(SessionFactoryUtils.java:772)]
2009-03-11 14:28:53,056 [ERROR] - could not initialize proxy - the owning Session was closed - [org.hibernate.LazyInitializationException.<init>(LazyInitializationException.java:19)]

SessionFactoryUtils line 316

logger.debug("Opening Hibernate Session");
  Session session = (entityInterceptor != null ?
    sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());

可以跟一下实现类SessionFactoryImpl, openSession可是每次new 一个Session。

private SessionImpl openSession(
  Connection connection,
     boolean autoClose,
     long timestamp,
     Interceptor sessionLocalInterceptor
 ) {
  return new SessionImpl(
          connection,
          this,
          autoClose,
          timestamp,
          sessionLocalInterceptor == null ? interceptor : sessionLocalInterceptor,
          settings.getDefaultEntityMode(),
          settings.isFlushBeforeCompletionEnabled(),
          settings.isAutoCloseSessionEnabled(),
          settings.getConnectionReleaseMode()
   );
 }

SessionFactory的openSession和getCurrentSession区别比较大的, 一般currentSession有个实现是和threadLocal绑定的, 也就是说每个请求多次查询数据库可能使用的都是同一个session.

心疼了吧, 什么都不配置直接使用HibernateTemplate是比较耗的.  而且currentSession的实现在事务之后会保证session关闭(至少CurrentSessionContext这个接口是这样定义的)

顺便瞄一下HibernateTemplate查询调用的代码吧.

/**
  * Execute the action specified by the given action object within a Session.
  * @param action callback object that specifies the Hibernate action
  * @param exposeNativeSession whether to expose the native Hibernate Session
  * to callback code
  * @return a result object returned by the action, or <code>null</code>
  * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
  */
 public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
  Assert.notNull(action, "Callback object must not be null");

Session session = getSession();//每次都是openSession, 因为ThreadLocal没同一个事务的session
  boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());//为false, 因为threadLocal没有session
  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()) {//默认配置alwaysUseNewSession=false
     SessionFactoryUtils.closeSession(session);
    }
    else {//每次都在这里关闭
     SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
    }
   }
  }
 }

(2) 那我们代码事务或用AOP配置的声明事务看下,主要就是看下HibernateTransactionmanager的使用了其实。假设使用的是TransactionDefinition.PROPAGATION_REQUIRED

public final TransactionStatus getTransaction(TransactionDefinition definition) {

}

void commit(TransactionStatus status) {

..processCommit(DefaultTransactionStatus status);...

}

processCommit(DefaultTransactionStatus status) {...

finally {
   cleanupAfterCompletion(status);
  }

}

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
  status.setCompleted();
  if (status.isNewSynchronization()) {
   TransactionSynchronizationManager.clear();
  }
  if (status.isNewTransaction()) {
   doCleanupAfterCompletion(status.getTransaction());
  }
  if (status.getSuspendedResources() != null) {
   if (status.isDebug()) {
    logger.debug("Resuming suspended transaction");
   }
   resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
  }
 }

其实调用的是Hibernate Session的Transaction, 默认的应该就是JDBCTransaction

protected void doCleanupAfterCompletion(Object transaction) {
  HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;

// Remove the session holder from the thread.
  if (txObject.isNewSessionHolder()) {
   TransactionSynchronizationManager.unbindResource(getSessionFactory());
  }

// Remove the JDBC connection holder from the thread, if exposed.
  if (getDataSource() != null) {
   TransactionSynchronizationManager.unbindResource(getDataSource());
  }

Session session = txObject.getSessionHolder().getSession();
  if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(session)) {
   // We're running with connection release mode "on_close": We're able to reset
   // the isolation level and/or read-only flag of the JDBC Connection here.
   // Else, we need to rely on the connection pool to perform proper cleanup.
   try {
    Connection con = session.connection();
    DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
   }
   catch (HibernateException ex) {
    logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
   }
  }

if (txObject.isNewSessionHolder()) {//如果没用OpenSessionInViewFilter,这个txObject是newSessionHolder=true,否则为false
   if (logger.isDebugEnabled()) {
    logger.debug("Closing Hibernate Session [" + SessionFactoryUtils.toString(session) +
      "] after transaction");
   }//deferredClose空,所以还是关了session
   SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
  }
  else {
   if (logger.isDebugEnabled()) {
    logger.debug("Not closing pre-bound Hibernate Session [" +
      SessionFactoryUtils.toString(session) + "] after transaction");
   }
   if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
    session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode());
   }
   if (hibernateSetTimeoutAvailable) {
    // Running against Hibernate 3.1+...
    // Let's explicitly disconnect the Session to provide efficient Connection handling
    // even with connection release mode "on_close". The Session will automatically
    // obtain a new Connection in case of further database access.
    // Couldn't do this on Hibernate 3.0, where disconnect required a manual reconnect.
    session.disconnect();
   }
  }
  txObject.getSessionHolder().clear();
 }

可以简单这样认为, 由于事务是新的(因为ThreadLocal的SessionHolder是新创的,事先不存在), 事务完成后session还是关了.

其实session关了是好习惯啊,就好像用完了连接就关闭一样。

2. 怎么让session在view也是active?

OpenSessionInViewFilter,

protected void doFilterInternal(
   HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
   throws ServletException, IOException {

SessionFactory sessionFactory = lookupSessionFactory(request);
  boolean participate = false;

if (isSingleSession()) {
   // single session mode
   if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
    // Do not modify the Session: just set the participate flag.
    participate = true;
   }
   else {
    logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
    Session session = getSession(sessionFactory);
    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));

//ThreadLocal保存一个SessionHolder, 之后在事务HibernateTransactionManager中就认为NewSessionHolder=false, 清理的时候就关不了session了; 
   }
  }
  else {
   // deferred close mode
   if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
    // Do not modify deferred close: just set the participate flag.
    participate = true;
   }
   else {
    SessionFactoryUtils.initDeferredClose(sessionFactory);
   }
  }

try {
   filterChain.doFilter(request, response);//下一个filter, 如果没filter就是要采访的jsp/servlet了
  }

finally {
   if (!participate) {
    if (isSingleSession()) {
     // single session mode
     SessionHolder sessionHolder =
       (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
     logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
     closeSession(sessionHolder.getSession(), sessionFactory);
    }
    else {
     // deferred close mode
     SessionFactoryUtils.processDeferredClose(sessionFactory);
    }
   }
  }
 }

很多人说的一个诟病就是session跨多个层, 特别是在页面渲染的时候, 写到客户端时pending时间长, session资源没及时释放, 或者有一定的道理, 很可惜没看到数据证明。

老实说, 这么一个问题的引入也证明一点, 没什么框架是完美的真那么傻瓜的, 适合使用, 简单就好。

Hibernate也只是我们项目中持久层的一个应用框架.

再谈Hibernate the owing session was closed相关推荐

  1. Hibernate中发生Session is closed 的另一种可能!

    Hibernate中发生"Session is closed" 的另一种可能! 文章分类:Java编程 关键字: hibernate session is closed Hiber ...

  2. Hibernate中发生Session is closed 的另一种可能

    Hibernate中发生"Session is closed" 的另一种可能:没有commit的Transaction. 关键字:"Session is closed&q ...

  3. hibernate的异常 Session was already closed

    今天写hibernate时候遇到一些异常 代码: Session session = sessionFactory.getCurrentSession(); session.beginTransact ...

  4. 浅谈Hibernate中的几个查询

    浅谈Hibernate中的几个查询 一.load和get方法的区别 1.load在查询时,先从一级缓存中寻找与数据索引对应的实体对象,然后构建并返回一个代理对象,当我们真正使用这个代理对象的时候,这时 ...

  5. Java程序员从笨鸟到菜鸟之(五十二)细谈Hibernate(三)Hibernate常用API详解及源码分析--csdn 曹胜欢...

    新接触一个框架的目的就是想利用这个框架来为我们做一些工作,或者是让他来简化我们的工作,利用这个框架无非就是要利用这个框架所给我们提供的API去操作我们的数据,所以利用一个框架的好坏很大一部分取决于你对 ...

  6. 我的WCF之旅 (11): 再谈WCF的双向通讯-基于Http的双向通讯 V.S. 基于TCP的双向通讯...

    在一个基于面向服务的分布式环境中,借助一个标准的.平台无关的Communication Infrastructure,各个Service通过SOAP Message实现相互之间的交互.这个交互的过程实 ...

  7. tensorflow和python先学哪个-前辈说先学会了这些Python知识点,再谈学习人工智能!...

    原标题:前辈说先学会了这些Python知识点,再谈学习人工智能! 首先我们看一看Python的优势: 开源,跨平台. 社区.不要小看这一点.社区意味着有很多教程.书籍,出了问题很容易google到,乃 ...

  8. the vm session was closed before any attempt to power it on

    今天启动VBOX出现这个问题"the vm session was closed before any attempt to power it on" 1.找到一个解决方法:到存储 ...

  9. org.hibernate.HibernateException: No Session found for current thread

    spring.springmvc和hibernate整合 在sessionFactory.getCurrentSession()时,出现以下异常 No Session found for curren ...

  10. Java程序员从笨鸟到菜鸟之(五十一)细谈Hibernate(二)开发第一个hibernate基本详解...

    在上篇博客中,我们介绍了<hibernate基本概念和体系结构>,也对hibernate框架有了一个初步的了解,本文我将向大家简单介绍Hibernate的核心API调用库,并讲解一下它的基 ...

最新文章

  1. 【LeetCode】106. Construct Binary Tree from Inorder and Postorder Traversal
  2. 9号团队-团队任务4:每日立会(2018-11-26,2018-11-27)
  3. Winform中在ZedGraph中最多可以添加多少条曲线
  4. vue代码生成器可视化界面_手把手教你基于SqlSugar4编写一个可视化代码生成器(生成实体,以SqlServer为例,文末附源码)...
  5. 【MySQL】MySQL删除数据库的时候卡死
  6. html网页商品销量滞后怎么做,iview 刷新滞后于html问题
  7. 如何禁止用户安装应用程序,记下来,方便以后用
  8. .NET(C#):觉察XML反序列化中的未知节点
  9. ISO27001认证的主要步骤
  10. MonkeyTest小结
  11. MATLAB图像处理学习日记之__图像的K-means均值法与局部阈值和迭代式阈值分割法算法——整理资源汇总
  12. 如何查询电脑最大可扩展内存
  13. VB功能模块:最全的VB操作网页功能模块
  14. word去掉首页页眉页脚
  15. 网易游戏策划笔试2019春招题解
  16. 快速解决win7系统Aero主题无法使用
  17. zoj 3939 The Lucky Week(打表找循环节)
  18. 适用于 Flutter 的 Google 移动广告 SDK 正式版现已发布
  19. iphone-使用TextField及关闭键盘(useing TextField for inputs、using the keyboard)
  20. 苹果笔记本电脑运行win系统时温度过高解决办法

热门文章

  1. 推荐个电脑桌面便签软件工具:好用便签,简单、免费、无广告、电脑手机同步、支持团队共享,用来做桌面便签笔记、备忘录、待办日程任务清单很多。
  2. 6-1 哈夫曼树及哈夫曼编码
  3. 待支付取件费用是什么意思_待支付(待支付_订单待支付是什么意思_待支付取件费用)...
  4. java8对类集合使用 Comparator.comparing 进行排序
  5. java 异常 ppt_Java程序设计基础与实践 第6章 异常处理.ppt
  6. java xmx 最大值_java – JVM超过用-Xmx定义的最大内存
  7. 项目管理学习 ---- 理解项目管理思维框架
  8. chrome添加网页单词翻译插件
  9. 使用laser_filters屏蔽车架
  10. 【网络】把路由器用作交换机的方案