Hibernate lanza esta excepción durante la creación de SessionFactory:
org.hibernate.loader.MultipleBagFetchException: no puede recuperar simultáneamente varias bolsas
Este es mi caso de prueba:
Parent.java
@Entity
public Parent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
// @IndexColumn(name="INDEX_COL") if I had this the problem solve but I retrieve more children than I have, one child is null.
private List<Child> children;
}
Child.java
@Entity
public Child {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Parent parent;
}
¿Qué tal este problema? ¿Que puedo hacer?
EDITAR
OK, el problema que tengo es que hay otra entidad "padre" dentro de mi padre, mi comportamiento real es este:
Parent.java
@Entity
public Parent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private AnotherParent anotherParent;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<Child> children;
}
AnotherParent.java
@Entity
public AnotherParent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<AnotherChild> anotherChildren;
}
A Hibernate no le gustan dos colecciones FetchType.EAGER
, pero esto parece ser un error, no estoy haciendo cosas inusuales ...
La eliminación FetchType.EAGER
del Parent
o AnotherParent
resuelve el problema, sino que lo necesita, por lo que verdadera solución es utilizar @LazyCollection(LazyCollectionOption.FALSE)
en lugar de FetchType
(gracias a Bozho para la solución).
select * from master; select * from child1 where master_id = :master_id; select * from child2 where master_id = :master_id
List<child>
con lafetchType
definida por más de unaList<clield>
Respuestas:
Creo que una versión más nueva de hibernate (compatible con JPA 2.0) debería manejar esto. Pero de lo contrario, puede solucionarlo anotando los campos de recopilación con:
Recuerde eliminar el
fetchType
atributo de la@*ToMany
anotación.Pero tenga en cuenta que en la mayoría de los casos a
Set<Child>
es más apropiado queList<Child>
, por lo tanto, a menos que realmente necesite unList
- vaya aSet
Pero recuerde que con el uso de conjuntos no eliminará la capa subyacente Producto cartesiano como lo describe Vlad Mihalcea en su respuesta !
fuente
fetchType
de la@*ToMany
?Simplemente cambie de
List
tipo aSet
tipo.¡Pero recuerde que no eliminará el Producto cartesiano subyacente como lo describe Vlad Mihalcea en su respuesta !
fuente
*ToMany
. Cambiar el tipo paraSet
resolver mi problema también. Excelente y ordenada solución. Esta debería ser la respuesta oficial.Agregue una anotación @Fetch específica de Hibernate a su código:
Esto debería solucionar el problema relacionado con el error de Hibernate HHH-1718
fuente
Set
realmente tenga sentido. Tener una solaOneToMany
relación utilizandoSet
resultados en1+<# relationships>
consultas, mientras que utilizandoFetchMode.SUBSELECT
resultados en1+1
consultas. Además, el uso de la anotación en la respuesta aceptada (LazyCollectionOption.FALSE
) hace que se ejecuten incluso más consultas.Teniendo en cuenta que tenemos las siguientes entidades:
Y quieres traer a algún padre
Post
entidades junto con todos loscomments
ytags
colecciones .Si está usando más de uno
JOIN FETCH
directiva:Hibernate arrojará a los infames:
Hibernate no permite buscar más de una bolsa porque eso generaría un producto cartesiano .
La peor "solución"
Ahora, encontrará muchas respuestas, publicaciones de blog, videos u otros recursos que le indican que use un en
Set
lugar de unList
para sus colecciones.Ese es un consejo terrible. ¡No hagas eso!
Usar en
Sets
lugar deLists
hará que elMultipleBagFetchException
desaparezca, pero el Producto cartesiano seguirá estando allí, lo que en realidad es aún peor, ya que descubrirá el problema de rendimiento mucho después de aplicar esta "solución".La solución adecuada
Puedes hacer el siguiente truco:
Siempre que obtenga como máximo una colección usando
JOIN FETCH
, estará bien.Al utilizar múltiples consultas, evitará el Producto cartesiano ya que cualquier otra colección, pero la primera se obtiene mediante una consulta secundaria.
Hay más que puedes hacer
Si está utilizando la
FetchType.EAGER
estrategia en el momento del mapeo@OneToMany
o las@ManyToMany
asociaciones, podría terminar fácilmente con unMultipleBagFetchException
.Es mejor cambiar de
FetchType.EAGER
a,Fetchype.LAZY
ya que la búsqueda ansiosa es una idea terrible que puede conducir a problemas críticos de rendimiento de la aplicación .Conclusión
Evita
FetchType.EAGER
y no cambiesList
aSet
solo porque hacerlo hará que Hibernate escondaMultipleBagFetchException
debajo de la alfombra. Obtenga solo una colección a la vez y estará bien.Siempre que lo haga con la misma cantidad de consultas que las colecciones para inicializar, está bien. Simplemente no inicialice las colecciones en un bucle, ya que eso provocará problemas de consulta N + 1 , que también son malos para el rendimiento.
fuente
DISTINCT
es asesino de rendimiento en esta solución. ¿Hay alguna forma de deshacerse de éldistinct
? (Intenté regresarSet<...>
, no ayudó mucho)PASS_DISTINCT_THROUGH
está configurado comofalse
. DISTINCT tiene 2 significados en JPQL, y aquí, lo necesitamos para deduplicar en el lado de Java, no en el lado de SQL. Mira este artículo para más detalles.hibernate.jdbc.fetch_size
(eventualmente lo configuré en 350). Por casualidad, ¿sabes cómo optimizar las relaciones anidadas? Por ejemplo, entidad1 -> entidad2 -> entidad3.1, entidad 3.2 (donde entidad3.1 / 3.2 son relaciones @OneToMany)Después de probar con todas las opciones descritas en estas publicaciones y en otras, llegué a la conclusión de que la solución es la siguiente.
En cada lugar de XToMany @
XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
e intermediamente despuésEsto funciono para mi
fuente
@Fetch(value = FetchMode.SUBSELECT)
fue suficientePara solucionarlo, simplemente tome el
Set
lugar deList
su objeto anidado.y no olvides usar
fetch=FetchType.EAGER
funcionará.
Hay un concepto mas
CollectionId
en Hibernate si desea seguir solo con la lista.¡Pero recuerde que no eliminará el Producto cartesiano subyacente como lo describe Vlad Mihalcea en su respuesta !
fuente
Encontré una buena publicación de blog sobre el comportamiento de Hibernate en este tipo de asignaciones de objetos: http://blog.eyallupu.com/2010/06/hibernate-exception-sim simultáneamente.html
fuente
puede mantener listas EAGER de stand en JPA y agregar al menos a una de ellas la anotación JPA @OrderColumn (obviamente con el nombre de un campo que se debe ordenar). No necesita anotaciones específicas de hibernación. Pero tenga en cuenta que podría crear elementos vacíos en la lista si el campo elegido no tiene valores que comiencen desde 0
en Niños, entonces debe agregar el campo orderIndex
fuente
Intentamos Set en lugar de List y es una pesadilla: cuando agrega dos objetos nuevos, equals () y hashCode () no logran distinguirlos a ambos. Porque no tienen ninguna identificación.
Las herramientas típicas como Eclipse generan ese tipo de código de las tablas de la base de datos:
También puede leer este artículo que explica correctamente qué tan desordenado es JPA / Hibernate. Después de leer esto, creo que es la última vez que uso un ORM en mi vida.
También me he encontrado con tipos de diseño impulsado por dominio que básicamente dicen que ORM es algo terrible.
fuente
Cuando tenga objetos demasiado complejos con una colección saveral, no podría ser una buena idea tenerlos todos con EAGER fetchType, mejor use LAZY y cuando realmente necesite cargar las colecciones, use:
Hibernate.initialize(parent.child)
para buscar los datos.fuente
Para mí, el problema era haber anidado las recuperaciones de EAGER .
Una solución es establecer los campos anidados en LAZY y usar Hibernate.initialize () para cargar los campos anidados:
fuente
Al final, esto sucedió cuando tuve varias colecciones con FetchType.EAGER, así:
Además, las colecciones se unieron en la misma columna.
Para resolver este problema, cambié una de las colecciones a FetchType.LAZY ya que estaba bien para mi caso de uso.
¡Buena suerte! ~ J
fuente
Comentando ambos
Fetch
y aLazyCollection
veces ayuda a ejecutar el proyecto.fuente
Una cosa buena
@LazyCollection(LazyCollectionOption.FALSE)
es que varios campos con esta anotación pueden coexistir mientrasFetchType.EAGER
que no pueden, incluso en las situaciones en que dicha coexistencia es legítima.Por ejemplo, un
Order
puede tener una lista deOrderGroup
(una corta) así como una lista dePromotions
(también corta).@LazyCollection(LazyCollectionOption.FALSE)
se puede usar en ambos sin causarLazyInitializationException
ningunoMultipleBagFetchException
.En mi caso
@Fetch
resolvió mi problemaMultipleBacFetchException
pero luego causaLazyInitializationException
el infameno Session
error.fuente
Podría usar una nueva anotación para resolver esto:
De hecho, el valor predeterminado de fetch también es FetchType.LAZY.
fuente