2012-06-22 13 views
6

निम्नलिखित वर्गों का प्रतिनिधित्व पर उचित उलटा (धाराप्रवाह NHibernate) सेटिंग्स की परवाह किए बिना बाहरी अद्यतन बयान उत्सर्जक, एक नंगे न्यूनतम रास्ते में, मेरे वास्तविक दुनिया परिदृश्य। मैं इसमें नए कॉलम जोड़ सकता हूं, लेकिन यह सब कुछ मैं कर सकता हूं, क्योंकि 300+ सौ टेबल डेटाबेस का उपयोग कई अन्य विरासत अनुप्रयोगों द्वारा किया जाता है जो एनएचबेर्नेट को पोर्ट नहीं किया जाएगा (इसलिए समग्र कुंजी से माइग्रेट करना एक विकल्प नहीं है) :NHibernate एक विरासत डेटाबेस के साथ संबंधों

public class MapeamentoParent : ClassMap<Parent> 
{ 
    public MapeamentoParent() 
    { 
     Id(_ => _.Id, "PARENT_ID").GeneratedBy.Identity(); 
     HasMany(_ => _.Children) 
      .Inverse() 
      .AsSet() 
      .Cascade.All() 
      .KeyColumn("PARENT_ID"); 
    } 
} 
public class MapeamentoChild : ClassMap<Child> 
{ 
    public MapeamentoChild() 
    { 
     CompositeId() 
      .KeyReference(_ => _.Parent, "PARENT_ID") 
      .KeyProperty(_ => _.ChildId, "CHILD_ID"); 
     HasMany(_ => _.Items) 
      .AsSet() 
      .Cascade.All() 
      .KeyColumns.Add("PARENT_ID") 
      .KeyColumns.Add("CHILD_ID"); 
     Version(Reveal.Member<Child>("version")); 
    } 
} 
public class MapeamentoItem : ClassMap<Item> 
{ 
    public MapeamentoItem() 
    { 
     Id(_ => _.ItemId).GeneratedBy.Assigned(); 
     Version(Reveal.Member<Item>("version")); 
    } 
} 

इस कोड मेरे तीन बच्चे और एक के साथ एक बच्चों के साथ एक माता पिता को सम्मिलित करने का उपयोग कर रहा है:

public class Parent 
{ 
    public virtual long Id { get; protected set; } 
    ICollection<Child> children = new HashSet<Child>(); 
    public virtual IEnumerable<Child> Children { get { return children; } } 
    public virtual void AddChildren(params Child[] children) 
    { 
     foreach (var child in children) AddChild(child); 
    } 
    public virtual Child AddChild(Child child) 
    { 
     child.Parent = this; 
     children.Add(child); 
     return child; 
    } 
} 
public class Child 
{ 
    public virtual Parent Parent { get; set; } 
    public virtual int ChildId { get; set; } 
    ICollection<Item> items = new HashSet<Item>(); 
    public virtual ICollection<Item> Items { get { return items; } } 
    long version; 
    public override int GetHashCode() 
    { 
     return ChildId.GetHashCode()^(Parent != null ? Parent.Id.GetHashCode() : 0.GetHashCode()); 
    } 
    public override bool Equals(object obj) 
    { 
     var c = obj as Child; 
     if (ReferenceEquals(c, null)) 
      return false; 
     return ChildId == c.ChildId && Parent.Id == c.Parent.Id; 
    } 
} 
public class Item 
{ 
    public virtual long ItemId { get; set; } 
    long version; 
} 

यह मैं "मौजूदा" डेटाबेस के लिए इन कैसे मैप किया गया है मद:

 using (var tx = session.BeginTransaction()) 
     { 
      var parent = new Parent(); 
      var child = new Child() { ChildId = 1, }; 
      parent.AddChildren(
       child, 
       new Child() { ChildId = 2, }, 
       new Child() { ChildId = 3 }); 
      child.Items.Add(new Item() { ItemId = 1 }); 
      session.Save(parent); 
      tx.Commit(); 
     } 

इन एसक्यूएल पिछले कोड के लिए उत्पन्न बयान कर रहे हैं:

-- statement #1 
INSERT INTO [Parent] 
DEFAULT VALUES; 

select SCOPE_IDENTITY() 

-- statement #2 
INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_0 */, 
      1 /* @p1_0 */, 
      1 /* @p2_0 */) 

INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_1 */, 
      1 /* @p1_1 */, 
      2 /* @p2_1 */) 

INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_2 */, 
      1 /* @p1_2 */, 
      3 /* @p2_2 */) 


-- statement #3 
INSERT INTO [Item] 
      (version, 
      ItemId) 
VALUES  (1 /* @p0_0 */, 
      1 /* @p1_0 */) 

