हाइबरनेट एकाधिकबागफैक्स्ट अपवाद फेंकता है - एक साथ कई बैग नहीं ला सकता है


471

Hibernate सत्र अपवाद निर्माण के दौरान इस अपवाद को फेंकता है:

org.hibernate.loader.MultipleBagFetchException: एक साथ कई बैग नहीं ला सकते

यह मेरी परीक्षा का मामला है:

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;

}

इस समस्या के बारे में कैसे? मैं क्या कर सकता हूँ?


संपादित करें

ठीक है, मेरे पास समस्या यह है कि एक और "माता-पिता" संस्था मेरे माता-पिता के अंदर है, मेरा वास्तविक व्यवहार यह है:

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;

}

हाइबरनेट को दो संग्रह पसंद नहीं हैं FetchType.EAGER, लेकिन यह एक बग लगता है, मैं असामान्य चीजें नहीं कर रहा हूं ...

समस्या FetchType.EAGERसे निकालना Parentया AnotherParentहल करना, लेकिन मुझे इसकी आवश्यकता है, इसलिए वास्तविक समाधान इसका उपयोग @LazyCollection(LazyCollectionOption.FALSE)करने के लिए है FetchType( समाधान के लिए बोझो के लिए धन्यवाद )।


मैं पूछता हूँ, कि आप कौन सी एसक्यूएल क्वेरी उत्पन्न करने की उम्मीद कर रहे हैं जो एक साथ दो अलग-अलग संग्रह प्राप्त करेगा? एसक्यूएल के प्रकार जो इन को प्राप्त करने में सक्षम होंगे, उन्हें या तो कार्टेशियन जॉइन करना होगा (संभावित रूप से अत्यधिक अक्षम) या असंतुष्ट कॉलम (भी बदसूरत) का एक संघ। संभवतः एक साफ और कुशल तरीके से SQL में इसे प्राप्त करने में असमर्थता एपीआई डिजाइन को प्रभावित करती है।
थॉमस डब्ल्यू

@ThomasW ये एक sql क्वेरी है, जो इसे उत्पन्न करनी चाहिए:select * from master; select * from child1 where master_id = :master_id; select * from child2 where master_id = :master_id
nurettin

1
यदि आप एक से अधिक के लिए एक से अधिक परिभाषित के List<child>साथ एक simillar त्रुटि प्राप्त कर सकते हैंfetchType List<clield>
Big Zed

जवाबों:


555

मुझे लगता है कि हाइबरनेट के एक नए संस्करण (जेपीए 2.0 का समर्थन) को इसे संभालना चाहिए। लेकिन अन्यथा आप इसके साथ संग्रह क्षेत्रों की व्याख्या करके इसे काम कर सकते हैं:

@LazyCollection(LazyCollectionOption.FALSE)

एनोटेशन fetchTypeसे विशेषता को हटाने के लिए याद रखें @*ToMany

लेकिन ध्यान दें कि ज्यादातर मामलों में एक Set<Child>से अधिक उपयुक्त है List<Child>, इसलिए जब तक आप वास्तव में एक की जरूरत है List- के लिए जाओSet

लेकिन याद रखें कि सेट के उपयोग से आप अपने जवाब में व्लाद मिहेल्सिया द्वारा वर्णित कार्टेलियन उत्पाद को समाप्त नहीं करेंगे !


4
अजीब है, यह मेरे लिए काम किया है। क्या आप से हटा fetchTypeदिया @*ToMany?
17

101
समस्या यह है कि जेपीए एनोटेशन को 2 से अधिक उत्सुकता से भरे संग्रह की अनुमति नहीं देने के लिए तैयार किया गया है। लेकिन हाइबरनेट-विशिष्ट एनोटेशन इसकी अनुमति देते हैं।
Bozho

14
1 से अधिक EAGER की आवश्यकता पूरी तरह से यथार्थवादी लगती है। क्या यह सीमा सिर्फ जेपीए की निगरानी है? मल्गर ईएजीईआरएस होने पर मुझे किन चिंताओं पर ध्यान देना चाहिए?
AR3Y35

