Hibernate rzuca MultipleBagFetchException - nie może jednocześnie pobrać wielu worków
Hibernate wyrzuca ten wyjątek podczas tworzenia SessionFactory:
Org.hibernacja.ładowacz.MultipleBagFetchException: nie można pobrać jednocześnie wielu toreb]}
To mój testowy przypadek:
Rodzic.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;
}
Dziecko.java
@Entity
public Child {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Parent parent;
}
A co z tym problemem? Co mogę zrobić?
EDIT
OK, problem jest taki, że inny" rodzic " jest wewnątrz mój rodzic, moje prawdziwe zachowanie jest takie:
Rodzic.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;
}
Kolejny rodzic.java
@Entity
public AnotherParent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<AnotherChild> anotherChildren;
}
Hibernate nie lubi dwóch kolekcji z FetchType.EAGER
, ale to chyba błąd, nie robię niezwykłych rzeczy...
Usunięcie FetchType.EAGER
z Parent
lub AnotherParent
rozwiązuje problem, ale potrzebuję go, więc prawdziwym rozwiązaniem jest użycie @LazyCollection(LazyCollectionOption.FALSE)
zamiast FetchType
(dzięki Bozho za rozwiązanie).
16 answers
Myślę, że nowsza wersja hibernate (obsługująca JPA 2.0) powinna sobie z tym poradzić. Ale poza tym możesz to obejść, adnotując pola kolekcji za pomocą:
@LazyCollection(LazyCollectionOption.FALSE)
Pamiętaj, aby usunąć atrybut fetchType
z adnotacji @*ToMany
.
Ale zauważ, że w większości przypadków {[3] } jest bardziej odpowiedni niż List<Child>
, więc jeśli naprawdę nie potrzebujesz List
- idź do Set
Ale przypomnij, że używając zbiorów nie wyeliminujesz iloczynu kartezjańskiego Jak to opisał Vlad Mihalcea w swojej odpowiedzi !
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-08 12:14:44
Po prostu zmień typ z List
Na typ Set
.
Ale przypomnij, że nie wyeliminujesz iloczynu kartezjańskiego opisanego przez Vlada Mihalcea w jego odpowiedzi!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-08 12:14:06
Dodaj adnotację @ Fetch specyficzną dla Hibernate do kodu:
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
Powinno to rozwiązać problem związany z błędem Hibernate HHH-1718
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-18 14:39:31
Biorąc pod uwagę, że mamy następujące byty:
I, chcesz pobrać element nadrzędny Post
wraz ze wszystkimi kolekcjami comments
i tags
.
Jeśli używasz więcej niż jednej dyrektywy JOIN FETCH
:
List<Post> posts = entityManager
.createQuery(
"select p " +
"from Post p " +
"left join fetch p.comments " +
"left join fetch p.tags " +
"where p.id between :minId and :maxId", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.getResultList();
Hibernate rzuci niesławny:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags [
com.vladmihalcea.book.hpjp.hibernate.fetching.Post.comments,
com.vladmihalcea.book.hpjp.hibernate.fetching.Post.tags
]
Hibernate nie pozwala na pobieranie więcej niż jednego worka, ponieważ generowałoby to iloczyn kartezjański.
Najgorsze "rozwiązanie"
Teraz znajdziesz wiele odpowiedzi, posty na blogu, filmy lub inne zasoby mówiące o używaniu Set
zamiast List
do swoich kolekcji.
To straszna Rada. Nie rób tego!
Użycie Sets
zamiast Lists
sprawi, że MultipleBagFetchException
zniknie, ale produkt kartezjański nadal tam będzie, co jest jeszcze gorsze, ponieważ problem z wydajnością dowiesz się długo po zastosowaniu tej "poprawki".
Właściwe rozwiązanie
Możesz wykonać następujące czynności trick:
List<Post> posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.id between :minId and :maxId ", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.tags t " +
"where p in :posts ", Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
W pierwszym zapytaniu JPQL,
distinct
nie przechodzi do instrukcji SQL. Dlatego ustawiamy podpowiedźPASS_DISTINCT_THROUGH
JPA query nafalse
.DISTINCT ma dwa znaczenia w JPQL, a tutaj potrzebujemy go do deduplikacji odwołań do obiektów Java zwracanych przez
getResultList
Po stronie Javy, a nie po stronie SQL.
Tak długo, jak pobierasz co najwyżej jedną kolekcję używając JOIN FETCH
, wszystko będzie dobrze.
Używając wielu zapytań, unikniesz iloczynu kartezjańskiego, ponieważ każdy inna kolekcja, ale pierwsza jest pobierana za pomocą zapytania wtórnego.
There ' s more you could do
Jeśli używasz FetchType.EAGER
strategii w czasie mapowania dla @OneToMany
lub @ManyToMany
asocjacji, możesz łatwo skończyć z MultipleBagFetchException
.
Lepiej jest przełączyć się z FetchType.EAGER
na Fetchype.LAZY
, ponieważ eager fetching to okropny pomysł, który może prowadzić do krytycznych problemów z wydajnością aplikacji.
Podsumowanie
Unikaj FetchType.EAGER
i nie przełącz się z List
na Set
tylko dlatego, że zrobi to Hibernate ukryje MultipleBagFetchException
pod dywan. Przynieś tylko jedną kolekcję na raz, a będzie dobrze.
Tak długo, jak robisz to z taką samą liczbą zapytań, jak masz kolekcje do zainicjowania, jesteś w porządku. Po prostu nie inicjuj kolekcji w pętli, ponieważ spowoduje to N + 1 problemów z zapytaniami, które również są złe dla wydajności.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-08 21:05:51
Po wypróbowaniu każdej opcji opisanej w tym postach i innych, doszedłem do wniosku, że poprawka jest następująca.
In every xtomany place @XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
i pośrednio po
@Fetch(value = FetchMode.SUBSELECT)
To zadziałało dla mnie
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-27 16:46:14
Aby to naprawić, po prostu weź Set
w miejsce List
dla zagnieżdżonego obiektu.
@OneToMany
Set<Your_object> objectList;
I nie zapomnij użyć fetch=FetchType.EAGER
Jest jeszcze jedna koncepcja CollectionId
w Hibernate, jeśli chcesz trzymać się tylko listy.
Ale przypomnij, że nie wyeliminujesz iloczynu kartezjańskiego opisanego przez Vlada Mihalcea w jego odpowiedzi !
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-08 12:13:50
Znalazłem dobry wpis na blogu o zachowaniu Hibernate w tego typu mapowaniach obiektów: http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-11 15:52:34
Możesz zachować listy Bootha w JPA i dodać do co najmniej jednej z nich adnotację JPA @ OrderColumn (Oczywiście z nazwą pola do zamówienia). Nie ma potrzeby stosowania konkretnych adnotacji hibernate. Należy jednak pamiętać, że może tworzyć puste elementy na liście, jeśli wybrane pole nie ma wartości zaczynających się od 0
[...]
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@OrderColumn(name="orderIndex")
private List<Child> children;
[...]
W przypadku dzieci należy dodać pole orderIndex
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-25 09:29:18
Próbowaliśmy ustawić zamiast List i jest to koszmar: po dodaniu dwóch nowych obiektów, równa się() i hashCode () nie rozróżnia obu z nich ! Bo nie mają dokumentów.
Typowe narzędzia takie jak Eclipse generują taki kod z tabel bazy danych:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
Możesz również przeczytać ten artykuł, który wyjaśnia, jak pokręcone jest JPA/Hibernate. Po przeczytaniu tego, myślę, że to ostatni raz, Kiedy używam jakiegokolwiek ORM w moim życie.
Spotkałam również facetów z Domain Driven Design, którzy w zasadzie mówią, że ORM to straszna rzecz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-06 19:13:21
Jeśli masz zbyt złożone obiekty z kolekcją saveral nie może być dobrym pomysłem, aby mieć wszystkie z nich z EAGER fetchType, lepiej Użyj LAZY i kiedy naprawdę trzeba załadować Kolekcje użyj: Hibernate.initialize(parent.child)
aby pobrać dane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-15 13:38:21
Dla mnie problemem było zagnieżdżanie EAGER pobrań.
Jednym z rozwiązań jest ustawienie zagnieżdżonych pól na LAZY i użycie Hibernate.initialize () ładuje zagnieżdżone pola:
x = session.get(ClassName.class, id);
Hibernate.initialize(x.getNestedField());
U mnie stało się tak, gdy miałem wiele kolekcji z FetchType.EAGER, like this:
@ManyToMany(fetch = FetchType.EAGER, targetEntity = className.class)
@JoinColumn(name = "myClass_id")
@JsonView(SerializationView.Summary.class)
private Collection<Model> ModelObjects;
Dodatkowo zbiory łączyły się w tej samej kolumnie.
Aby rozwiązać ten problem, zmieniłem jedną z kolekcji na FetchType.Leniwy, ponieważ był w porządku dla mojego przypadku użycia.
Goodluck! ~JWarning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-11 21:14:35
Komentowanie zarówno Fetch
jak i LazyCollection
czasami pomaga w prowadzeniu projektu.
@Fetch(FetchMode.JOIN)
@LazyCollection(LazyCollectionOption.FALSE)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-27 05:47:43
Jedną dobrą rzeczą w @LazyCollection(LazyCollectionOption.FALSE)
jest to, że kilka pól z tą adnotacją może współistnieć, podczas gdy FetchType.EAGER
nie może, nawet w sytuacjach, gdy takie współistnienie jest legalne.
Na przykład, {[2] } może mieć listę OrderGroup
(krótką), a także listę Promotions
(również krótką). @LazyCollection(LazyCollectionOption.FALSE)
może być stosowany na obu bez powodowania LazyInitializationException
ani MultipleBagFetchException
.
W moim przypadku @Fetch
rozwiązał mój problem MultipleBacFetchException
, ale potem powoduje LazyInitializationException
, niesławny błąd no Session
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-14 15:43:40
Rozwiązałem przez adnotację:
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-29 10:12:42
Możesz użyć nowej adnotacji, aby rozwiązać ten problem:
@XXXToXXX(targetEntity = XXXX.class, fetch = FetchType.LAZY)
W rzeczywistości domyślną wartością fetch jest FetchType.Leniwy też.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-06 03:26:41