2012-05-24 23 views
16

परिदृश्य पर विचार करें दो तरीकों अलग राज्यविहीन सेम में मौजूद हैजेपीए नेस्टेड लेन-देन और लॉकिंग

public class Bean_A { 
    Bean_B beanB; // Injected or whatever 
    public void methodA() { 
    Entity e1 = // get from db 
    e1.setName("Blah"); 
    entityManager.persist(e1); 
    int age = beanB.methodB(); 

    } 
} 
public class Bean_B { 
    //Note transaction 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void methodB() { 

    // complex calc to calculate age 
    } 

} 

BeanA.methodA द्वारा शुरू किया लेन-देन निलंबित कर दिया जाएगा और नए लेनदेन BeanB.methodB में शुरू किया जाएगा। क्या होगा यदि विधिबी को विधि ए द्वारा संशोधित एक ही इकाई तक पहुंचने की आवश्यकता हो। इसका परिणाम डेडलॉक होगा। क्या अलगाव स्तर पर भरोसा किए बिना इसे रोकना संभव है?

+0

आप कैसे और कहाँ डेडलॉक प्राप्त करते हैं? सत्र कैश से या डेटाबेस लॉक पंक्तियों से? –

उत्तर

20

एचएम, चलो सभी मामलों की सूची।

REQUIRES_NEW वास्तव में लेनदेन घोंसला नहीं करता है, लेकिन जैसा कि आपने बताया है कि वर्तमान में रोक दिया गया है। फिर एक ही जानकारी तक पहुंचने के लिए केवल दो लेन-देन होते हैं। (यह दो नियमित समवर्ती लेनदेन के समान है, सिवाय इसके कि वे समवर्ती नहीं हैं लेकिन निष्पादन के समान धागे में हैं)।

T1 T2   T1 T2 
―    ― 
|    | 
       | 
    ―   | ― 
    |   | | 
    |  =  | | 
    ―   | ― 
       | 
|    | 
―    ― 

फिर हम आशावादी बनाम निराशावादी लॉकिंग पर विचार करने की जरूरत है।

इसके अलावा, हमें पर विचार करने की आवश्यकता है ORM द्वारा संचालित। ORMs के साथ, हम के बाद से flush ढांचे द्वारा नियंत्रित किया जाता है, जब राईट होता है एक स्पष्ट नियंत्रण नहीं है,। आमतौर पर, एक अंतर्निहित फ्लश होता है से पहले प्रतिबद्ध है, लेकिन अगर कई प्रविष्टियों संशोधित कर रहे हैं, ढांचे के रूप में अच्छी तरह से मध्यवर्ती flushes कर सकते हैं।

1) आइए आशावादी लॉकिंग पर विचार करें, जहां ताले हासिल नहीं करते हैं, लेकिन विशिष्ट ताले हासिल करते हैं।

टी 1 द्वारा पढ़ा गया लॉक प्राप्त नहीं करता है।

1 ए) यदि टी 1 ने स्वाभाविक रूप से परिवर्तनों को फ्लश किया था, तो उसने एक विशेष लॉक हासिल किया था। जब टी 2 काम करता है, तो यह लॉक हासिल करने का प्रयास करता है लेकिन नहीं कर सकता। सिस्टम अवरुद्ध है। यह किसी विशेष प्रकार की डेडलॉक के बावजूद हो सकता है। पूरा करने पर निर्भर करता है कि लेन-देन या ताले कैसे समय निकालते हैं।

1 बी) यदि टी 1 ने स्वाभाविक रूप से परिवर्तनों को फ्लश नहीं किया है, तो कोई लॉक अधिग्रहण नहीं किया गया है। जब टी 2 काम करता है, तो यह प्राप्त करता है और इसे रिलीज़ करता है और सफल होता है। जब टी 1 प्रतिबद्ध करने का प्रयास करता है, तो यह एक संघर्ष को नोटिस करता है और विफल रहता है।

2) चलो निराशावादी लॉकिंग पर विचार करें, जहां साझा ताले हासिल करें और विशेष ताले लिखें।

टी 1 द्वारा पढ़ा गया एक साझा लॉक प्राप्त करें।

2 ए) यदि टी 1 प्रक्षेपित रूप से फ्लश हो जाता है, तो यह लॉक इंट को एक विशेष लॉक बदल देता है। स्थिति 1 ए के समान है)

2 बी) यदि टी 1 ने स्वाभाविक रूप से फ्लश नहीं किया है, तो टी 1 में एक साझा लॉक है। जब टी 2 प्रतिबद्ध होता है, तो यह एक विशेष लॉक और ब्लॉक प्राप्त करने का प्रयास करता है। सिस्टम को फिर से अवरुद्ध कर दिया गया है

निष्कर्ष: यदि कोई समयपूर्व फ्लश नहीं होता है तो यह आशावादी लॉकिंग के साथ ठीक है, जिसे आप कड़ाई से नियंत्रित नहीं कर सकते हैं।

+0

@ewernil मुझे यहां कोई संदेह है, अब हमारे पास दो लेनदेन हैं, पहला लेनदेन अभी तक पूरा नहीं हुआ है, तो दूसरा लेनदेन (require_new) परिणाम को कैसे देख सकता है जो अभी तक पहले तक नहीं किया गया है? क्या आप कृपया कुछ प्रकाश डालेंगे? –

