Hibernate मानदंड कई बार FetchType.EAGER के साथ बच्चों को देता है


115

मेरे पास एक Orderवर्ग है जिसकी एक सूची है OrderTransactionsऔर मैंने इसे एक-से-कई हाइबरनेट मैपिंग के साथ मैप किया है:

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

इन Orderक्षेत्रों में एक फ़ील्ड भी होता है orderStatus, जिसे निम्नलिखित मानदंड के साथ फ़िल्टर करने के लिए उपयोग किया जाता है:

public List<Order> getOrderForProduct(OrderFilter orderFilter) {
    Criteria criteria = getHibernateSession()
            .createCriteria(Order.class)
            .add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));
    return criteria.list();
}

यह काम करता है और परिणाम अपेक्षित है।

अब यहाँ मेरा सवाल है : क्यों, जब मैं स्पष्ट रूप से लाने के लिए प्रकार सेट करता हूं EAGER, तो क्या Orderपरिणामी सूची में कई बार एस दिखाई देते हैं?

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

नई सेटिंग के साथ उसी परिणाम तक पहुंचने के लिए मुझे अपना मानदंड कोड कैसे बदलना होगा?


1
क्या आपने यह दिखाने की कोशिश की है कि नीचे क्या चल रहा है?
मिरको एन।

कृपया ऑर्डरट्रांसैक्शन और ऑर्डर क्लासेस कोड भी जोड़ें। \ _
एरन मेदान

जवाबों:


115

यह वास्तव में अपेक्षित व्यवहार है अगर मैंने आपके कॉन्फ़िगरेशन को सही ढंग से समझा।

आपको Orderकिसी भी परिणाम में एक ही उदाहरण मिलता है , लेकिन अब चूंकि आप इसमें शामिल हो रहे हैं OrderTransaction, इसलिए इसे उसी तरह के परिणामों को वापस करना होगा जो एक नियमित एसक्यूएल जॉइन करेगा।

तो वास्तव में यह कई बार माफी चाहिए । यह लेखक (गेविन किंग) ने खुद यहां बहुत अच्छी तरह से समझाया है : यह दोनों बताते हैं कि क्यों, और कैसे अभी भी अलग परिणाम प्राप्त करने के लिए


इसके अलावा हाइबरनेट में उल्लेख पूछे जाने वाले प्रश्न :

हाइबरनेट एक क्वेरी के लिए अलग-अलग परिणाम नहीं देता है जिसमें बाहरी सम्मिलित करने के लिए एक संग्रह के लिए सक्षम होना चाहिए (भले ही मैं अलग कीवर्ड का उपयोग करूं)? सबसे पहले, आपको एसक्यूएल को समझने की जरूरत है और कैसे ओआरएस जॉइन एसक्यूएल में काम करता है। यदि आप SQL में बाहरी जॉइन को पूरी तरह से नहीं समझते और समझते हैं, तो इस FAQ आइटम को पढ़ना जारी न रखें, लेकिन SQL मैनुअल या ट्यूटोरियल से सलाह लें। अन्यथा आप निम्नलिखित स्पष्टीकरण को नहीं समझेंगे और आप हाइबरनेट फोरम पर इस व्यवहार के बारे में शिकायत करेंगे।

समान आदेश ऑब्जेक्ट के डुप्लिकेट संदर्भों को लौटाने वाले विशिष्ट उदाहरण:

List result = session.createCriteria(Order.class)
                    .setFetchMode("lineItems", FetchMode.JOIN)
                    .list();

<class name="Order">
    ...
    <set name="lineItems" fetch="join">

List result = session.createCriteria(Order.class)
                       .list();
List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();

ये सभी उदाहरण एक ही SQL स्टेटमेंट का उत्पादन करते हैं:

SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID

जानना चाहते हैं कि डुप्लिकेट क्यों हैं? एसक्यूएल परिणाम को देखें, हाइबरनेट इन डुप्लिकेट को बाहरी सम्मिलित परिणाम के बाईं ओर छिपाता नहीं है, लेकिन ड्राइविंग तालिका के सभी डुप्लिकेट लौटाता है। यदि आपके पास डेटाबेस में 5 ऑर्डर हैं, और प्रत्येक ऑर्डर में 3 लाइन आइटम हैं, तो परिणाम 15 पंक्तियों का होगा। इन प्रश्नों की जावा परिणाम सूची में 15 तत्व होंगे, सभी प्रकार के ऑर्डर। हाइबरनेट द्वारा केवल 5 ऑर्डर इंस्टेंसेस बनाए जाएंगे, लेकिन SQL परिणाम के डुप्लिकेट को इन 5 इंस्टेंस के डुप्लिकेट संदर्भ के रूप में संरक्षित किया जाता है। यदि आप इस अंतिम वाक्य को नहीं समझते हैं, तो आपको जावा पर पढ़ने की जरूरत है और जावा हीप पर एक उदाहरण और इस तरह के उदाहरण के बीच का अंतर।

