web-dev-qa-db-ja.com

データが正しく表示されているのに、このSpring MVCWebアプリケーションでHibernateLazyInitializationExceptionが発生するのはなぜですか?

HibernateをORMレイヤーとして、SpringMVCを使用してWebアプリケーションを作成しようとしています。ただし、両方のフレームワークの経験が浅いため、苦労しています。

次のコードは、探しているすべてのレコードを正しく表示しますが、それでもスタックトレースをログにスローします。 HibernateとSpringMVCの統合に関する完全なドキュメントを見つけるのに苦労しています(springsource.orgを調べて、interwebのさまざまな記事を読みました)。私がここで間違っている可能性があることを誰かが指摘できますか?

this SOの質問を見るなど、インターネットで回答を追跡するためにいくつかの時間を費やしたことに注意してください。残念ながら、これは役に立ちませんでした。

また、このアプリケーションのORM部分は、スタンドアロンJavaアプリケーションで問題なく使用およびテストされているため、SpringMVCと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)
.....

これが私のWebページコントローラーからのコードです。

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を使用してWebページで採用されています。

<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を追加しました。正常に表示されます。

そして私のapplicationContext.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>
11
James McMahon

推測しているだけですが、スタックトレースから、toStringがSearchRuleで呼び出されているようです。 SearchRuleには、ロードされていない可能性のある子オブジェクトがありますか? SearchRule.toStringが、LazyInitializationExceptionを引き起こす可能性のある初期化されていない子オブジェクトの値を取得しようとした場合。

5
Mark

私はこのLazyInitializationマラソンを通過しました。

主要な問題は、Sessionのライフサイクル外、つまりSpring MVCのWebビューで、休止状態で管理されているエンティティにアクセスしようとしていることです。私の場合、これは_List<>_ _@OneToMany_アソシエーションであり、デフォルトで遅延ロードされます。

いくつかの異なるアプローチがあります-マークは、怠惰な関連付けに対して「ダミー」の反復を行う方法について言及しました。構成(クラス全体)(JPAでは@Fetch(value = FetchType.EAGER))を介して、またはより具体的にはHQLを介して、積極的な読み込みを強制することもできます。しかし、これはもっと証明されます 怠惰な関連付けがListsの場合は問題があります

私が見つけた最もクリーンな解決策は、Springの OpenEntityManagerInViewFilter (HibernateにはOpenSessionInViewFilterがあります)-_web.xml_(他のサーブレットフィルターよりも先に)、Springはスレッドセーフでトランザクション対応のSessionper-HTTP-requestを自動的に作成します。もうLazyInitializationException

24
jtgameover

実際、遅延初期化例外を回避する方法は3つあります。

  • マッピングファイルでlazyプロパティをfalseに設定します。データベースの負荷が増加し、パフォーマンスが低下するため、このアプローチはお勧めしません。

  • セッションを開いたままにします。データを処理する前にセッションを閉じないでください。リクエスト中にセッションが開いている場合は、関連するグラフを取得できますが、アクションが同じトランザクション内で実行されることを確認する必要があります。

  • 関連付けを熱心にフェッチします。 HQLクエリで、キーワード「fetch」を使用して関連付けを取得します。私の観点からは、これは遅延初期化の問題を回避するための最良の解決策です。 HQLでは、from句にfetchキーワードを追加するだけで、関連付けを熱心にフェッチできます。

次に例を示します。

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
ignacio.suay

@PersistenceContext(type = PersistenceContextType.EXTENDED)

仕事です:)

1
shalunv

Hibernate.initialize(list)呼び出しは、コレクションで参照されているターゲットエンティティオブジェクトを初期化しません。レポートを繰り返し処理し、個々のオブジェクトを初期化する必要があります。レポートを初期化するための呼び出しは、プロキシコレクションをレポートプロキシの具体的なコレクションに変えます。以下のコードを試してください。

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

鈍い斧のアプローチは、hbmクラスにlazy="false"を追加して、遅延読み込みをオフにすることです。これは、オブジェクトを取得するたびに常にオブジェクト全体を反復処理する場合に意味があります(初期化ステップをOBEにします)。

1
Mark

わかりました、私はばかです。私の問題は、スタックトレースを一瞥したが、実際には読み取れなかったことです。これが完全なスタックトレースです(またはそのうちの1つ、わずかに異なる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によって呼び出されたものが適切に初期化されていないため、異常終了します。したがって、私の問題は2つあります。リフレクションベースのToStringは、休止状態のpojoにとっては悪い考えのようであり、Netbeansはそれを観察しようとしてランタイム動作を変更しています。

皆さんの助けに感謝します。私はこの問題をあまりにも長い間綿密に調べていたので、少し後退する必要があったと思います。

1
James McMahon