2012-05-26 1 views
7

Ich verwende JPA 2.0 und Spring in meiner Entwicklung. Meine Entitätsklasse enthält zwei @ManyToMany-Beziehungen.So verwenden Sie JPA 2.0 @ManyToMany ohne Probleme

@Entity("payment") 
public class PaymentData implements Serializable 
{ 
    private Long pk; 

    private Collection<PaymentItemData> paymentItem; 
    /** 
    * minorPaymentItem 
    * 
    */ 
    private Collection<MinorPayItemData> minorPaymentItem; 

    @ManyToMany(fetch=FetchType.EAGER) 
    @JoinTable(name = "payitem_m_assig", 
    joinColumns = 
    @JoinColumn(name = "pay_item_id", nullable = false), 
    inverseJoinColumns = 
    @JoinColumn(name = "minor_pay_item_id", nullable = false)) 
    public Collection<MinorPayItemData> getMinorPaymentItem() 
    { 
     return minorPaymentItem; 
    } 

    /** 
    * @param minorPaymentItem the minorPaymentItem to set 
    */ 
    public void setMinorPaymentItem(final Collection<MinorPayItemData> value) 
    { 
     this.minorPaymentItem = value; 
    } 

    @ManyToMany(fetch=FetchType.EAGER) 
    @JoinTable(name = "payitem_assigned", 
    joinColumns = 
    @JoinColumn(name = "pay_item_id", nullable = false), 
    inverseJoinColumns = 
    @JoinColumn(name = "pay_item_id", nullable = false)) 
    public Collection<PaymentItemData> getPaymentItem() 
    { 
     return paymentItem; 
    } 

    /** 
    * Set the property paymentItem 
    * 
    * @param value -paymentItem 
    * 
    */ 
    public void setPaymentItem(final Collection<PaymentItemData> value) 
    { 
     this.paymentItem = value; 
    }  

} 

Wenn ich eine Abfrage ausführen, Datensätze aus der Zahlungstabelle in der Datenbank abgerufen werden, wie

Query q = manager.createQuery("select a from PaymentData a"); 
q.getResultList(); 

Wenn ich die fetch=FetchType.EAGER auf dem @ManyToMany erlauben, erhalte ich die folgenden Fehler

Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags 
    at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:94) 
    at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:119) 
    at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:71) 
    at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:54) 
    at org.hibernate.loader.entity.BatchingEntityLoader.createBatchingEntityLoader(BatchingEntityLoader.java:133) 
    at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1914) 
    at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1937) 
    at org.hibernate.persister.entity.AbstractEntityPersister.createLoaders(AbstractEntityPersister.java:3205) 
    at org.hibernate.persister.entity.AbstractEntityPersister.postInstantiate(AbstractEntityPersister.java:3191) 
    at org.hibernate.persister.entity.SingleTableEntityPersister.postInstantiate(SingleTableEntityPersister.java:728) 
    at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:348) 
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1845) 
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906) 
    ... 39 more 

Aber wenn ich fetch=FetchType.EAGER entferne und es als @ManyToMany lasse, werde ich die Ausnahme

haben
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.data.PaymentData.paymentItem, no session or session was closed 
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383) 
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375) 
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368) 
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111) 
    at org.hibernate.collection.PersistentBag.toString(PersistentBag.java:506) 
    at java.lang.String.valueOf(String.java:2826) 
    at java.lang.StringBuilder.append(StringBuilder.java:115) 
    at com.niu.util.Util.toString(Util.java:131) 
    at com.niu.util.data.BaseData.toString(BaseData.java:107) 
    at java.lang.String.valueOf(String.java:2826) 
    at java.lang.StringBuilder.append(StringBuilder.java:115) 
    at java.util.AbstractCollection.toString(AbstractCollection.java:422) 
    at java.lang.String.valueOf(String.java:2826) 
    at java.io.PrintStream.println(PrintStream.java:771) 
    at org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:269) 
    at sun.reflect.GeneratedMethodAccessor1861.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:452) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:291) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:254) 
    at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:176) 
    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:133) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:207) 
    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:207) 
    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:190) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:75) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:94) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.niu.web.common.interceptor.ApplicationModelDrivenInterceptor.intercept(ApplicationModelDrivenInterceptor.java:31) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at org.apache.struts2.interceptor.ProfilingActivationInterceptor.intercept(ProfilingActivationInterceptor.java:104) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:270) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171) 
    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:176) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:190) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:187) 
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:248) 
    at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52) 
    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:498) 
    at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77) 
    at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:91) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) 
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:399) 
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:317) 
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:204) 
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:311) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
    at java.lang.Thread.run(Thread.java:662) 

