2008-12-02 12 views
19

मेरे पास इकाइयों का 3-स्तर का पदानुक्रम है: ग्राहक-ऑर्डर-लाइन, जिसे मैं किसी दिए गए ग्राहक के लिए ISession.Get (id) का उपयोग करके पूरी तरह से पुनर्प्राप्त करना चाहता हूं। मैं निम्न XML टुकड़े है:एनएचबर्ननेट कई स्तरों पर उत्सुक उत्सुक

customer.hbm.xml:

<bag name="Orders" cascade="all-delete-orphan" inverse="false" fetch="join"> 
    <key column="CustomerID" /> 
    <one-to-many class="Order" /> 
</bag> 

order.hbm.xml:

<bag name="Lines" cascade="all-delete-orphan" inverse="false" fetch="join"> 
    <key column="OrderID" /> 
    <one-to-many class="Line" /> 
</bag> 

मैं का इस्तेमाल किया है लाने = "में शामिल होने के" है कि इंगित करने के लिए विशेषता मैं प्रत्येक माता-पिता के लिए बच्चे इकाइयों को लाने के लिए चाहता हूं, और इसने सही एसक्यूएल बनाया है:

SELECT 
    customer0_.ID AS ID8_2_, 
    customer0_.Name AS Name8_2_, 
    orders1_.CustomerID AS CustomerID__4_, 
    orders1_.ID AS ID4_, 
    orders1_.ID AS ID9_0_, 
    orders1_.PostalAddress AS PostalAd2_9_0_, 
    orders1_.OrderDate AS OrderDate9_0_, 
    lines2_.OrderID AS OrderID__5_, 
    lines2_.ID AS ID5_, 
    lines2_.ID AS ID10_1_, 
    lines2_.[LineNo] AS column2_10_1_, 
    lines2_.Quantity AS Quantity10_1_, 
    lines2_.ProductID AS ProductID10_1_ 

FROM Customer customer0_ 

LEFT JOIN [Order] orders1_ 
     ON customer0_.ID=orders1_.CustomerID 

LEFT JOIN Line lines2_ 
     ON orders1_.ID=lines2_.OrderID 

WHERE customer0_.ID=1 

अभी तक, यह दिखता है अच्छा - एसक्यूएल रिकॉर्ड्स का सही सेट देता है (केवल एक अलग ऑर्डरिड के साथ), लेकिन जब मैं ऑर्डर और लाइन्स के लिए सही संख्या में इकाइयों (एनएच से) की पुष्टि करने के लिए एक परीक्षण चलाता हूं, तो मुझे गलत परिणाम मिलते हैं

I होना चाहिए (मेरे टेस्ट डेटा से), 1xOrder और 4xLine, हालांकि, मुझे 4xOrder और 4xLine मिल रहा है। ऐसा प्रतीत होता है कि एनएच परिणाम सेट में ऑर्डर जानकारी के 'दोहराव' समूह को मान्यता नहीं दे रहा है, न ही ऑर्डर इकाई को सही तरीके से 'पुन: उपयोग' कर रहा है।

मैं सभी पूर्णांक आईडी (पीके) का उपयोग कर रहा हूं, और मैंने आशा व्यक्त की है कि एनएच इन इकाइयों की समानता को देखेगा, इस आईडी का उपयोग कर टी के आईसीओपरटेबल को लागू करने की कोशिश की है। मैंने आईडी का उपयोग करने के लिए बराबर और GetHashCode ओवरराइड करने का भी प्रयास किया है। इन 'प्रयासों' में से कोई भी सफल नहीं हुआ है।

क्या एनएच के लिए एक समर्थित ऑपरेशन "एकाधिक स्तरित fetch" है, और यदि हां, तो क्या इसका समर्थन करने के लिए कोई XML सेटिंग आवश्यक है (या कुछ अन्य तंत्र)?


