org.hibernate.LazyInitializationException:如何正确使用Hibernate的延迟加载function

使用Hibernate和lazy = true模式从数据库加载对象列表时遇到了一些问题。 希望有人可以帮助我。

我有一个名为UserAccount的简单类,如下所示:

public class UserAccount { long id; String username; List mailAccounts = new Vector(); public UserAccount(){ super(); } public long getId(){ return id; } public void setId(long id){ this.id = id; } public String getUsername(){ return username; } public void setUsername(String username){ this.username = username; } public List getMailAccounts() { if (mailAccounts == null) { mailAccounts = new Vector(); } return mailAccounts; } public void setMailAccounts(List mailAccounts) { this.mailAccounts = mailAccounts; } } 

我通过以下映射文件在Hibernate中映射此类:

                

如您所见,lazy在bag mapping元素中设置为“true”。

将数据保存到数据库工作正常:

加载也可以通过调用loadUserAccount(String username) (参见下面的代码):

 public class HibernateController implements DatabaseController { private Session session = null; private final SessionFactory sessionFactory = buildSessionFactory(); public HibernateController() { super(); } private SessionFactory buildSessionFactory() { try { return new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { System.err.println("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public UserAccount loadUserAccount(String username) throws FailedDatabaseOperationException { UserAccount account = null; Session session = null; Transaction transaction = null; try { session = getSession(); transaction = session.beginTransaction(); Query query = session.createQuery("FROM UserAccount WHERE username = :uname").setParameter("uname", username)); account = (UserAccount) query.uniqueResult(); transaction.commit(); } catch (Exception e) { transaction.rollback(); throw new FailedDatabaseOperationException(e); } finally { if (session.isOpen()) { // session.close(); } } return account; } private Session getSession() { if (session == null){ session = getSessionFactory().getCurrentSession(); } return session; } } 

问题是:当我访问列表“mailAccounts”中的元素时,我得到以下exception:

org.hibernate.LazyInitializationException:懒得初始化角色集合:test.account.UserAccount.mailAccounts,没有会话或会话被关闭

我假设这个exception的原因是会话被关闭(不知道为什么和如何),因此Hibernate无法加载列表。 如您所见,我甚至从loadUserAccount()方法中删除了session.close()调用,但会话似乎仍然被关闭或被另一个实例替换。 如果我设置lazy=false ,那么一切都很顺利,但这不是我想要的,因为我需要由于性能问题“按需”加载数据的function。

因此,如果在loadUserAccount(String username)终止后我无法确定我的会话是否仍然有效,那么具有该function的重点是什么?如何解决这个问题?

谢谢你的帮助!

Ps:我是Hibernate初学者,所以请原谅我的noobishness。

更新:这是我的hibernate config.cfg.xml

     com.mysql.jdbc.Driver foo jdbc:mysql://localhost:3306/mytable user org.hibernate.dialect.MySQLInnoDBDialect  <!-- create-->  thread      

延迟加载是否与事务边界无关。 它只要求会话开放。

但是,当会话打开时,取决于你实际上如何设置SessionFactory,你没有告诉我们! 在SessionFactory.getCurrentSession()实际执行的操作后面有配置! 如果您使用ThreadLocalSessionContext的默认版本并且没有做任何事情来管理生命周期,那么它确实默认在您提交时关闭会话。 (因此,扩展事务边界的常见概念是延迟加载exception的“修复”。)

如果使用sessionFactory.openSession()session.close()管理自己的会话生命周期,则可以在事务边界外的会话生命周期内延迟加载。 或者,您可以提供ThreadLocalSessionContext的子类,该子类使用您需要的边界管理会话生命周期。 还有一些现成的替代方案,例如OpenSessionInViewfilter,可以在Web应用程序中使用它来将会话生命周期绑定到Web请求生命周期。

编辑:当然,如果适合您,您也可以初始化事务中的列表。 我认为当你需要为你的实体的每个水合程度的某种’flag’参数的新方法签名时,这会导致真正笨重的API。 dao.getUser()dao.getUserWithMailAccounts()dao.getUserWIthMailAccountsAndHistoricalIds()等等。

编辑2:您可能会发现这对于会话保持打开的时间/会话范围与事务范围之间的关系的不同方法很有帮助。 (特别是每个请求的会话 – 分离对象与每个会话的会话的想法。)

这取决于您的要求和架构,实际上对话有多大。

您获得exception的原因可能是您加载数据的事务已关闭(以及与之相关的会话),即您在会话外工作。 在一个会话中使用实体时(或正确使用二级缓存时跨会话),延迟加载特别有用。

AFAIK你可以告诉Hibernate自动打开一个新的会话进行延迟加载但是我暂时没有使用它,因此我必须再查看它是如何工作的。

您需要将整个流程包装在事务中。

因此,不要在loadUserAccount中启动和提交事务,而是在其之外执行。

例如:

 public void processAccount() { getSession().beginTransaction(); UserAccount userAccount = loadUserAccount("User"); Vector accts = userAccount.getMailAccounts(); //This here is lazy-loaded -- DB requests will happen here getSession().getTransaction().commit(); } 

通常,您希望将事务包装在整个工作单元中。 我怀疑你对交易的理解有点过于细微。