6
बात यह है, हाइबरनेट एक संग्रह के साथ दो संग्रह नहीं ला सकता है। इसलिए जब आप मूल इकाई के लिए क्वेरी करते हैं, तो इसे प्रति परिणाम 2 अतिरिक्त प्रश्नों की आवश्यकता होगी, जो कि सामान्य रूप से कुछ ऐसा है जो आप नहीं चाहते हैं।
बोहो

7
यह इस मुद्दे को हल करने के लिए के रूप में एक स्पष्टीकरण होना बहुत अच्छा होगा।
वेबनेट

290

बस Listटाइप से टाइप बदलें Set

लेकिन याद दिलाएं कि आप व्लाद मिहेल्सिया द्वारा उनके जवाब में वर्णित कार्टेलियन प्रोडक्ट को खत्म नहीं करेंगे !


42
एक सूची और एक सेट एक ही चीज़ नहीं हैं: एक सेट आदेश को संरक्षित नहीं करता है
Matteo

17
लिंक्डहाशसेट ऑर्डर को संरक्षित करता है
उदाहरण के लिए

15
यह एक महत्वपूर्ण अंतर है और, जब आप इसके बारे में सोचते हैं, पूरी तरह से सही है। डीबी में एक विदेशी कुंजी द्वारा कार्यान्वित ठेठ कई-टू-वन वास्तव में एक सूची नहीं है, यह एक सेट है क्योंकि ऑर्डर संरक्षित नहीं है। तो सेट वास्तव में अधिक उपयुक्त है। मुझे लगता है कि हाइबरनेट में फर्क पड़ता है, हालांकि मुझे नहीं पता कि क्यों।
मूर्ख

3
मैं एक ही साथ कई बैग नहीं ला सकता था, लेकिन एनोटेशन के कारण नहीं। मेरे मामले में, मैं दोनों के साथ बाएं जुड़ाव और असहमति कर रहा था *ToManySetमेरी समस्या को हल करने के लिए भी प्रकार बदलना । उत्कृष्ट और साफ समाधान। यह आधिकारिक उत्तर होना चाहिए।
एल। होलांडा

20
मुझे उत्तर पसंद आया, लेकिन मिलियन डॉलर का प्रश्न है: क्यों? सेट के साथ अपवाद क्यों नहीं दिखा? धन्यवाद
Hinotori

140

अपने कोड में एक हाइबरनेट-विशिष्ट @ फ़ोट एनोटेशन जोड़ें:

@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;

यह हाइबरनेट बग HHH-1718 से संबंधित समस्या को ठीक करना चाहिए


5
@DaveRlz क्यों subSelect इस समस्या को हल करता है। मैंने आपके समाधान और इसके काम करने की कोशिश की, लेकिन न जाने कैसे इस समस्या को हल करके इसका उपयोग किया गया?
हकुनामाता

यह सबसे अच्छा जवाब है जब तक कि Setवास्तव में कोई मतलब नहीं है। प्रश्नों में परिणामों OneToManyका उपयोग करके एकल संबंध रखना, जहां प्रश्नों में परिणाम का उपयोग करना । इसके अलावा, स्वीकृत उत्तर ( ) में एनोटेशन का उपयोग करने से और भी अधिक प्रश्नों का निष्पादन होता है। Set1+<# relationships>FetchMode.SUBSELECT1+1LazyCollectionOption.FALSE
मासिक धर्म २१'१५

1
FetchType.EAGER इसके लिए एक उचित समाधान नहीं है। Hibernate Fetch Profiles के साथ आगे बढ़ने की जरूरत है और इसे हल करने की जरूरत है
Milinda Bandara

2
दो अन्य शीर्ष जवाबों से मेरी समस्या हल नहीं हुई। यह एक किया। धन्यवाद!
ब्लाइंडवर्क्स

3
क्या किसी को पता है कि SUBSELECT इसे ठीक क्यों करता है, लेकिन JOIN नहीं करता है?
मासूमियत

41

यह प्रश्न स्टैकऑवरफ्लो या हाइबरनेट फोरम दोनों पर आवर्ती विषय रहा है, इसलिए मैंने उत्तर को एक लेख में बदलने का फैसला किया ।

निम्नलिखित संस्थाओं को ध्यान में रखते हुए:

