2009-03-10 14 views
11

Próbuję utworzyć aplikację internetową przy użyciu Spring MVC, a jako warstwę ORM jako warstwę Hibernate. Jednak ze względu na brak doświadczenia w obu frameworkach mam problemy.Dlaczego otrzymuję Hibernate LazyInitializationException w tej wiosennej aplikacji internetowej MVC, gdy dane są wyświetlane poprawnie?

Poniższy kod poprawnie wyświetli wszystkie poszukiwane rekordy, ale nadal będzie zawierał ślad stosu w moich dziennikach. Mam problem ze znalezieniem szczegółowej dokumentacji dotyczącej integracji Hibernate i SpringMVC (zajrzałem do springsource.org i przeczytałem różne artykuły na temat interweb). Czy ktokolwiek mógłby wskazać, co mógłbym robić źle tutaj?

Należy zauważyć, że spędziłem kilka prób, aby znaleźć odpowiedzi w Internecie na ten temat, w tym na pytanie this SO. Co niestety nie było pomocne.

Należy również zauważyć, że część ORM tej aplikacji została bez problemu wykorzystana i przetestowana w samodzielnej aplikacji Java. Uważam, że przyczyną problemu jest integracja Spring MVC i Hibernate.

Oto ślad stosu (obcięty) ze słynnym problemem leniwego inicjowania;

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) 
..... 

Oto kod kontrolera mojej strony internetowej;

Który jest używany na stronie internetowej przy użyciu tego wyświetlacza html;

Uwaga: Dodałem report.searchRule.name, aby przetestować, czy mogę uzyskać dostęp do obiektów w obiekcie raportu. Wyświetla się dobrze.

I w moim 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> 

Oto odwzorowania ORM, na wszelki wypadek;

hibernate.cfg.xml (na żądanie)

<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> 

Od 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> 
+0

pls POST mapowanie także dla raportów –

+0

Dodano plik hibernate.cfg.xml, który zawiera moje mapowania. –

+0

Czy możesz wyświetlić plik Report.hbm.xml? –

Odpowiedz

5

Zgaduję, ale ze śladu stosu wydaje się, że toString jest wywoływany w SearchRule. Czy funkcja SearchRule ma jakiekolwiek obiekty podrzędne, które mogły nie zostać załadowane? Jeśli funkcja SearchRule.toString próbowała uzyskać wartość niezainicjowanego obiektu podrzędnego, co może spowodować wyjątek LazyInitializationException.

+1

Hmm, tak. Używam popularnego Apache ReflectionToString, aby zbudować moje ciągi, spróbuję je usunąć jutro. Widziałem, że w stosie także ślad, nie wiem, dlaczego to nie przyszło mi do głowy, dopóki nie wspomniałeś o tym. Myślę, że zbyt długo wpatrywałem się w ten problem. –

+0

Bez problemu, miło mi pomóc. – Mark

1

Hibernate.initialize (lista) rozmowa nie zainicjować obiekty jednostka docelowa które są przywoływane w kolekcji. Będziesz musiał powtórzyć raporty i zainicjować każdy pojedynczy obiekt. Wywołanie inicjowania raportów przekształca kolekcję proxy w konkretną kolekcję serwerów proxy raportów. Spróbuj kod poniżej:

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

Podejście tępy topór jest, aby wyłączyć leniwy załadunku dodając lazy="false" do swoich klas HBM. Może to mieć sens, jeśli zawsze będzie się iterować nad całym obiektem za każdym razem, gdy zostanie on pobrany (wykonaj krok inicjalizacji OBE).

+0

Tak, próbowałem już tego wcześniej, bez radości. –

+0

To nie zadziałało! – user613114

24

Właśnie przeszedłem przez ten maratoninicjację maratonu.

Głównym problemem jest to, że próbujesz uzyskać dostęp do zarządzanego przez hibernację obiektu poza cyklem życia urządzenia Session, tj. W widoku sieciowym Spring MVC. W moim przypadku było to skojarzenie List<>@OneToMany, które domyślnie są ładowane leniwie.

Istnieje kilka różnych podejść - zaznaczył jeden z nich, gdzie wykonujesz "atrapę" iteracji nad leniwymi skojarzeniami. Możesz także wymusić ładowanie, korzystając z konfiguracji (w całej klasie) (w języku JPA będzie to @Fetch(value = FetchType.EAGER)) lub dokładniej za pośrednictwem HQL. Ale to się okaże więcej problematic if your lazy associations are Lists.

Najczystsze rozwiązanie znalazłem było użycie sprężyny OpenEntityManagerInViewFilter (Jest takie OpenSessionInViewFilter dla Hibernate) - prosty filtr servlet upadłego w celu web.xml (wyprzedzić innych filtrów serwletów), a wiosną będzie automatycznie utworzyć wątku bezpieczny , świadomy transakcji Sessionżądanie per-HTTP. Nigdy więcej LazyInitializationException!

+0

Dzięki, nie dotarłem jeszcze do tego problemu (nie wiem dlaczego), ale brzmi to jak rada, która może być naprawdę przydatna w przyszłości. –

0

Ok Jestem idiotą. Mój problem polega na zerknięciu na ślad stosu, ale tak naprawdę go nie czytałem. Oto pełne ślady stosu (lub jedna z nich, 3 nieznacznie różne wersje pojawiają się w moich dziennikach).

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 jest podobno robi jakieś monitorowania po stronie serwera, który wywołuje toString, która wpada w panikę, bo coś zwane przez toString nie jest prawidłowo zainicjowany. Więc mój problem jest dwojaki, Refleksowanie ToStringiem wydaje się złym pomysłem na hibernację, a Netbeans zmienia moje zachowanie podczas robienia tego, próbując go zaobserwować.

Dziękuję wszystkim za pomoc, myślę, że właśnie zbyt długo przyglądałem się temu problemowi i musiałem trochę odejść.

0

@PersistenceContext (typ = PersistenceContextType.EXTENDED)

jest praca :)

+1

Odp .: "@PersistenceContext (type = PersistenceContextType.EXTENDED) jest WORK :)" To dość prosta odpowiedź. PersistenceContextType.EXTENDED oznacza, że ​​musisz zarządzać własnymi transakcjami – mhvelplund

1

Faktycznie, istnieją trzy sposoby, aby uniknąć Exception leniwe inicjowanie:

  • Set właściwość leniwy do fałszu w pliku odwzorowania.Nie polecam tego podejścia, ponieważ zwiększy to obciążenie bazy danych, a zatem spowoduje wygenerowanie de zagięcie w wydajności.

  • Pozostaw sesję otwartą. Nie zamykaj sesji przed przetworzeniem danych. Jeśli sesja jest otwarta podczas żądania, możesz uzyskać powiązany wykres, ale musisz mieć pewność, że czynność odbywa się w ramach tej samej transakcji.

  • Chętnie pobrać skojarzenia. W kwerendzie HQL użyj słowa kluczowego "pobierz", aby pobrać powiązanie. Z mojego punktu widzenia jest to najlepsze rozwiązanie, aby uniknąć leniwego problemu z inicjalizacją. W HQL wystarczy dodać słowo kluczowe fetch w klauzuli from, aby z ochotą pobrać skojarzenie.

Oto przykład:

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

napisałem post o tej kwestii, niektóre przykłady kodu i linki do projektu github:

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