(बाईं ओर क्यों सम्मिलित हों? यदि आपके पास कोई पंक्ति आइटम नहीं है, तो परिणाम सेट में 16 पंक्तियाँ होंगी जिसमें NULL दाईं ओर भरता है, जहाँ पंक्ति वस्तु डेटा अन्य आदेश के लिए है। आप चाहे तो भी आदेश दे सकते हैं। उनके पास लाइन आइटम नहीं हैं, ठीक है (यदि नहीं, तो अपने एचक्यूएल में एक आंतरिक जुड़ाव का उपयोग करें)।

हाइबरनेट डिफ़ॉल्ट रूप से इन डुप्लिकेट संदर्भों को फ़िल्टर नहीं करता है। कुछ लोग (आप नहीं) वास्तव में यही चाहते हैं। आप उन्हें कैसे फ़िल्टर कर सकते हैं?

ऐशे ही:

Collection result = new LinkedHashSet( session.create*(...).list() );

121
यहां तक ​​कि अगर आप निम्नलिखित स्पष्टीकरण को समझते हैं, तो आप हाइबरनेट फोरम पर इस व्यवहार के बारे में अच्छी तरह से शिकायत कर सकते हैं, क्योंकि यह बेवकूफी भरा व्यवहार है!
टॉम एंडरसन

17
काफी सही टॉम, आईडी गेविन किंग्स अभिमानी रवैया के बारे में भूल गया। वह यह भी कहते हैं 'हाइबरनेट डिफ़ॉल्ट रूप से इन डुप्लिकेट संदर्भों को फ़िल्टर नहीं करता है। कुछ लोग (आप नहीं) वास्तव में यह चाहते हैं कि जब लोग वास्तव में चींटी हों तो यह 'आईडी' रूचि ले।
पॉल टेलर

16
@TomAnderson हाँ बिल्कुल। किसी को उन डुप्लिकेट की आवश्यकता क्यों होगी? मैं शुद्ध जिज्ञासा से बाहर पूछ रहा हूं, क्योंकि मुझे कोई पता नहीं है ... आप खुद को डुप्लिकेट बना सकते हैं, उनमें से कई जो आप चाहते हैं .. ;-)
पैरोबे

13
आह। यह वास्तव में हाइबरनेट दोष है, IMHO। मैं अपने प्रश्नों को अनुकूलित करना चाहता हूं, इसलिए मैं अपनी मैपिंग फ़ाइल में "सेलेक्ट" से "जॉइन" करता हूं। अचानक मेरा कोड सभी जगह बन गया। फिर मैं परिणाम ट्रांसफार्मर और व्हाट्सएप को जोड़कर अपने सभी डीएओ को इधर-उधर चलाता हूं। उपयोगकर्ता का अनुभव == बहुत नकारात्मक। मैं समझता हूं कि कुछ लोगों को विचित्र कारणों से डुप्लिकेट होना बहुत पसंद है, लेकिन मैं क्यों नहीं कह सकता "इन वस्तुओं को प्राप्त करें FASTER लेकिन मुझे" = justworkplease "निर्दिष्ट करके डुप्लिकेट के साथ बग नहीं"?
रोमन ज़ेनका

@Eran: मैं इसी तरह की समस्या का सामना कर रहा हूं। मुझे डुप्लिकेट पैरेंट ऑब्जेक्ट्स नहीं मिल रहे हैं, लेकिन मुझे प्रत्येक पेरेंट ऑब्जेक्ट में बच्चे मिल रहे हैं जो कि कई बार दोहराया जाता है क्योंकि प्रतिक्रिया में पैरेंट ऑब्जेक्ट्स की संख्या होती है। किसी भी विचार क्यों इस समस्या?
मन्त्री

93

एरन ने जो उल्लेख किया है, उसके अलावा, व्यवहार को प्राप्त करने का एक और तरीका, परिणाम ट्रांसफार्मर सेट करना है:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

8
यह ज्यादातर मामलों के लिए काम करेगा .... सिवाय जब आप 2 संग्रह / संघों को लाने के लिए मानदंड का उपयोग करने का प्रयास करते हैं।
जेम्स डीआर

42

प्रयत्न

@Fetch (FetchMode.SELECT) 

उदाहरण के लिए

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch (FetchMode.SELECT)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;

}


11
FetchMode.SELECT हाइबरनेट द्वारा निकाल दिए गए SQL प्रश्नों की संख्या बढ़ाता है, लेकिन रूट इकाई रिकॉर्ड के अनुसार केवल एक उदाहरण सुनिश्चित करता है। हाइबरनेट इस मामले में प्रत्येक बच्चे के रिकॉर्ड के लिए एक आग का चयन करेगा। तो आपको प्रदर्शन के विचार के संबंध में इसका हिसाब देना चाहिए।
बिपुल