यहाँ छवि विवरण दर्ज करें

और, आप Postसभी commentsऔर tagsसंग्रह के साथ कुछ मूल संस्थाओं को लाना चाहते हैं ।

यदि आप एक से अधिक 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();

हाइबरनेट कुख्यात को फेंक देगा:

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
]

हाइबरनेट एक से अधिक बैग लाने की अनुमति नहीं देता है क्योंकि यह कार्टेशियन उत्पाद उत्पन्न करेगा ।

सबसे खराब "समाधान"

अब, आपको अपने संग्रह के लिए उपयोग करने के Setबजाय बहुत सारे उत्तर, ब्लॉग पोस्ट, वीडियो या अन्य संसाधन मिलेंगे List

वह भयानक सलाह है। ऐसा मत करो!

के Setsबजाय का उपयोग कर दूर जाना Listsहोगा MultipleBagFetchException, लेकिन कार्टेशियन उत्पाद अभी भी वहाँ है, जो वास्तव में भी बदतर है, के रूप में आप इस "फिक्स" लागू करने के लंबे समय बाद प्रदर्शन के मुद्दे का पता चल जाएगा।

उचित समाधान

आप निम्न चाल कर सकते हैं:

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();

पहली JPQL क्वेरी में, distinctSQL कथन पर नहीं जाते हैं। इसलिए हम PASS_DISTINCT_THROUGHJPA क्वेरी संकेत को सेट करते हैं false

DISTINCT के JPQL में दो अर्थ हैं, और यहाँ, हमें यह आवश्यक है getResultListकि जावा पक्ष द्वारा लौटाए गए जावा ऑब्जेक्ट संदर्भों को SQL साइड नहीं, को काट दें। की जाँच करें इस लेख अधिक जानकारी के लिए।

जब तक आप उपयोग करते हुए अधिकांश एक संग्रह में लाते हैं JOIN FETCH, तब तक आप ठीक रहेंगे।

कई प्रश्नों का उपयोग करके, आप किसी अन्य संग्रह के बाद से कार्टेसियन उत्पाद से बचेंगे लेकिन पहले एक माध्यमिक क्वेरी का उपयोग करके लाया जाता है।

और भी बहुत कुछ है जो आप कर सकते हैं

यदि आप FetchType.EAGERरणनीति के लिए @OneToManyया @ManyToManyसंघों के मानचित्रण के समय का उपयोग कर रहे हैं , तो आप आसानी से समाप्त कर सकते हैं MultipleBagFetchException

आप से स्विच से बेहतर कर रहे हैं FetchType.EAGERकरने के लिए Fetchype.LAZYके बाद से उत्सुक प्राप्त कर रहा है एक है भयानक विचार है कि महत्वपूर्ण आवेदन प्रदर्शन के मुद्दों को जन्म दे सकता

निष्कर्ष

से बचें FetchType.EAGERऔर से स्विच नहीं है Listकरने के लिए Setसिर्फ इसलिए कि ऐसा करने से हाइबरनेट कर देगा छिपाने MultipleBagFetchExceptionकालीन के नीचे। एक बार में केवल एक संग्रह प्राप्त करें, और आप ठीक हो जाएंगे।

जब तक आप इसे आरंभ करने के लिए संग्रह के समान प्रश्नों की संख्या के साथ करते हैं, तब तक आप ठीक हैं। बस लूप में संग्रह को इनिशियलाइज़ न करें, क्योंकि यह N + 1 क्वेरी मुद्दों को ट्रिगर करेगा , जो प्रदर्शन के लिए भी खराब हैं।


साझा ज्ञान के लिए धन्यवाद। हालांकि, DISTINCTइस समाधान में प्रदर्शन हत्यारा है। क्या छुटकारा पाने का कोई तरीका है distinct? ( Set<...>बजाय लौटने की कोशिश की , ज्यादा मदद नहीं की)
लियोनिद डैशको

1
DISTINCT SQL स्टेटमेंट पर नहीं जाता है। इसीलिए PASS_DISTINCT_THROUGHसेट किया गया है false। DISTINCT के JPQL में 2 अर्थ हैं, और यहाँ, हमें इसकी ओर जावा पक्ष में समर्पण करने की आवश्यकता है, न कि SQL की तरफ। की जाँच करें इस लेख अधिक जानकारी के लिए।
व्लाद मिहालसी