एनबी: मैंने अंततः इस कोड को हल करने के लिए अपने कोड में कुछ बदलावों के साथ सिरोको का समाधान किया। एक्सएमएल को बैग से सेट में बदलना होगा, सभी संग्रहों के लिए, और अधिकारों को स्वयं आईसीओपरपेबल <> को लागू करने के लिए बदल दिया गया था, जो कि विशिष्टता स्थापित करने के लिए एक सेट की आवश्यकता है।

public class BaseEntity : IComparable<BaseEntity> 
{ 
    ... 

    private Guid _internalID { get; set; } 
    public virtual Guid ID { get; set; } 

    public BaseEntity() 
    { 
     _internalID = Guid.NewGuid(); 
    } 

    #region IComparable<BaseEntity> Members 

    public int CompareTo(BaseEntity other) 
    { 
     if (ID == Guid.Empty || other.ID == Guid.Empty) 
      return _internalID.CompareTo(other._internalID); 

     return ID.CompareTo(other.ID); 
    } 

    #endregion 

    ... 

} 

एक आंतरिक आईडी फ़ील्ड के उपयोग पर ध्यान दें। यह नई (क्षणिक) इकाइयों के लिए आवश्यक है, अन्यथा उनके पास शुरुआत में एक आईडी नहीं होगी (मेरे मॉडल ने उन्हें सहेजे जाने पर आपूर्ति की है)।

+0

[यह उत्तर] (http://stackoverflow.com/questions/5266180/fighting-cartesian-product-x-join-when-using-nhibernate-3-0-0/5285739#5285739) मुझे यह देखने में मदद मिली कि कैसे डुप्लिकेट लौटाए बिना बच्चों और पोते-बच्चों को उत्सुकता से लाने के लिए क्वेरीओवर और भविष्य के प्रश्नों का उपयोग करना। इस तकनीक में कार्य को अलग-अलग SQL क्वेरी में विभाजित करना शामिल है जो डेटाबेस में एक राउंडट्रिप में निष्पादित होते हैं। –

उत्तर

21

आपको 4 एक्सऑर्डर और 4 एक्सलाइन मिल रही हैं क्योंकि लाइनों के साथ जुड़ने से परिणाम दोगुना हो जाते हैं।आप आईसीआरटीरिया पर ट्रांसफॉर्मर सेट कर सकते हैं जैसे:

.SetResultTransformer(new DistinctRootEntityResultTransformer()) 
+3

मैंने पाया कि यह वास्तव में समस्या को हल करता है, * यदि * मैपिंग बैग से सेट में बदल जाते हैं, और मैं बेस क्लास पर आवश्यक आईसीओपरपेबल लागू करता हूं। –

+1

दिलचस्प, मैंने DistinctRoot का उपयोग किया है .... लेकिन हमेशा एक बैग के साथ, और कभी भी IComparable लागू नहीं। लेकिन 3 स्तर लोडिंग के लिए कभी नहीं गए :) – sirrocco

+0

इसके अलावा, मुझे लगता है कि ग्राहक को लोड करना और ग्राहक को जाना नहीं चाहिए। ऑर्डर। आम तौर पर आप ग्राहक से अपने आदेश बताने के लिए नहीं कहते हैं। तो रिपो और: GetOrdersForCustomerId (int id) होना सबसे अच्छा होगा। – sirrocco

5

मैं सिर्फ Ayende's Blogpost पढ़ जहां वह निम्नलिखित उदाहरण का इस्तेमाल किया:

session.CreateCriteria(typeof(Post)) 
    .SetFetchMode("Comments", FetchMode.Eager) 
    .List(); 

एक मानदंड क्वेरी में एक विशेष क्वेरी

पर लेज़ी लोड हो रहा है से बचने के लिए हो सकता है कि आप कर सकते हैं।

+0

आपने इस टिप्पणी के कारण मुझे कुछ हद तक रिग्रेशन परीक्षण बचाया। बहुत आभारी। –

+1

