【问题标题】:Hibernate: "Session is closed" although never closed (manually)休眠:“会话已关闭”虽然从未关闭(手动)
【发布时间】:2011-07-12 12:28:49
【问题描述】:

我在这里遇到了一个非常奇怪的问题,我完全不明白为什么会这样。

问题如下:

我有一个名为“SmampiAccount”的类,它包含一个电子邮件帐户列表。映射文件如下所示(缩短):

<hibernate-mapping>
    <class name="com.smampi.web.model.account.SmampiAccount" table="SMAMPIACCOUNT">
            <id name="id" type="long" access="field">
                    <column name="SMAMPI_ACCOUNT_ID" />
                    <generator class="native" />
            </id>

            <bag name="mailAccounts" table="MAILACCOUNTS" lazy="false" inverse="true">
                    <key column="SMAMPI_ACCOUNT_ID"></key>
                    <one-to-many class="com.smampi.web.model.mail.account.MailAccount"/>
            </bag>
    </class>
</hibernate-mapping>

我通过这个方法得到这个类的实例:

public SmampiAccount loadSmampiAccount(long id) throws FailedDatabaseOperationException {

    SmampiAccount smampiAccount = null;
    Session session = null;
    Transaction transaction = null;
    try {
        session = getSession();
        transaction = session.beginTransaction();
        smampiAccount = (SmampiAccount) session.load(com.smampi.web.model.account.SmampiAccount.class, id);
        List<MailAccount> mailAccounts = smampiAccount.getMailAccounts();
        doSomething(mailAccounts);
        transaction.commit();
    } catch (Exception e) {
        rollback(transaction);
        closeSession();
        throw new FailedDatabaseOperationException(e);
    } finally {
        closeSession();
    }

    return smampiAccount;
}

private Session getSession() {
    if (_session == null) {
        _session = getSessionFactory().openSession();
    }
    if (_session.isOpen() == false) {
        _session = getSessionFactory().openSession();
    }
    return _session;
}

这可以正常工作。

现在,我想向映射文件添加一个新属性,以保存对默认电子邮件帐户的引用:

<many-to-one name="defaultMailAccount" column="DEFAULT_MAIL_ACCOUNT_ID" />

现在,我在这一行的方法 public SmampiAccount loadSmampiAccount(long id) 中遇到异常:

List<MailAccount> mailAccounts = smampiAccount.getMailAccounts();

堆栈跟踪:

org.hibernate.SessionException: Session is closed!
    at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:72)
    at org.hibernate.impl.SessionImpl.getPersistenceContext(SessionImpl.java:1954)
    at org.hibernate.event.def.DefaultPostLoadEventListener.onPostLoad(DefaultPostLoadEventListener.java:49)
    at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:250)
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:982)
    at org.hibernate.loader.Loader.doQuery(Loader.java:857)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2037)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:86)
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:76)
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3293)
    at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:496)
    at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:477)
    at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
    at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:147)
    at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
    at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:1026)
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at com.smampi.web.model.account.SmampiAccount_$$_javassist_19.getMailAccounts(SmampiAccount_$$_javassist_19.java)

这怎么可能? 会话没有手动关闭,.commit() 还没有被调用(这通常会关闭会话)。 也不可能有另一种方法在这里干扰,因为我为每个方法调用创建了一个新的休眠会话,该会话专用于这个方法。


编辑

我在会话打开状态上添加了一些调试信息:

session = getSession();

System.err.println(session.isOpen());
transaction = session.beginTransaction(); // 1 (true)

System.err.println(session.isOpen()); // 2 (true)
smampiAccount = (SmampiAccount) session.load(com.smampi.web.model.account.SmampiAccount.class, id);

System.err.println(session.isOpen()); // 3 (true)
List<MailAccount> mailAccounts = smampiAccount.getMailAccounts(); // Throws exception that session is closed
doSomething(mailAccounts);

System.err.println(session.isOpen()); // 4 (not called)
transaction.commit();

这给了我:

true
true
true
org.hibernate.SessionException: Session is closed!

【问题讨论】:

  • 看起来很奇怪。我可以建议您在调用 load 方法(System.err.println(session.isOpen()); 或类似的东西)之前和之后检查会话的状态。另一个问题:如果在load()之前手动关闭会话,堆栈跟踪是否等效?
  • 只是为了测试,把lazy="false"加到&lt;many-to-one&gt;上可以吗?另外,您使用的是哪个版本的 Hibernate?
  • @waxwing 我在原始帖子中添加了一些调试信息。结果对我来说看起来很奇怪。我还尝试在session.load(...) 调用之前添加session.close()。这又给了我一个“会话已关闭”异常。如果我将session.close() 移动到session.load(...) 调用下方,我会在此行中得到org.hibernate.LazyInitializationException: could not initialize proxy - no Session:List&lt;MailAccount&gt; mailAccounts = smampiAccount.getMailAccounts();
  • @matt 将lazy="false" 添加到&lt;many-to-one&gt; 无效。我将Hibernate 3.6.3.Final 与mysql-connector-java-3.0.10-stable 和C3P0 一起使用。
  • 只是出于理智的目的,堆栈跟踪是否指向代码示例中 smampiAccount.getMailAccounts(); 的确切行?您是否使用任何其他库来获取 Hibernate 会话,例如 Spring 或任何形式的 AOP?尝试将 Hibernate 升级到更新的 3.6.x 版本,例如 3.6.5 怎么样?也许一个错误修复可以解决这个问题。

标签: java hibernate session


【解决方案1】:

我是世界上最大的白痴。

在 defaultMailAccount 的设置器中,我有这个:

public void setDefaultMailAccount(MailAccount defaultMailAccount) {
    this.defaultMailAccount = defaultMailAccount;
    try {
        databasecontroller.update(this);
    } catch (FailedDatabaseOperationException e) {
        handleException(e, false, null, null);
    }
}

每当 Hibernate 尝试从数据库加载持久版本时,对 databasecontroller.update(this) 的调用会导致级联,并再次导致会话关闭。

将databasecontroller.update(..) 的调用移到方法之外解决了这个问题。

很抱歉占用您的时间并感谢您的帮助!

【讨论】:

  • 我的建议:查看 spring 并阅读“关注点分离”。
  • 当我查看堆栈跟踪时,我知道有些奇怪:在它意识到会话已关闭之前,它在代码中“太远”了(一直到 onPostLoad)。我正要建议它作为一个 Hibernate 错误。但现在一切都说得通了。
猜你喜欢
  • 1970-01-01
  • 2011-01-23
  • 1970-01-01
  • 2012-03-10
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-05
相关资源
最近更新 更多