Was mache ich falsch

Antwort

12

die MultipleBagFetchException zu vermeiden, statt FetchType.EAGER zu verwenden, versuchen, die @LazyCollection(LazyCollectionOption.FALSE) wie in diesem Beispiel mit:

@ManyToMany 
@LazyCollection(LazyCollectionOption.FALSE) 
@JoinTable(name = "payitem_m_assig", joinColumns = @JoinColumn(name = "pay_item_id", nullable = e), inverseJoinColumns = @JoinColumn(name = "minor_pay_item_id", nullable = false)) 
public Collection<MinorPayItemData> getMinorPaymentItem() 
{ 
    return minorPaymentItem; 
} 

Hier eine kleine Beschreibung von den docs :

  • @LazyCollection: Definiert die Lazyness-Option in @ManyToMany und @ OneToMany-Zuordnungen. LazyCollectionOption kann TRUE sein (die Sammlung ist faul und wird geladen, wenn ihr Status zugegriffen wird), EXTRA (die Sammlung ist faul und alle Operationen werden versuchen, die Sammlung zu vermeiden, dies ist besonders nützlich für große Sammlungen beim Laden alle Elemente ist nicht erforderlich) und FALSE (Verband nicht faul)

  • @Fetch: definiert die Abrufstrategie verwendet, um die Vereinigung zu laden. FetchMode kann SELECT sein (eine Auswahl wird ausgelöst, wenn die Verknüpfung geladen werden muss), SUBSELECT (nur für Sammlungen verfügbar, verwenden Sie eine Subselect-Strategie - bitte beziehen Sie sich auf die Hibernate-Referenz Dokumentation für weitere Informationen) oder JOIN (verwenden Sie a SQL JOIN zum Laden die Zuordnung beim Laden der Eigentümerinstanz). JOIN überschreibt jedes beliebige Lazy-Attribut (eine über eine JOIN-Strategie geladene Verknüpfung kann nicht faul sein).

Ich hoffe, es

+0

Es funktionierte nahtlos. Was passiert, wenn ich die JPA-Implementierung der Eclipse-Verbindung ändere, würde das den Code nicht brechen? –

+2

@Uchenna Tatsächlich gibt es Unterschiede in JPA-Implementierungen. Aus Gründen der Portabilität sollten Sie bestimmte Funktionen vermeiden. Soweit ich weiß, ist dies ein Hibernate-spezifischer, aber auch Probleme, die in einer Implementierung existieren, können in einer anderen nicht auftreten. Also denke ich, das Beste ist, mit dem Problem fertig zu werden. – rbento

+0

@ RodrigoS.Bento: Funktioniert es, weil der Standard-Abrufmodus einer LazyCollection SELECT statt JOIN ist? –

4

Der beste Weg hilft, ist die XxxtoMany Verbände faul (Standard) zu machen. Auf diese Weise werden die Sammlungen nur dann geladen, wenn sie benötigt werden, oder wenn Sie Hibernate anweisen, sie mithilfe einer Abfrage mit einer Join-Fetch-Klausel abzurufen. Wenn Sie EAGER machen, wird der Ruhezustand auf immer geladen, auch wenn Sie sie nicht benötigen.

Natürlich, wenn Sie sie benötigen und sie als LAZY konfiguriert sind, können sie nur geladen werden, wenn die Sitzung noch offen ist. Sobald die Sitzung geschlossen ist, wird die Entität getrennt und die Sammlungen können nicht mehr geladen werden.Sie müssen sie daher explizit initialisieren (indem Sie eine Methode der Auflistung aufrufen, oder indem Sie Hibernate.initialize(collection) aufrufen), bevor Sie die Sitzung schließen.

Wenn Sie wirklich wollen, dass sie eifrig geladen bleiben, dann sollte nur eine von ihnen eine Tasche sein (d. H. Vom Typ Collection oder List). Die anderen sollten als Set deklariert werden.

Randnotiz: Die Zuordnung der zweiten Assoziation ist falsch: Sie verwenden dieselbe Join-Spalte zweimal.

+0

Danke für Ihre Beobachtung und Antwort. Ich werde beide zu Set ändern und die eifrig geladenen behalten und sehen, ob es funktioniert. Insofern das jetzt funktioniert, ist es mein Wunsch, den Code portabel zu machen. –

Verwandte Themen