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

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

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

Обратите внимание, что я потратил несколько попыток найти ответы на этот вопрос в Интернете, включая просмотр этого ТАКОГО вопроса. К сожалению, это не помогло.

Следует также отметить, что часть 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, чтобы проверить, смогу ли я добраться до объектов внутри объекта отчета. Отображается нормально.

И в моем приложении Kontext.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>

Ответы [ 6 ]

24 голосов
/ 11 марта 2009

Я только что прошел этот марафон LazyInitialization.

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

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

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

5 голосов
/ 11 марта 2009

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

1 голос
/ 09 сентября 2013

На самом деле, существует три способа избежать исключения Lazy Initialization:

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

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

  • С нетерпением извлекайте ассоциации. В запросе HQL используйте ключевое слово «fetch», чтобы получить связь. С моей точки зрения, это лучшее решение, чтобы избежать проблемы отложенной инициализации. В 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/

1 голос
/ 12 ноября 2010

@ PersistenceContext (тип = PersistenceContextType.EXTENDED)

РАБОТАЕТ:)

1 голос
/ 11 марта 2009

Хорошо, я идиот. Моя проблема в том, что я просмотрел трассировку стека, но не прочитал ее. Вот полные следы стека (или одна из них, в моих журналах появляются 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 кажется плохой идеей для гибернационных pojos, и Netbeans меняет мое поведение во время выполнения, пытаясь наблюдать его.

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

1 голос
/ 11 марта 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...