प्रश्नों के अनुसार यह "एकाधिक स्तरों पर" काम नहीं करेगा। कई स्तरों के लिए FetchMode.Eager का उपयोग करने के परिणामस्वरूप एक कार्टशियन उत्पाद होगा। सही एसक्यूएल उत्पन्न होता है, लेकिन NHibernate इसे आपके लिए हल नहीं करेगा। –

0

@ टाइगर: आपकी क्वेरी केवल टिप्पणियों के साथ पोस्ट लौटाती है। यह सभी टिप्पणियों (2 स्तर) के साथ सभी पोस्ट लाता है। बेन पूछता है कि ग्राहक लाइन टूटेम (3 स्तर) को ऑर्डर करने के लिए ग्राहक है। @ बेन: मेरे ज्ञान के लिए nHibernate अभी तक 3 स्तर तक उत्सुक लोडिंग का समर्थन नहीं करता है। हाइबरनेट आपको इसका समर्थन करता है।

+0

@ शेराज़ - मुझे आशा है कि आप गलत हैं :-) लेकिन अगर आप सही हैं, तो यह सही एसक्यूएल क्यों उत्पन्न करेगा? भाग्य? –

0

मुझे एक ही समस्या थी। यह thread देखें। मुझे समाधान नहीं मिला लेकिन फैबियो से एक संकेत मिला। बैग के बजाय सेट का प्रयोग करें। और यह काम किया।

तो मेरा सुझाव सेट का उपयोग करने का प्रयास है। आप Iesi संग्रह उपयोग IDictonary उपयोग करने के लिए नहीं है और आप रखने के लिए की जरूरत है राष्ट्रीय राजमार्ग खुश

public override IEnumerable<Baseline> GetAll() 
{ 
    var baselines = Session.CreateQuery(@" from Baseline b 
              left join fetch b.BaselineMilestones bm 
              left join fetch bm.BaselineMilestonePrevious ") 
              .SetResultTransformer(Transformers.DistinctRootEntity) 
              .List<Baseline>(); 
    return baselines; 
} 
+2

से पर स्विचिंग मेरे लिए भी काम करता है, लेकिन एक एसक्यूएल ट्रेस दिखाता है कि कार्टेशियन उत्पाद है अभी भी सर्वर पर उत्पादित। इसका मतलब यह है कि एनएचबीर्नेट अपने बच्चों को और बच्चे के बड़े संग्रहों को सही ढंग से तैयार करने के लिए परिणामों को छू रहा है और परिणामों को फ़िल्टर कर रहा है। यह शायद कई परिस्थितियों में आदर्श नहीं होगा, क्योंकि इसका मतलब हो सकता है कि तारों में हजारों या लाखों रिकॉर्ड खींचे जा रहे हैं, और फिर सही ऑब्जेक्ट ग्राफ़ बनाने के लिए बहुत सारी प्रोसेसिंग हो सकती है। –

1

है अपने एक-से-manys बैग के रूप में है, तो आप 2 प्रश्नों, प्रत्येक पदानुक्रम का केवल 1 स्तर के साथ जारी कर सकते हैं । इस तरह जैसे कुछ:

var temp = session.CreateCriteria(typeof(Order)) 
    .SetFetchMode("Lines", NHibernate.FetchMode.Eager) 
    .Add(Expression.Eq("Customer.ID", id)) 
    .List(); 

var customer = session.CreateCriteria(typeof(Customer)) 
    .SetFetchMode("Orders", NHibernate.FetchMode.Eager) 
    .Add(Expression.Eq("ID", id)) 
    .UniqueResult(); 

लाइन्स पहली क्वेरी में राष्ट्रीय राजमार्ग कैश में लोड करने के लिए, ताकि वे आलसी लोड हो रहा है की जरूरत नहीं होगी जब बाद में जैसे customer.Orders तक पहुँचने [0] .Lines [0]।

संबंधित मुद्दे