Подтвердить что ты не робот

Почему я получаю исключение Hibernate LazyInitializationException в этом веб-приложении Spring MVC, когда данные отображаются правильно?

Я пытаюсь создать веб-приложение, используя Spring MVC, с Hibernate как его слой ORM. Однако из-за моей неопытности с обеими структурами я борюсь.

Следующий код будет правильно отображать все записи, которые я ищу, но все равно бросаю трассировку стека в мои журналы. У меня возникли проблемы с поиском полной документации по интеграции Hibernate и SpringMVC (я просмотрел springsource.org и прочитал различные статьи о interweb). Может ли кто-нибудь указать, что я здесь делаю неправильно?

Обратите внимание, что я потратил несколько попыток отследить ответы в Интернете для этого, включая просмотр этого вопроса SO. К сожалению, помощь не помогла.

Следует также отметить, что часть ORM этого приложения была использована и протестирована в автономном приложении Java без проблем. Поэтому я считаю, что интеграция Spring MVC и Hibernate вызывает проблему.

Вот трассировка стека (усеченная) с известной ленивой проблемой инициализации;

2009-03-10 12:14:50,353 [http-8084-6] ERROR org.hibernate.LazyInitializationException.<init>(LazyInitializationException.java:19) - could not initialize proxy - no Session
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
    at com.generic.orm.generated.SearchRule$$EnhancerByCGLIB$$92abaed6.toString(<generated>)
    at java.lang.String.valueOf(String.java:2827)
    at java.lang.StringBuffer.append(StringBuffer.java:219)
    at org.apache.commons.lang.builder.ToStringStyle.appendDetail(ToStringStyle.java:578)
    at org.apache.commons.lang.builder.ToStringStyle.appendInternal(ToStringStyle.java:542)
    at org.apache.commons.lang.builder.ToStringStyle.append(ToStringStyle.java:428)
    at org.apache.commons.lang.builder.ToStringBuilder.append(ToStringBuilder.java:840)
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.appendFieldsIn(ReflectionToStringBuilder.java:606)
.....

Вот код моего контроллера веб-страницы;

private List<Report> getReports() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();

    List<Report> reports = session.createCriteria(Report.class).list();
    Hibernate.initialize(reports);

    session.getTransaction().commit();
    return reports;
}

который используется на веб-странице, используя этот экран html;

<table border="1">
    <c:forEach items="${model.reports}" var="report">
        <tr>
            <td><c:out value="${report.id}"/></td>
            <td><c:out value="${report.username}"/></td>
            <td><c:out value="${report.thresholdMet}"/></td>
            <td><c:out value="${report.results}"/></td>
            <td><c:out value="${report.searchRule.name}"/></td>
            <td><c:out value="${report.uuid}"/></td>
        </tr>
    </c:forEach>
</table>

Примечание. Я добавил report.searchRule.name, чтобы проверить, могу ли я получить объекты в объекте отчета. Он отображается нормально.

И в моем приложенииContext.xml;

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="configLocation">
        <value>classpath:hibernate.cfg.xml</value>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
        </props>
    </property>
</bean>

Здесь представлены отображения ORM, на всякий случай;

hibernate.cfg.xml(по запросу)

<hibernate-configuration>
  <session-factory>
    <property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
    <property name="hibernate.connection.url">jdbc:sqlserver://<removed></property>
    <property name="hibernate.connection.username"><removed></property>
    <property name="hibernate.connection.password"><removed></property>
    <property name="hibernate.current_session_context_class">thread</property>
    <property name="hibernate.show_sql">false</property>
    <mapping resource="com/generic/orm/generated/Report.hbm.xml"/>
    <mapping resource="com/generic/orm/generated/FieldRule.hbm.xml"/>
    <mapping resource="com/generic/orm/generated/Reconciliation.hbm.xml"/>
    <mapping resource="com/generic/orm/generated/SearchRule.hbm.xml"/>
    <mapping resource="com/generic/orm/generated/IndexTemplate.hbm.xml"/>
    <mapping resource="com/generic/orm/generated/Field.hbm.xml"/>
    <mapping resource="com/generic/orm/generated/ErrorCode.hbm.xml"/>
  </session-factory>
