2012-11-20 18 views
9

मैं निम्नलिखित कोड है:Grails UnexpectedRollbackException हुई: सुनिश्चित नहीं क्यों

class ServiceA { 

    def save(Object object) { 
     if (somethingBadComesBack) { 
     throw new CustomRuntimeException(data) 
     } 
    } 
} 

class ServiceB { 

    def serviceA 

    def save(Object object) { 
     try { 
     serviceA.save(object) 
     // do more stuff if good to go 
     } catch(CustomRuntimeException e) { 
     // populate some objects with errors based on exception 
     } 
    } 
} 

class ServiceC { 

    def serviceB 

    def process(Object object) { 
     serviceB.save(object) 
     if (object.hasErrors() { 
      // do some stuff 
     }else{ 
     // do some stuff 
     } 

     def info = someMethod(object) 
     return info 
    } 
} 

class SomeController { 

    def serviceC 

    def process() { 

    def object = ..... 
    serviceC.save(object) // UnexpectedRollbackException is thrown here 

    } 
} 

ServiceA.save() कहा जाता है और एक अपवाद होता है, ServiceC.save() एक UnexpectedRollbackException फेंक जाता है जब यह वापस जाने के लिए कोशिश करता है।

try { 
    serviceC.process(object) 
}catch(UnexpectedRollbackException e) { 
    println e.getMostSpecificCause() 
} 

और मैं हो रही है:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only 

मुझे यकीन है कि जहां इसे ठीक करने के लिए देख शुरू करने के लिए नहीं कर रहा हूँ

मैं निम्नलिखित किया था।

उत्तर

11

आप लेनदेन को वापस रोल करने के लिए रनटाइम अपवाद का उपयोग कर रहे हैं, लेकिन यह धोखाधड़ी है - यह दुष्प्रभाव का लाभ उठा रहा है। रनटाइम अपवाद स्वचालित रूप से लेन-देन वापस लेते हैं क्योंकि आपको उन्हें पकड़ने की आवश्यकता नहीं होती है, इसलिए यह माना जाता है कि अगर कोई फेंक दिया जाता है, तो इसकी अपेक्षा नहीं की जाती थी और डिफ़ॉल्ट व्यवहार वापस रोल करना होता है। आप विशिष्ट अपेक्षित रनटाइम अपवादों के लिए वापस रोल न करने के तरीकों को कॉन्फ़िगर कर सकते हैं, लेकिन यह कुछ दुर्लभ है। चेक किए गए अपवाद वापस अपवाद नहीं रोल करते हैं क्योंकि जावा में उन्हें throws में पकड़ा या घोषित किया जाना चाहिए, इसलिए आपको या तो इसे स्पष्ट रूप से फेंक दिया जाना चाहिए या इसे डुबो देना होगा; किसी भी तरह से आपको फिर कोशिश करने का मौका मिला।

सही तरीका जानबूझकर एक सौदे वापस रोल करने के लिए वर्तमान TransactionStatus पर setRollbackOnly() कॉल करने के लिए है, लेकिन यह एक सेवा विधि (यह एक withTransaction ब्लॉक में है, क्योंकि यह बंद करने के लिए तर्क है) में सीधा नहीं पहुंचा। लेकिन यह आसान है: org.springframework.transaction.interceptor.TransactionAspectSupport आयात करें और TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() पर कॉल करें। यह आवश्यक होगा कि आप अपना कोड पुन: कार्य करें क्योंकि पकड़ने के लिए अपवाद नहीं होगा, इसलिए आपको यह जांचना होगा कि इसे TransactionAspectSupport.currentTransactionStatus().isRollbackOnly() के साथ वापस ले जाया गया था।

मुझे यकीन नहीं है कि यह एक Grails मुद्दा या मानक व्यवहार है, लेकिन जब मैं इसे डिबग कर रहा था तो 3 अलग TransactionStatus उदाहरणों के साथ 3 प्रतिबद्ध कॉल थे। केवल पहले रोलबैक ध्वज सेट था, लेकिन दूसरा पहले के बारे में पता था और ठीक था। तीसरा व्यक्ति एक नया लेनदेन माना जाता था और वह वही था जिसने आप को देख रहे थे। तो यह मैं 2 और 3 सेवा तरीकों को यह जोड़ा आस-पास काम करने के लिए:

def status = TransactionAspectSupport.currentTransactionStatus() 
if (!status.isRollbackOnly()) status.setRollbackOnly() 
श्रृंखला के लिए

रोलबैक झंडा। यह काम करता है और मुझे UnexpectedRollbackException नहीं मिला।

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

+0

धन्यवाद बर्ट। इस दृष्टिकोण के साथ खेलने के लिए जा रहे हैं और देखें कि मैं एक क्या प्राप्त कर सकता हूं। मैं वापस रिपोर्ट करूंगा ... – Gregg

+2

Grails 2.3.7 में डिफ़ॉल्ट रूप से इस स्थिति को संभालने के लिए एक सुविधा शामिल होगी: http://jira.grails.org/browse/GRAILS-11145 –

+0

@FlareCoder - इसे साझा करने के लिए धन्यवाद !! मैंने अभी तक grails 2.3.6 से 2.3.7 तक अपग्रेड किया है और अब समस्या हल हो गई है। – arcseldon

0

ऐसा लगता है कि default transactionality of services आपको काट रहा है, और सर्विस ए में फेंक दिया गया अनचाहे अपवाद केवल एक बार पकड़े जाने पर भी रोलबैक पर लेनदेन को नष्ट कर रहा है।

उपरोक्त दस्तावेज़ों का कहना है कि टीएक्सएन प्रचार स्तर PROPAGATION_REQUIRED है, जिसका अर्थ यह होना चाहिए कि मेरी मेमोरी मेरी सेवा करता है, तो मेरी सेवा सी से एक ही लेनदेन साझा किया जाता है। क्या आपके पास सर्विस ए की save विधि बाद में ऑटो-रोलबैक से बचने के लिए, RuntimeException के बजाय एक चेक अपवाद फेंक सकती है? या अपनी सेवाओं पर लेनदेन अक्षम करें, अगर यह आपके लिए एक विकल्प है?

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

  • कोई संबंधित समस्या नहीं^_^