O Hibernate lança essa exceção durante a criação do SessionFactory:
org.hibernate.loader.MultipleBagFetchException: não é possível buscar simultaneamente vários pacotes
Este é o meu caso de teste:
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;
}
E esse problema? O que eu posso fazer?
EDITAR
OK, o problema que tenho é que outra entidade "pai" está dentro do meu pai, meu comportamento real é 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;
}
O Hibernate não gosta de duas coleções FetchType.EAGER
, mas isso parece ser um bug, não estou fazendo coisas incomuns ...
Remoção FetchType.EAGER
de Parent
ou AnotherParent
resolve o problema, mas eu preciso dele, então verdadeira solução é usar @LazyCollection(LazyCollectionOption.FALSE)
em vez de FetchType
(graças a Bozho para a solução).
select * from master; select * from child1 where master_id = :master_id; select * from child2 where master_id = :master_id
List<child>
comfetchType
definido para mais de umList<clield>
Respostas:
Eu acho que uma versão mais recente do hibernate (com suporte ao JPA 2.0) deve lidar com isso. Caso contrário, você pode contornar os campos da coleção com:
Lembre-se de remover o
fetchType
atributo da@*ToMany
anotação.Mas observe que, na maioria dos casos, a
Set<Child>
é mais apropriado do queList<Child>
, portanto, a menos que você realmente precise deList
- vá paraSet
Mas lembre-se de que, com o uso de conjuntos, você não eliminará o Produto Cartesiano subjacente, conforme descrito por Vlad Mihalcea em sua resposta !
fonte
fetchType
do@*ToMany
?Simplesmente mude de um
List
tipo para outroSet
.Mas lembre-se de que você não eliminará o Produto Cartesiano subjacente, conforme descrito por Vlad Mihalcea em sua resposta !
fonte
*ToMany
. Alterar o tipo paraSet
resolver o meu problema também. Solução excelente e arrumada. Essa deve ser a resposta oficial.Adicione uma anotação @Fetch específica do Hibernate ao seu código:
Isso deve corrigir o problema relacionado ao bug do Hibernate HHH-1718
fonte
Set
realmente faça sentido. Ter um únicoOneToMany
relacionamento usandoSet
resultados em1+<# relationships>
consultas, enquanto que usandoFetchMode.SUBSELECT
resultados em1+1
consultas. Além disso, o uso da anotação na resposta aceita (LazyCollectionOption.FALSE
) faz com que ainda mais consultas sejam executadas.Considerando que temos as seguintes entidades:
E você deseja buscar algumas
Post
entidades pai junto com todas as coleçõescomments
etags
.Se você estiver usando mais de uma
JOIN FETCH
diretiva:O Hibernate lançará o infame:
O Hibernate não permite buscar mais de uma bolsa, pois isso geraria um produto cartesiano .
A pior "solução"
Agora, você encontrará muitas respostas, postagens de blog, vídeos ou outros recursos solicitando que você use um em
Set
vez de umList
para suas coleções.Esse é um conselho terrível. Não faça isso!
Usar em
Sets
vez deLists
faráMultipleBagFetchException
desaparecer, mas o Produto Cartesiano ainda estará lá, o que é ainda pior, pois você descobrirá o problema de desempenho muito depois de aplicar essa "correção".A solução adequada
Você pode executar o seguinte truque:
Contanto que você busque no máximo uma coleção usando
JOIN FETCH
, você ficará bem.Ao usar várias consultas, você evitará o produto cartesiano, pois qualquer outra coleção, mas a primeira é buscada usando uma consulta secundária.
Há mais que você poderia fazer
Se você estiver usando a
FetchType.EAGER
estratégia no momento do mapeamento para@OneToMany
ou@ManyToMany
associações, poderá facilmente terminar com aMultipleBagFetchException
.É melhor mudar de
FetchType.EAGER
para,Fetchype.LAZY
pois a busca ansiosa é uma péssima idéia que pode levar a problemas críticos de desempenho do aplicativo .Conclusão
Evite
FetchType.EAGER
e não mude deList
paraSet
apenas porque isso fará com que o Hibernate oculte oMultipleBagFetchException
sob o tapete. Busque apenas uma coleção de cada vez, e você ficará bem.Contanto que você faça isso com o mesmo número de consultas que possui coleções para inicializar, você estará bem. Apenas não inicialize as coleções em um loop, pois isso desencadeará problemas de consulta N + 1 , que também prejudicam o desempenho.
fonte
DISTINCT
é assassino de desempenho nesta solução. Existe uma maneira de se livrardistinct
? (tentou retornarSet<...>
em vez disso, não ajuda muito)PASS_DISTINCT_THROUGH
está definido comofalse
. DISTINCT tem 2 significados no JPQL, e aqui, precisamos deduplicar no lado Java, não no SQL. Confira este artigo para mais detalhes.hibernate.jdbc.fetch_size
(eventualmente eu o configurei para 350). Por acaso, você sabe como otimizar relações aninhadas? Por exemplo entity1 -> entity2 -> entity3.1, entidade 3.2 (onde entity3.1 / 3.2 são relações @OneToMany)Depois de tentar com todas as opções descritas nessas postagens e em outras, cheguei à conclusão de que a correção é a seguinte.
Em todo lugar XToMany @
XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
e intermediariamente apósIsso funcionou para mim
fonte
@Fetch(value = FetchMode.SUBSELECT)
foi suficientePara corrigi-lo, basta
Set
substituirList
o objeto aninhado.e não se esqueça de usar
fetch=FetchType.EAGER
vai funcionar.
Há mais um conceito
CollectionId
no Hibernate, se você deseja manter apenas a lista.Mas lembre-se de que você não eliminará o Produto Cartesiano subjacente, conforme descrito por Vlad Mihalcea em sua resposta !
fonte
Encontrei uma boa publicação no blog sobre o comportamento do Hibernate nesse tipo de mapeamento de objetos: http://blog.eyallupu.com/2010/06/hibernate-exception-simultially.html
fonte
você pode manter as listas do EAGER do estande na JPA e adicionar a pelo menos uma delas a anotação da JPA @OrderColumn (com obviamente o nome de um campo a ser solicitado). Não há necessidade de anotações de hibernação específicas. Mas lembre-se de que ele pode criar elementos vazios na lista se o campo escolhido não tiver valores começando em 0
em Filhos, você deve adicionar o campo orderIndex
fonte
Tentamos definir em vez de lista e é um pesadelo: quando você adiciona dois novos objetos, equals () e hashCode () não conseguem distinguir os dois! Porque eles não têm identificação.
ferramentas típicas como o Eclipse geram esse tipo de código a partir de tabelas do banco de dados:
Você também pode ler este artigo que explica corretamente como o JPA / Hibernate está bagunçado. Depois de ler isso, acho que é a última vez que uso ORM em minha vida.
Também encontrei caras do Design Orientado a Domínio que basicamente dizem que o ORM é uma coisa terrível.
fonte
Quando você tem objetos muito complexos com coleção saveral, não seria uma boa ideia tê-los com o EAGER fetchType, use melhor o LAZY e, quando você realmente precisar carregar as coleções, use:
Hibernate.initialize(parent.child)
para buscar os dados.fonte
Para mim, o problema era ter aninhados no EAGER .
Uma solução é definir os campos aninhados como LAZY e usar Hibernate.initialize () para carregar os campos aninhados:
fonte
No final, isso aconteceu quando eu tive várias coleções com o FetchType.EAGER, assim:
Além disso, as coleções estavam se juntando na mesma coluna.
Para resolver esse problema, alterei uma das coleções para FetchType.LAZY, pois estava tudo bem no meu caso de uso.
Boa sorte! ~ J
fonte
Comentar
Fetch
eLazyCollection
às vezes ajuda a executar o projeto.fonte
Uma coisa boa
@LazyCollection(LazyCollectionOption.FALSE)
é que vários campos com esta anotação podem coexistir enquantoFetchType.EAGER
não podem, mesmo nas situações em que tal coexistência é legítima.Por exemplo, um
Order
pode ter uma lista deOrderGroup
(uma curta) e uma lista dePromotions
(também curta).@LazyCollection(LazyCollectionOption.FALSE)
pode ser usado em ambos sem causarLazyInitializationException
nenhumMultipleBagFetchException
.No meu caso
@Fetch
, resolveu meu problema de,MultipleBacFetchException
mas depois causaLazyInitializationException
, ono Session
erro infame .fonte
Você pode usar uma nova anotação para resolver isso:
De fato, o valor padrão da busca também é FetchType.LAZY.
fonte