व्लाद, मदद के लिए धन्यवाद मुझे यह वास्तव में उपयोगी लगता है। हालाँकि, इस मुद्दे से संबंधित था hibernate.jdbc.fetch_size(अंततः मैंने इसे 350 पर सेट किया)। संयोग से, क्या आप जानते हैं कि कैसे नेस्टेड संबंधों को अनुकूलित करना है? उदाहरण के तौर पर1 -> एंटिटी 2 -> एंटिटी 3.1, यूनिट 3.2 (जहां एंट्री 3.1 / 3.2 @ ओनोटायनी रिलेशनशिप हैं)
लियोनिद डैशको

1
@LeonidDashko डेटा प्राप्त करने से संबंधित कई युक्तियों के लिए मेरी उच्च-प्रदर्शन जावा दृढ़ता पुस्तक में फ़ेचिंग अध्याय देखें ।
व्लाद मिहेल्सिया

1
नहीं तुम नहीं कर सकते। SQL के संदर्भ में इसके बारे में सोचें। आप कार्टेशियन प्रोडक्ट को जनरेट किए बिना कई एक से कई एसोसिएशन नहीं जोड़ सकते।
व्लाद मिहालसी

31

इस पोस्ट और अन्य में वर्णित हर एक विकल्प के साथ प्रयास करने के बाद, मैं इस नतीजे पर पहुंचा कि फिक्स एक अनुसरण है।

हर XToMany जगह में @ XXXToMany(mappedBy="parent", fetch=FetchType.EAGER) और बाद में

@Fetch(value = FetchMode.SUBSELECT)

इसने मेरे लिए काम किया


5
जोड़ना @Fetch(value = FetchMode.SUBSELECT)पर्याप्त था
20:26 पर user2601995

1
यह एक हाइबरनेट एकमात्र समाधान है। यदि आप साझा JPA लाइब्रेरी का उपयोग कर रहे हैं तो क्या होगा?
माइकल

3
मुझे यकीन है कि आप का मतलब नहीं था, लेकिन डेवर्लज़ ने 3 साल पहले ही एक ही बात लिखी थी
phil294

21

इसे ठीक करने के लिए बस अपने नेस्टेड ऑब्जेक्ट Setके Listलिए जगह लें।

@OneToMany
Set<Your_object> objectList;

और उपयोग करने के लिए मत भूलना fetch=FetchType.EAGER

यह काम करेगा।

CollectionIdहाइबरनेट में एक और अवधारणा है यदि आप केवल सूची के साथ रहना चाहते हैं।

लेकिन याद दिलाएं कि आप व्लाद मिहेल्सिया द्वारा उनके जवाब में वर्णित कार्टेलियन प्रोडक्ट को खत्म नहीं करेंगे !


11

मुझे इस तरह के ऑब्जेक्ट मैपिंग में हाइबरनेट के व्यवहार के बारे में एक अच्छा ब्लॉग पोस्ट मिला: http://blog.eyallupu.com/2010/06/hibernate-exception-simultantly.html


4
ढेर अतिप्रवाह में आपका स्वागत है। हालांकि, लिंक-केवल उत्तर आमतौर पर हतोत्साहित किए जाते हैं। Faq, और विशेष रूप से देखें: stackoverflow.com/faq#deletion
फेमटो रीगन

6

आप बूथ EAGER सूचियों को JPA में रख सकते हैं और उनमें से कम से कम एक को JPA एनोटेशन @OrderColumn (आदेश देने के लिए किसी फ़ील्ड का नाम) के साथ जोड़ सकते हैं । विशिष्ट हाइबरनेट एनोटेशन की कोई आवश्यकता नहीं है। लेकिन ध्यान रखें कि यह सूची में खाली तत्व बना सकता है यदि चुने हुए क्षेत्र में 0 से शुरू होने वाले मान नहीं हैं

 [...]
 @OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
 @OrderColumn(name="orderIndex")
 private List<Child> children;
 [...]