</hibernate-configuration>

Из report.hbm.xml

<hibernate-mapping>
    <class name="com.generic.orm.generated.Report" table="Report" schema="dbo" catalog="CoolRecon">
        <id name="id" type="int">
            <column name="ID" />
            <generator class="native" />
        </id>
        <timestamp name="timeStamp" column="TimeStamp" />
        <many-to-one name="searchRule" class="com.generic.orm.generated.SearchRule" fetch="select">
            <column name="SearchRuleName" length="50" not-null="true" />
        </many-to-one>
        <many-to-one name="errorCode" class="com.generic.orm.generated.ErrorCode" fetch="select">
            <column name="ErrorCodeId" />
        </many-to-one>
        <many-to-one name="reconciliation" class="com.generic.orm.generated.Reconciliation" fetch="select">
            <column name="ReconciliationName" length="100" />
        </many-to-one>
        <property name="username" type="string">
            <column name="Username" length="50" />
        </property>
        <property name="supersheetDate" type="timestamp">
            <column name="SupersheetDate" length="23" not-null="true" />
        </property>
        <property name="milliSecondsTaken" type="long">
            <column name="MilliSecondsTaken" not-null="true" />
        </property>
        <property name="thresholdMet" type="boolean">
            <column name="ThresholdMet" not-null="true" />
        </property>
        <property name="results" type="int">
            <column name="Results" not-null="true" />
        </property>
        <property name="exception" type="string">
            <column name="Exception" length="750" />
        </property>
        <property name="uuid" type="string">
            <column name="UUID" length="36" not-null="true" />
        </property>
    </class>
</hibernate-mapping>
4b9b3361

Ответ 1

Я просто догадываюсь, но из трассировки стека кажется, что toString вызывается в SearchRule. Есть ли у SearchRule какие-либо дочерние объекты, которые, возможно, не были загружены? Если SearchRule.toString пытается получить значение для неинициализированного дочернего объекта, что может привести к исключению LazyInitializationException.

Ответ 2

Я просто прошел этот марафон LazyInitialization.

Основная проблема заключается в том, что вы пытаетесь получить доступ к объекту, управляемому спящим интерфейсом, за пределами жизненного цикла Session, то есть в веб-представлении Spring MVC. В моем случае это была ассоциация List<> @OneToMany, которая по умолчанию загружена лениво.

Существует несколько разных подходов - Mark упомянул один, где вы выполняете итерацию "dummy" по ленивым ассоциациям. Вы также можете принудительно загружать загрузку либо через конфигурацию (по всему классу) (в JPA она будет @Fetch(value = FetchType.EAGER)), или более конкретно через HQL. Но это будет более проблематично если ваши ленивые ассоциации Lists.

Самое чистое решение, которое я нашел, это использовать Spring OpenEntityManagerInViewFilter (там OpenSessionInViewFilter для Hibernate) - простой сервлет запустите фильтр web.xml (перед другими фильтрами сервлетов), а Spring автоматически создаст потокобезопасный, поддерживающий транзакцию Session для HTTP-запроса. Больше LazyInitializationException!

Ответ 3

Вызов Hibernate.initialize(list) не инициализирует объекты целевого объекта, на которые ссылаются в коллекции. Вам нужно будет перебирать отчеты и инициализировать каждый отдельный объект. Вызов инициализации отчетов превращает коллекцию прокси в конкретный набор прокси отчетов. Попробуйте выполнить код ниже:

for(Report r : reports)
   Hibernate.initialize(r);