1
@ बिपुलकुमार हाँ, लेकिन यह विकल्प है जब हम आलसी भ्रूण का उपयोग नहीं कर सकते क्योंकि हमें उप वस्तुओं तक पहुँचने के लिए आलसी लाने के लिए एक सत्र बनाए रखने की आवश्यकता है।
मथि

18

List और ArrayList का उपयोग न करें लेकिन Set और HashSet करें।

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public Set<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

2
क्या यह हाइबरनेट सर्वोत्तम अभ्यास का एक आकस्मिक उल्लेख है या ओपी से प्रश्न पूछने वाले बहु-बच्चे के लिए प्रासंगिक है?
जैकब ज्वियर्स


समझ गया। ओपी के सवाल पर माध्यमिक। हालांकि, dzone लेख शायद नमक के एक अनाज के साथ लिया जाना चाहिए ... टिप्पणियों में लेखक के अपने प्रवेश के आधार पर।
जैकब ज्वियर्स

2
यह एक बहुत अच्छा जवाब है IMO। यदि आप डुप्लिकेट नहीं चाहते हैं, तो यह अत्यधिक संभावना है कि आप सूची के बजाय एक सेट का उपयोग करेंगे- एक सेट का उपयोग करना (और सही बराबर / हैकोड विधियों को लागू करना) ने मेरे लिए समस्या हल कर दी। हैशकोड / बराबर लागू करते समय सावधान रहें, जैसा कि रेडहैट डॉक में कहा गया है, आईडी फ़ील्ड का उपयोग करने के लिए नहीं।
Mat

1
आपके IMO के लिए धन्यवाद। इसके अलावा, समतुल्य () और हैशकोड () विधियों को बनाने में परेशानी नहीं होती है। अपनी आईडीई या लोम्बोक उन्हें आपके लिए उत्पन्न करें।

3

जावा 8 और स्ट्रीम का उपयोग करके मैं अपनी उपयोगिता विधि में यह रिटर्न स्टेटमेंट जोड़ता हूं:

return results.stream().distinct().collect(Collectors.toList());

धाराएँ बहुत तेज़ी से डुप्लिकेट निकालती हैं। मैं इस तरह अपने एंटिटी वर्ग में एनोटेशन का उपयोग करता हूं:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;

मुझे लगता है कि मेरे ऐप में सत्र का उपयोग करने के लिए bether है जहां मुझे डेटा बेस से डेटा की आवश्यकता है। जब मैंने किया क्लोज सेशन। लसीक प्रकार का उपयोग करने के लिए ऑन्कोर्स ने मेरा एंटिटी वर्ग निर्धारित किया है। मैं रिफ्लेक्टर के पास जाता हूं।


3

मुझे 2 संबद्ध संग्रह लाने के लिए समान समस्या है: उपयोगकर्ता की 2 भूमिकाएं (सेट) और 2 भोजन (सूची) और भोजन डुप्लिकेट हैं।

@Table(name = "users")
public class User extends AbstractNamedEntity {

   @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
   @Column(name = "role")
   @ElementCollection(fetch = FetchType.EAGER)
   @BatchSize(size = 200)
   private Set<Role> roles;

   @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
   @OrderBy("dateTime DESC")
   protected List<Meal> meals;
   ...
}

DISTINCT मदद नहीं करता है (DATA-JPA क्वेरी):

@EntityGraph(attributePaths={"meals", "roles"})
@QueryHints({@QueryHint(name= org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")}) // remove unnecessary distinct from select
@Query("SELECT DISTINCT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);

अंत में मुझे 2 समाधान मिले हैं:

  1. LinkedHashSet में सूची बदलें
  2. केवल फ़ील्ड "भोजन" के साथ EntityGraph का उपयोग करें और LOAD टाइप करें, जो भूमिकाओं को लोड करते हैं जैसा कि उन्होंने घोषित किया (NAG 1 समस्या को रोकने के लिए BAGSize = 200 द्वारा):

अंतिम समाधान:

@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);

1

इसके बजाय हैक का उपयोग करने की तरह:

  • Set के बजाय List
  • criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

जो आपकी sql क्वेरी को संशोधित नहीं करता है, हम (JPA चश्मा को उद्धृत करते हुए) का उपयोग कर सकते हैं

q.select(emp).distinct(true);

जो परिणामस्वरूप sql क्वेरी को संशोधित करता है, इस प्रकार DISTINCTइसमें एक होता है।


0

यह बाहरी जुड़ाव को लागू करने और दोहराए गए परिणाम लाने के लिए एक महान व्यवहार नहीं लगता है। केवल एक ही उपाय बचता है जो धाराओं का उपयोग करके हमारे परिणाम को फ़िल्टर करता है। धन्यवाद java8 फ़िल्टर करने का आसान तरीका।

return results.stream().distinct().collect(Collectors.toList());
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.