Hibernate:org.hibernate.LazyInitializationException:无法初始化代理 - 没有会话。

15

有以下查询要发送到数据库:

        Session session = EmployeesDAO.getSessionFactory().getCurrentSession();
        List<Employee> employees = new ArrayList<Employee>();
        try {
            session.beginTransaction();
            String hqlQuery = "from Employee emp "
                    + "left join fetch emp.employeesOffices employeesOffice "
                    + "left join fetch employeesOffice.office employeesOfficeOffice "
                    + "left join fetch employeesOfficeOffice.company "
                    + "left join fetch emp.address empAddress "
                    + "left join fetch empAddress.city empAddressCity "
                    + "left join fetch empAddressCity.country";
            Query empQuery = session.createQuery(hqlQuery);
            empQuery.setMaxResults(maxResult);
            employees = (List<Employee>) empQuery.list();
            session.getTransaction().commit();

        } catch (HibernateException e) {
            session.getTransaction().rollback();
            e.printStackTrace();
        }

当获取employee.address.street、employee.address.houseNumber或employee.address.city时,会抛出异常:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:164)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:285)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
    at com.employees.model.Address_$$_javassist_6.getCity(Address_$$_javassist_6.java)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at javax.el.BeanELResolver.getValue(BeanELResolver.java:87)
    at javax.el.CompositeELResolver.getValue(CompositeELResolver.java:67)
    at org.apache.el.parser.AstValue.getValue(AstValue.java:169)
    at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:189)
    at org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:985)
    at org.apache.jsp.WEB_002dINF.listEmployees_jsp._jspx_meth_c_005fout_005f3(listEmployees_jsp.java:306)
    at org.apache.jsp.WEB_002dINF.listEmployees_jsp._jspx_meth_c_005fforEach_005f1(listEmployees_jsp.java:248)
    at org.apache.jsp.WEB_002dINF.listEmployees_jsp._jspx_meth_c_005fforEach_005f0(listEmployees_jsp.java:155)
    at org.apache.jsp.WEB_002dINF.listEmployees_jsp._jspService(listEmployees_jsp.java:89)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:690)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:477)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:402)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:329)
    at com.employees.controller.EmployeeController.processRequest(EmployeeController.java:69)
    at com.employees.controller.EmployeeController.doGet(EmployeeController.java:30)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

员工映射:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.employees.model.Employee" table="employees">
        <id column="employee_id" name="employeeId" type="int">
            <generator class="sequence">
                <param name="sequence">EMP_SEQ</param>
            </generator>
        </id>
        <property column="first_name" name="firstName" type="java.lang.String" />
        <property column="last_name" name="lastName" type="java.lang.String" />

        <many-to-one name="address" column="address_id" 
            class="com.employees.model.Address"/>
        <set name="employeesOffices" >
            <key column="employee_id" />
            <one-to-many class="com.employees.model.EmployeeOffice"/>
        </set>

    </class>
</hibernate-mapping>

地址映射:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.employees.model.Address" table="addresses">
        <id column="address_id" name="addressId" type="int">
            <generator class="sequence">
                <param name="sequence">ADDRESS_SEQ</param>
            </generator>
        </id>
        <property column="street" name="street" type="java.lang.String" />
        <property column="house_number" name="houseNumber" type="int" />
        <many-to-one name="city" column="city_id"
            class="com.employees.model.City"/>
    </class>

</hibernate-mapping>

在其他类(如Office、Company等)中,这是绝对正常的。如果将jsp中加载地址字段的行注释掉,则应用程序可以正常运行而没有任何异常。出了什么问题?顺便说一下,尽管出现异常,但它仍然在jsp上显示所有信息。


离题:我认为在 hibernate-mapping 标签中指定 package 属性会更好,这样你就不需要编写类的完整名称了。 - Aliaksei Bulhak
4个回答

37

您在employee中的对象是lazy初始化的。这意味着,它们仅在非关闭会话上需求时初始化。因此,在从数据库获取后的循环中,您必须手动初始化它们:

Query empQuery = session.createQuery(hqlQuery);
empQuery.setMaxResults(maxResult);
employees = (List<Employee>) empQuery.list();
for (Employee emp : employees) {
    Hibernate.initialize(emp.address);
}

5
那懒加载的意义是什么呢?不如将语义更改为急切获取。 - Rob
@Rob 这并不总是必要的。有时候懒惰初始化很方便,只有在需要时才初始化子元素。 - Andremoniy
没错,这就是为什么我的建议是针对每种情况正确地获取作用域语义。 :) - Rob
1
这里的重点是默认情况下希望映射是延迟加载的。这样可以让你有机会在需要数据时加载它,而不需要时则不加载。如果你需要数据,那就必须要加载它,无法逃避!你可以使用join fetch或者在关闭session之前访问属性,但无论哪种方式,你都需要这些数据,所以一定要确保加载它们! - Alex Barnes
@tiktak 另外添加检查 null 值的代码:... { if (emp != null) Hibernate.initialize(emp.getAddress()); } ...。如果这不起作用,请在此处发布完整的堆栈跟踪信息,以解决此 NPE 异常。 - Andremoniy
显示剩余4条评论

16

自从Hibernate 4.2版本以来,您可以使用

.setProperty("hibernate.enable_lazy_load_no_trans", "true");
这个选项解决了可怕的问题。
org.hibernate.LazyInitializationException: could not initialize proxy -
no Session

这让程序员花费了许多时间。


1
在 Hibernate 4.3.11.Final 中对我无效。 - Hubert Grzeskowiak
这在Hibernate 5.0.11.Final上起了作用。 - Jeremy Rea
适用于我 - Hibernate 4.3.6 Final - Prasanna Mondkar
3
在进行此操作之前,您可以阅读这篇文章。链接如下:https://vladmihalcea.com/2016/09/05/the-hibernate-enable_lazy_load_no_trans-anti-pattern/。 - Nauman Rafique
1
它是反模式。 - zond
@zond,这是唯一能解决我的问题的方法,我们还能做什么来克服这个问题呢? - securecurve

9
与映射无关。当您为对象提供惰性加载时,您承担了ORM世界十年来一直困扰着它的责任:加载对象的会话将与发出惰性请求时存在的会话完全相同
在您的情况下,看起来会话已经完全消失。
您必须有某种方式来维护会话(通常需要对话范围,这意味着要么使用Seam或CDI(在EE 6中),要么通过重新查找对象来将其与新会话重新同步)。

2
<class name="com.employees.model.Employee" table="employees"> 中添加 laze=false,或者在从数据库获取对象时,在 Hibernate.init(Object) 中初始化对象。请注意保留 HTML 标签。

谢谢,但是 lazy=false 不起作用。关于 Hibernate.initialize 的问题,我在另一个答案下已经回答过了。它也不起作用。 - tiktak
哦,我犯了个错误。在 <class name="com.employees.model.Employee" table="employees"> 中指定 lazy=false。这将使 Employee 类的所有字段都不进行延迟初始化。 - Aliaksei Bulhak
所有的Hibernate类属性默认情况下都不是延迟加载的,关联属性通过左连接抓取来加载。这不对吗?我真的看不到任何延迟加载的属性。 - tiktak
1
默认情况下,所有属性都是延迟加载的。 - Aliaksei Bulhak
1
但是,如果您在映射中将所有类的lazy=false设置为false,则会自动进行fetch join。 - Aliaksei Bulhak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接