-- statement #4 
UPDATE [Child] 
SET version = 2 /* @p0 */ 
WHERE PARENT_ID = 1 /* @p1 */ 
     AND CHILD_ID = 1 /* @p2 */ 
     AND version = 1 /* @p3 */ 

-- statement #5 
UPDATE [Child] 
SET version = 2 /* @p0 */ 
WHERE PARENT_ID = 1 /* @p1 */ 
     AND CHILD_ID = 2 /* @p2 */ 
     AND version = 1 /* @p3 */ 

-- statement #6 
UPDATE [Child] 
SET version = 2 /* @p0 */ 
WHERE PARENT_ID = 1 /* @p1 */ 
     AND CHILD_ID = 3 /* @p2 */ 
     AND version = 1 /* @p3 */ 

-- statement #7 
UPDATE [Item] 
SET PARENT_ID = 1 /* @p0_0 */, 
     CHILD_ID = 1 /* @p1_0 */ 
WHERE ItemId = 1 /* @p2_0 */ 

वक्तव्य 4, 5 और 6 बाहरी/ज़रूरत से ज़्यादा हैं, क्योंकि वह सब जानकारी पहले से ही बयान में बैच आवेषण में डेटाबेस के लिए भेजा गया था 2.

यह अपेक्षित व्यवहार होगा यदि अभिभावक मानचित्रण ने HasMany (एक से कई) रिश्ते पर उलटा संपत्ति निर्धारित नहीं की है।

बाल से संग्रह निकालें और मद में एक बच्चे प्रॉपर्टी जोड़ते हैं:

वास्तव में, यह अभी भी अजनबी है जब हम इस तरह के आइटम के लिए बाल से एक-से-अनेक संबंध से छुटकारा पाने के हो जाता है बाल और आइटम की मैपिंग मद से HasMany को हटाने और एक संदर्भ बाल वापस करने के लिए आइटम पर समग्र कुंजी पर जोड़ने के लिए

public class Child 
    { 
     public virtual Parent Parent { get; set; } 
     public virtual int ChildId { get; set; } 
     long version; 
     public override int GetHashCode() 
     { 
      return ChildId.GetHashCode()^(Parent != null ? Parent.Id.GetHashCode() : 0.GetHashCode()); 
     } 
     public override bool Equals(object obj) 
     { 
      var c = obj as Child; 
      if (ReferenceEquals(c, null)) 
       return false; 
      return ChildId == c.ChildId && Parent.Id == c.Parent.Id; 
     } 
    } 

    public class Item 
    { 
     public virtual Child Child { get; set; } 
     public virtual long ItemId { get; set; } 
     long version; 
    } 

बदलें:

public class MapeamentoChild : ClassMap<Child> 
{ 
    public MapeamentoChild() 
    { 
     CompositeId() 
      .KeyReference(_ => _.Parent, "PARENT_ID") 
      .KeyProperty(_ => _.ChildId, "CHILD_ID"); 
     Version(Reveal.Member<Child>("version")); 
    } 
} 
public class MapeamentoItem : ClassMap<Item> 
{ 
    public MapeamentoItem() 
    { 
     Id(_ => _.ItemId).GeneratedBy.Assigned(); 
     References(_ => _.Child).Columns("PARENT_ID", "CHILD_ID"); 
     Version(Reveal.Member<Item>("version")); 
    } 
} 

बदलें ग

 using (var tx = session.BeginTransaction()) 
     { 
      var parent = new Parent(); 
      var child = new Child() { ChildId = 1, }; 
      parent.AddChildren(
       child, 
       new Child() { ChildId = 2, }, 
       new Child() { ChildId = 3 }); 
      var item = new Item() { ItemId = 1, Child = child }; 
      session.Save(parent); 
      session.Save(item); 
      tx.Commit(); 
     } 

जिसके परिणामस्वरूप एसक्यूएल बयान कर रहे हैं:: निम्नलिखित है (ध्यान दें कि अब हम आइटम स्पष्ट रूप से बचाने के लिए कॉल करने की आवश्यकता) को स्तोत्र

-- statement #1 
INSERT INTO [Parent] 
DEFAULT VALUES; 

select SCOPE_IDENTITY() 

-- statement #2 
INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_0 */, 
      1 /* @p1_0 */, 
      1 /* @p2_0 */) 

INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_1 */, 
      1 /* @p1_1 */, 
      2 /* @p2_1 */) 

INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_2 */, 
      1 /* @p1_2 */, 
      3 /* @p2_2 */) 

-- statement #3 
INSERT INTO [Item] 
      (version, 
      PARENT_ID, 
      CHILD_ID, 
      ItemId) 
VALUES  (1 /* @p0_0 */, 
      1 /* @p1_0 */, 
      1 /* @p2_0 */, 
      1 /* @p3_0 */) 

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