बच्चों में तब आपको ऑर्डरइंडेक्स फ़ील्ड जोड़ना चाहिए


2

हमने सूची के बजाय सेट की कोशिश की और यह एक बुरा सपना है: जब आप दो नई वस्तुओं को जोड़ते हैं, तो बराबर () और हैशकोड () दोनों को अलग करने में विफल होते हैं! क्योंकि उनके पास कोई आईडी नहीं है।

ग्रहण जैसे विशिष्ट उपकरण डेटाबेस तालिकाओं से उस तरह का कोड उत्पन्न करते हैं:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    return result;
}

आप इस लेख को भी पढ़ सकते हैं जो ठीक से बताता है कि जेपीए / हाइबरनेट कितना गड़बड़ है। इसे पढ़ने के बाद, मुझे लगता है कि यह आखिरी बार है जब मैं अपने जीवन में किसी भी ओआरएम का उपयोग करता हूं।

मैंने डोमेन ड्रिवेन डिज़ाइन वालों से भी सामना किया है जो मूल रूप से कहते हैं कि ओआरएम एक भयानक चीज है।


1

जब आपके पास सेवरल संग्रह के साथ बहुत अधिक जटिल वस्तुएं होती हैं, तो उन सभी को ईएजीआर के साथ लाने के लिए अच्छा विचार नहीं हो सकता है, बेहतर उपयोग LAZY और जब आपको वास्तव में संग्रह उपयोग को लोड करने की आवश्यकता होती है: Hibernate.initialize(parent.child)डेटा प्राप्त करने के लिए।


0

मेरे लिए, समस्या NAGER भ्रूणों के साथ हो रही थी ।

एक समाधान यह है कि नेस्टेड फ़ील्ड्स को LAZY में सेट किया जाए और नेस्टेड फ़ील्ड (ओं) को लोड करने के लिए Hibernate.initialize () का उपयोग करें:

x = session.get(ClassName.class, id);
Hibernate.initialize(x.getNestedField());

0

मेरे अंत में, यह तब हुआ जब मेरे पास FetchType.EAGER के साथ कई संग्रह थे, जैसे:

@ManyToMany(fetch = FetchType.EAGER, targetEntity = className.class)
@JoinColumn(name = "myClass_id")
@JsonView(SerializationView.Summary.class)
private Collection<Model> ModelObjects;

इसके अतिरिक्त, संग्रह एक ही कॉलम में शामिल हो रहे थे।

इस समस्या को हल करने के लिए, मैंने संग्रह में से एक को FetchType.LAZY में बदल दिया क्योंकि यह मेरे उपयोग-मामले के लिए ठीक था।

सौभाग्य! ~ जम्मू


0

दोनों पर टिप्पणी करना Fetchऔर LazyCollectionकभी-कभी प्रोजेक्ट चलाने में मदद करता है।

@Fetch(FetchMode.JOIN)
@LazyCollection(LazyCollectionOption.FALSE)

0

इसके बारे @LazyCollection(LazyCollectionOption.FALSE)में एक अच्छी बात यह है कि इस एनोटेशन के साथ कई क्षेत्र FetchType.EAGERसह-अस्तित्व में रह सकते हैं, यहां तक ​​कि उन स्थितियों में भी जहां ऐसा सह-अस्तित्व वैध है।

उदाहरण के लिए, Orderएक सूची OrderGroup(एक छोटी) के साथ-साथ (एक छोटी भी) सूची हो सकती है Promotions@LazyCollection(LazyCollectionOption.FALSE)दोनों पर इस्तेमाल किया जा सकता है LazyInitializationExceptionऔर न ही कारण के बिना MultipleBagFetchException

मेरे मामले में @Fetchमेरी समस्या का समाधान किया, MultipleBacFetchExceptionलेकिन फिर LazyInitializationException, कुख्यात no Sessionत्रुटि।


-5

इसे हल करने के लिए आप एक नए एनोटेशन का उपयोग कर सकते हैं:

@XXXToXXX(targetEntity = XXXX.class, fetch = FetchType.LAZY)

वास्तव में, भ्रूण का डिफ़ॉल्ट मान FetchType.LAZY है।


5
JPA3.0 मौजूद नहीं है।
holmis83
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.