【发布时间】: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"加到<many-to-one>上可以吗?另外,您使用的是哪个版本的 Hibernate? -
@waxwing 我在原始帖子中添加了一些调试信息。结果对我来说看起来很奇怪。我还尝试在
session.load(...)调用之前添加session.close()。这又给了我一个“会话已关闭”异常。如果我将session.close()移动到session.load(...)调用下方,我会在此行中得到org.hibernate.LazyInitializationException: could not initialize proxy - no Session:List<MailAccount> mailAccounts = smampiAccount.getMailAccounts(); -
@matt 将
lazy="false"添加到<many-to-one>无效。我将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 怎么样?也许一个错误修复可以解决这个问题。