मैं बाल से किसी भी HasMany संबंधों को दूर करने के सिवाय उन अवांछित/अनावश्यक अद्यतन बयान को रोकने के लिए किसी भी तरह से नहीं मिल रहा। ऐसा लगता है कि चूंकि बच्चा पहले से ही "उलटा" एक से कई रिश्तों ("खुद को बचाने से ज़िम्मेदार है) से" कई "है, इसलिए यह किसी अन्य से" एक "हिस्सा होने पर व्यस्त सेटअप का सम्मान नहीं करता है करने वाली कई उल्टे संबंध ...

यह मैं पागल गाड़ी चला रहा है। मैं उन अतिरिक्त अद्यतन विवरणों को बिना किसी तरह के विचारों के स्पष्टीकरण के स्वीकार नहीं कर सकता :-) क्या कोई जानता है कि यहां क्या हो रहा है?

उत्तर

8

रात और एक जवाब स्टैक ओवरफ़्लो :-) मैं समाधान के साथ आए हैं में भी यहां के लिए दृष्टि में कोई उम्मीद के माध्यम से इस के साथ संघर्ष करने के बाद ... मुझे लगता है कि शायद यह में बदलाव शुरू कर दिया था बाल वस्तुओं को माता-पिता के संग्रह में बदलाव के रूप में माना जा रहा था और इसके परिणामस्वरूप इकाई संस्करण में बदलाव आया।प्रजाति है कि की वेतन वृद्धि में संग्रह के परिणामों के राज्य के लिए परिवर्तन इकाई के संस्करण के मालिक: - (सही पर चूक वैकल्पिक)

(13) आशावादी लॉक: मेरा अनुमान है यह पढ़ने के बाद जमना शुरू कर दिया। (कई संगठनों के लिए एक के लिए, यह इस सेटिंग को अक्षम करने के लिए अक्सर उचित है।) (यहाँ मिला: http://nhibernate.info/doc/nh/en/index.html#collections)

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

public MapeamentoParent() 
    { 
     Id(_ => _.Id, "PARENT_ID").GeneratedBy.Identity(); 
     HasMany<Child>(_ => _.Children) 
      .Inverse() 
      .AsSet() 
      .Cascade.All() 
      .Not.OptimisticLock() 
      .KeyColumn("PARENT_ID"); 
    } 

यह काम नहीं किया। लेकिन तब मैं बाहरी अपडेट में दिलचस्प कुछ देखा है:

-- statement #1 
UPDATE [Child] 
SET version = 2 /* @p0 */ 
WHERE PARENT_ID = 1 /* @p1 */ 
     AND CHILD_ID = 1 /* @p2 */ 
     AND version = 1 /* @p3 */ 

-- statement #2 
UPDATE [Child] 
SET version = 2 /* @p0 */ 
WHERE PARENT_ID = 1 /* @p1 */ 
     AND CHILD_ID = 2 /* @p2 */ 
     AND version = 1 /* @p3 */ 

-- statement #3 
UPDATE [Child] 
SET version = 2 /* @p0 */ 
WHERE PARENT_ID = 1 /* @p1 */ 
     AND CHILD_ID = 3 /* @p2 */ 
     AND version = 1 /* @p3 */ 

मैं सूचना के लिए उस संस्करण 2 के लिए अद्यतन किया जा रहा था भाग्यशाली था! (डिग्रेशन: मैं डेटटाइम वर्जन फ़ील्ड का उपयोग कर रहा था, लेकिन चूंकि इसमें एक अनंत परिशुद्धता नहीं है क्योंकि मैंने जानबूझकर इसे एक अभिन्न संस्करण में बदल दिया है जब मैंने सोचना शुरू किया कि यह एक संस्करण मुद्दा था, ताकि मैं संस्करण में प्रत्येक वृद्धि को देख सकूं , और मिलीसेकंड से कम में होने वाली वृद्धि को याद न करें, जो इसकी सटीकता या कमी की वजह से डेटटाइम संस्करणों द्वारा पता लगाने योग्य नहीं हैं)। तो, एक बार फिर निराशा से पहले, मैंने माता-पिता की हैसनी को पहले जो कुछ भी किया था (किसी भी संभावित समाधान को अलग करने की कोशिश करने के लिए) को बदल दिया है और इसके बजाय बच्चे के मानचित्र पर Not.OptimisticLock() को जोड़ा है (सभी संस्थाओं के बाद जो लग रहा था बिल्कुल

-- statement #1 
INSERT INTO [Parent] 
DEFAULT VALUES; 

select SCOPE_IDENTITY() 

-- statement #2 
INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_0 */, 
      1 /* @p1_0 */, 
      1 /* @p2_0 */) 

INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_1 */, 
      1 /* @p1_1 */, 
      2 /* @p2_1 */) 

