2012-05-26 29 views
7

Estoy usando JPA 2.0 y Spring en mi desarrollo. Mi clase de entidad contiene dos relaciones @ManyToMany.Cómo usar JPA 2.0 @ManyToMany sin problemas

@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; 
    }  

} 

Cuando corro una consulta para recuperar registros de la tabla de pagos en la base de datos, como

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

Si permito la fetch=FetchType.EAGER en el @ManyToMany, me sale el siguiente error

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 

Pero si elimino fetch=FetchType.EAGER y lo dejo como @ManyToMany, tendré la excepción

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) 

¿qué estoy haciendo mal

Respuesta

12

Para evitar la MultipleBagFetchException, en lugar de utilizar FetchType.EAGER, intente utilizar el @LazyCollection(LazyCollectionOption.FALSE) como en este ejemplo:

@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; 
} 

He aquí una pequeña descripción de Del docs :

  • @LazyCollection: define la opción de latencia en @ManyToMany y @OneToMany asociaciones. LazyCollectionOption puede ser TRUE (la colección es floja y se cargará cuando se accede a su estado), EXTRA (la colección es floja y todas las operaciones intentarán evitar la carga de la colección , esto es especialmente útil para grandes colecciones al cargar todos los elementos no es necesario) y FALSE (asociación no perezoso)

  • @Fetch: define la estrategia de recuperación utilizada para cargar la asociación. FetchMode puede ser SELECCIONADO (una selección se activa cuando se debe cargar la asociación ), SUBSELECCIONAR (solo disponible para colecciones, utilizar una estrategia de subselección - consulte la documentación de Hibernate Reference para obtener más información) o JOIN (use a SQL JOIN para cargar la asociación al cargar la entidad owner). JOIN anula cualquier atributo diferido (una asociación cargada mediante una estrategia JOIN no puede ser floja).

espero que ayude

+0

Funcionó a la perfección. ¿Qué pasa si cambio a la implementación de JPA de enlace de eclipse, no romperá el código? –

+2

@Uchenna De hecho, existen diferencias en las implementaciones de JPA. Por razones de portabilidad, debe evitar características específicas. Hasta donde yo sé, esto es específico de Hibernate, pero también pueden ocurrir en otra implementación problemas que existen en una implementación. Así que creo que lo mejor es lidiar con el problema que tenemos entre manos. – rbento

+0

@ RodrigoS.Bento: ¿funciona porque el modo de captación predeterminado de un LazyCollection es SELECT en lugar de JOIN? –

4

La mejor manera es hacer que las asociaciones XxxtoMany perezoso (por defecto). De esta forma, las colecciones solo se cargarán cuando sea necesario, o cuando le pidas a Hibernate que las busque con entusiasmo utilizando una consulta con una cláusula de captación de unión. Haciéndolas fuerzas EAGER Hibernate a siempre cárguelos, incluso cuando no los necesite.

Por supuesto, si los necesita y están configurados como LAZY, solo se pueden cargar si la sesión todavía está abierta. Una vez que se cierra la sesión, la entidad se desconecta y las colecciones ya no se pueden cargar de forma diferida.Por lo tanto, debe inicializarlos explícitamente (llamando a un método de la colección, o invocando Hibernate.initialize(collection)) antes de cerrar la sesión.

Si realmente desea mantenerlos cargados con entusiasmo, solo uno de ellos debe ser una bolsa (es decir, del tipo Collection o List). Los demás deben declararse como Set.

Nota al margen: el mapeo de la segunda asociación es incorrecto: está utilizando la misma columna de combinación dos veces.

+0

Gracias por su observación y respuesta. Voy a cambiar ambos a Establecer y mantener la carga con entusiasmo y ver si funciona. En la medida en que esto está funcionando ahora, es mi deseo hacer que el código sea portátil. –

Cuestiones relacionadas