+0

@ एसएएम जब आप किसी लेनदेन में पंक्ति को संशोधित करते हैं तो आप लॉक प्राप्त करते हैं। दूसरा लेनदेन पुरानी पंक्ति को पढ़ सकता है लेकिन पहली लॉक जारी होने तक पंक्ति को संशोधित नहीं कर सकता है। – ewernli

0

entityManager.persist(e1); के बाद और int age = beanB.methodB(); से पहले लेनदेन करने के द्वारा प्रोग्रामिंग कर रहा है?

public class Bean_A { 
    Bean_B beanB; // Injected or whatever 
    public void methodA() { 
    EntityManager em = createEntityManager(); 
    Entity e1 = // get from db 
    e1.setName("Blah"); 
    entityManager.persist(e1); 
    em.getTransaction().commit(); 
    int age = beanB.methodB(); 

    } 
} 
public class Bean_B { 
    //Note transaction 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void methodB() { 

    // complex calc to calculate age 
    } 

} 

संपादित: CMT

यदि आप CMT, आप अभी भी प्रोग्राम के लिए प्रतिबद्ध कर सकते हैं, तो आप सिर्फ लेन-देन EJBContext से मिलता है। उदाहरण: http://geertschuring.wordpress.com/2008/10/07/how-to-use-bean-managed-transactions-with-ejb3-jpa-and-jta/

या आप @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void methodC() जोड़ सकते हैं जो e1.setName("Blah"); entityManager.persist(e1); करेगा, यानी, यह एक लेनदेन में ई 1 जारी रहेगा। फिर अपने methodA()

methodC(); 
beanB.methodB(); 
+0

और क्या होगा यदि यह संभव नहीं है? सीएमटी – anergy

+0

के मामले में उदाहरण सीएमटी में लेनदेन करने की अजीब सलाह है लेकिन फिर भी अन्य परिदृश्य हो सकता है जहां मध्य में प्रतिबद्ध होना संभव नहीं है क्योंकि आप किसी अन्य बीन मेन्थोड – anergy

+1

को कॉल कर रहे हैं, यह ईजेबी मैन्युअल रूप से मैन्युअल नहीं है लेनदेन का प्रबंधन करें ... विधि बी के बाद कोई अपवाद होता है या नहीं? कोई रोलबैक संभव नहीं है ... –

1

यहाँ कहेंगे REQUIRES_NEW लेनदेन सीमांकन के उपयोग के बारे में recent article है।

मेरे अनुभव से, वहाँ मानक कोड के साथ कोई मृत लॉक होना चाहिए: प्रतिबंधात्मक where खंड और कुछ आवेषण के साथ किए जाते हैं। कुछ विशिष्ट मामलों में, कुछ डेटाबेस इंजन लॉक एस्केलेशन कर सकते हैं यदि लेन-देन के दौरान एक ही टेबल पर कई पंक्तियां पढ़ी जाती हैं या डाली जाती हैं ... और उस स्थिति में, हाँ एक मृत-लॉक हो सकता है।

लेकिन उस स्थिति में, समस्या REQUIRES_NEW से नहीं बल्कि SQL डिज़ाइन से आती है। यदि उस डिज़ाइन में सुधार नहीं किया जा सकता है, तो आपके पास अलगाव स्तर को और अधिक ढीले स्तर में बदलने के लिए कोई अन्य विकल्प नहीं है।

3

इकाई दर्रा और मर्ज ...

आप methodB() के लिए अपने नए इकाई पारित कर सकते हैं, और नए EntityManager को यह मर्ज करें। जब विधि रिटर्न अपनी इकाई को ताज़ा परिवर्तन देखने के लिए:

public class Bean_A { 
    Bean_B beanB; // Injected or whatever 
    public void methodA() { 
    Entity e1 = // get from db 
    e1.setName("Blah"); 
    entityManager.persist(e1); 
    int age = beanB.methodB(e1); 
    entityManager.refresh(e1); 
    } 
} 

public class Bean_B { 
    //Note transaction 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void methodB(Entity e1) { 
    e1 = entityManager.merge(e1); 
    // complex calc to calculate age 
    } 

} 

नोट जब नए लेन-देन methodB के बाद बंद कर देता है कि यह आपके इकाई के लिए प्रतिबद्ध होगा।

... या methodB

कॉल करने से पहले इसे सहेजें आप इकाई अपने मुख्य लेन-देन से अलग से सहेजा जाता है उपरोक्त विधि का उपयोग करते हैं, तो आप नहीं ढीला कुछ भी आप कॉल करने से पहले Bean_A से बचाने अगर methodB():

public class Bean_A { 
    Bean_B beanB; // Injected or whatever 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void createEntity() { 
    Entity e1 = // get from db 
    e1.setName("Blah"); 
    entityManager.persist(e1); 
    } 

    public void methodA() { 
    createEntity() 
    int age = beanB.methodB(); 
    } 
} 

public class Bean_B { 
    //Note transaction 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void methodB() { 

    // complex calc to calculate age 
    } 

}