INSERT INTO [Child] 
      (version, 
      PARENT_ID, 
      CHILD_ID) 
VALUES  (1 /* @p0_2 */, 
      1 /* @p1_2 */, 
      3 /* @p2_2 */) 

-- statement #3 
INSERT INTO [Item] 
      (version, 
      ItemId) 
VALUES  (1 /* @p0_0 */, 
      1 /* @p1_0 */) 

-- statement #4 
UPDATE [Item] 
SET PARENT_ID = 1 /* @p0_0 */, 
     CHILD_ID = 1 /* @p1_0 */ 
WHERE ItemId = 1 /* @p2_0 */ 

कोई बाहरी अद्यतन कथन !!!: उनके संस्करणों को अद्यतन किया है थे बच्चे):

public class MapeamentoChild : ClassMap<Child> 
    { 
     public MapeamentoChild() 
     { 
      CompositeId() 
       .KeyReference(_ => _.Parent, "PARENT_ID") 
       .KeyProperty(_ => _.ChildId, "CHILD_ID"); 
      HasMany(_ => _.Items) 
       .AsSet() 
       .Cascade.All() 
       .Not.OptimisticLock() 
       .KeyColumns.Add("PARENT_ID") 
       .KeyColumns.Add("CHILD_ID"); 
      Version(Reveal.Member<Child>("version")); 
     } 
    } 

और यह पूरी तरह से निम्नलिखित एसक्यूएल बयान जारी काम :-)

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

इस के साथ मेरा सबसे बड़ा मुद्दा यह है कि माता-पिता के संग्रह पर सभी बाल वस्तुओं को अद्यतन किया जा रहा था, भले ही मैंने किसी भी बच्चे की वस्तुओं में कोई आइटम नहीं जोड़ा हो। यह अकेले इस तथ्य के लिए हो रहा था कि बच्चे के पास आइटम से संबंधित संबंध है ... (किसी भी बच्चे को किसी अतिरिक्त आइटम को "अतिरिक्त" प्राप्त करने के लिए कोई आइटम जोड़ने की आवश्यकता नहीं है)। ऐसा लगता है कि एनएचबेर्नेट यहां चीजों को गलत तरीके से समझ रहा है, लेकिन चूंकि मुझे पूरी तरह से एनएचबर्ननेट की गहरी समझ नहीं है, इसलिए मैं निश्चित रूप से कह नहीं सकता हूं, न ही समस्या कहां से तय कर सकता हूं, स्पष्ट रूप से यह भी पुष्टि नहीं करता कि यह वास्तव में एक समस्या है यह वास्तव में एनएचबीरनेट की वास्तविक कमी हो सकती है जो असली अपराधी है! :-)

मुझे आशा है कि कोई और अधिक प्रबुद्ध हो जाएगा कि क्या हो रहा था, यह बताने के लिए आता है, लेकिन दस्तावेज द्वारा सुझाए गए एक से कई रिश्तों पर आशावादी लॉक को झूठ बोलना इस मुद्दे को हल करता है।

+0

मैंने पाया कि माता-पिता के बीच एक अंतर है। AddChild (...) और बच्चे। माता-पिता = माता-पिता; बाद में _not_ परिणाम एक बाहरी संस्करण अद्यतन में होता है (यह बच्चे को माता-पिता में नहीं जोड़ता है। सत्र के दौरान बच्चे संग्रह, लेकिन जब माता-पिता को बाद के सत्र में लोड किया जा रहा है, तो यह वहां है।) – BatteryBackupUnit

+0

@ बैटरी बैकअप यह सच है, लेकिन एक ओआरएम के रूप में इसे संग्रह में जोड़ने पर या जब आप सीधे माता-पिता को सीधे सेट करते हैं तो इसे अलग-अलग "गलत व्यवहार" नहीं करना चाहिए। कभी-कभी हमें इसे डेटाबेस से लोड करने से पहले, संग्रह पर तुरंत रखना होगा। उन मामलों में उन अपर्याप्त प्रश्न अभी भी वहां होंगे। लेकिन यह जानना अच्छा है कि हम उन्हें "रोक" सकते हैं यदि हम चाहते हैं कि बच्चों को माता-पिता को "टाई" करना है ... केवल बच्चे का उपयोग करें। अभिभावक के संग्रह में जोड़ने के बजाय माता-पिता। :-) – Loudenvier

+1

मैं पूरी तरह से सहमत हूं। मैंने यह सुनिश्चित करने के लिए कि यह डिफ़ॉल्ट व्यवहार है, मैंने अपने सम्मेलनों में '.Not.OptimisticLock()' के साथ एक FluentNHibernate 'IHasManyConvention' जोड़ा है। – BatteryBackupUnit

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