Подпункт тупого топора - это отключение ленивой загрузки, добавляя lazy="false" к вашим классам hbm. Это может иметь смысл, если вы всегда будете перебирать весь объект каждый раз при его извлечении (сделать шаг инициализации OBE).

Ответ 4

На самом деле существует три способа избежать Lazy Initialization Exception:

  • Установите для параметра lazy значение false значение в файле сопоставления. Я не рекомендую этот подход, потому что он увеличит нагрузку на базу данных и, следовательно, приведет к снижению производительности.

  • Держите сеанс открытым. Не закрывайте сеанс перед обработкой данных. Если сеанс открыт во время запроса, вы можете получить связанный граф, но вам нужно быть уверенным, что действие принимает одну и ту же транзакцию.

  • Желательно получить ассоциации. В запросе HQL используйте ключевое слово "выборка" для извлечения связи. С моей точки зрения, это лучшее решение, чтобы избежать ленивой проблемы с инициализацией. В HQL вам просто нужно добавить ключевое слово fetch в предложение from, чтобы с нетерпением получить ассоциацию.

Вот пример:

from Doctor doc
left join fetch doc.patients
where doc.name like ‘Doctor A%’

Я написал сообщение об этой проблеме с некоторыми примерами кода и ссылками на проект github:

http://ignaciosuay.com/how-to-avoid-hibernate-lazy-initialization-exception/

Ответ 5

Хорошо, я идиот. Моя проблема в том, что я взглянул на трассировку стека, но на самом деле не прочитал ее. Вот полный тракт стека (или один из них, 3 несколько разных версий отображаются в моих журналах).

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
    at com.generic.orm.generated.SearchRule$$EnhancerByCGLIB$$de674d10.toString(<generated>)
    at java.lang.String.valueOf(String.java:2827)
    at java.lang.StringBuffer.append(StringBuffer.java:219)
    at org.apache.commons.lang.builder.ToStringStyle.appendDetail(ToStringStyle.java:578)
    at org.apache.commons.lang.builder.ToStringStyle.appendInternal(ToStringStyle.java:542)
    at org.apache.commons.lang.builder.ToStringStyle.append(ToStringStyle.java:428)
    at org.apache.commons.lang.builder.ToStringBuilder.append(ToStringBuilder.java:840)
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.appendFieldsIn(ReflectionToStringBuilder.java:606)
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:759)
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:287)
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:121)
    at com.generic.orm.generated.Report.toString(Report.java:141)
    at java.lang.String.valueOf(String.java:2827)
    at java.lang.StringBuilder.append(StringBuilder.java:115)
    at java.util.AbstractCollection.toString(AbstractCollection.java:422)
    at java.lang.String.valueOf(String.java:2827)
    at java.lang.StringBuilder.append(StringBuilder.java:115)
    at java.util.AbstractMap.toString(AbstractMap.java:490)
    at org.netbeans.modules.web.monitor.server.MonitorFilter.recordRequestAttributes(MonitorFilter.java:1376)
    at org.netbeans.modules.web.monitor.server.MonitorFilter.recordRequestData(MonitorFilter.java:1184)
    at org.netbeans.modules.web.monitor.server.MonitorFilter.getDataBefore(MonitorFilter.java:803)
    at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:361)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:630)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
    at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:167)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:239)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1158)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:808)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:476)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:431)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:390)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Thread.java:619)

Netbeans, по-видимому, делает какой-то мониторинг на стороне сервера, который вызывает toString, который запутывается, потому что что-то, вызванное toString, не инициализируется должным образом. Таким образом, моя проблема в два раза, отраженный на основе ToString кажется плохой идеей для hibernate pojos, а Netbeans меняет свое поведение во время выполнения, пытаясь его наблюдать.

Спасибо всем за помощь, я думаю, что я слишком долго смотрел на эту проблему слишком долго и мне нужно немного отступить.

Ответ 6

@PersistenceContext (тип = PersistenceContextType.EXTENDED)

- РАБОТА:)