मुझे कहां से @ ट्रांसैसेंटल एनोटेशन करना चाहिए: एक इंटरफ़ेस परिभाषा पर या एक कार्यान्वयन वर्ग में?


91

कोड में शीर्षक से प्रश्न:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

बनाम

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

जवाबों:


121

से http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

स्प्रिंग टीम की सिफारिश यह है कि आप केवल @Transactionalएनोटेशन के साथ कंक्रीट कक्षाओं की व्याख्या करें, जैसा कि एनोटेटिंग इंटरफेस के विपरीत है। आप निश्चित रूप से @Transactionalएनोटेशन को एक इंटरफ़ेस (या एक इंटरफ़ेस विधि) पर रख सकते हैं , लेकिन यह केवल तभी काम करेगा जब आप इंटरफ़ेस-आधारित प्रॉक्सी का उपयोग कर रहे हों। तथ्य यह है कि एनोटेशन विरासत में नहीं हैं , इसका मतलब है कि यदि आप वर्ग-आधारित प्रॉक्सी का उपयोग कर रहे हैं, तो लेन-देन सेटिंग्स को क्लास-आधारित प्रॉक्सी इंफ्रास्ट्रक्चर द्वारा मान्यता नहीं दी जाएगी और वस्तु को एक ट्रांसेक्शनल प्रॉक्सी में लपेटा नहीं जाएगा (जो कि निश्चित रूप से खराब होगा ) । इसलिए कृपया स्प्रिंग टीम की सलाह लें और @Transactionalएनोटेशन के साथ केवल ठोस कक्षाओं (और कंक्रीट कक्षाओं की विधियाँ) की व्याख्या करें।

नोट: चूंकि यह तंत्र प्रॉक्सी पर आधारित है, इसलिए प्रॉक्सी के माध्यम से आने वाली केवल 'बाहरी' विधि कॉल को इंटरसेप्ट किया जाएगा। इसका मतलब यह है कि 'सेल्फ-इनवोकेशन', यानी टारगेट ऑब्जेक्ट के भीतर एक ऐसी विधि जिसे टारगेट ऑब्जेक्ट का कोई और तरीका बताया जाता है, तब भी रनटाइम पर एक वास्तविक ट्रांजेक्शन नहीं होगा, भले ही इनवॉइस मेथड चिह्नित हो @Transactional!

(पहले वाक्य में जोर दिया गया, मूल से अन्य जोर।)


बिग +1 बीटीडब्ल्यू, मैंने उद्धरण को एक उद्धरण बनाने की स्वतंत्रता ली, ताकि लोग भ्रमित न हों, और मूल से इटैलिक्स और इस तरह वापस जोड़ा।
टीजे क्राउडर

मैंने इस कथन को स्प्रिंग डेन्स में पहले पढ़ा है, लेकिन फिर भी मैं यह नहीं समझ सकता कि लेन-देन सेटिंग को वर्ग-आधारित प्रॉक्सी संरचना द्वारा मान्यता क्यों नहीं दी जाएगी ? मुझे व्यक्तिगत रूप से लगता है, यह सिर्फ स्प्रिंग कार्यान्वयन सीमा है, न कि अंतर्निहित प्रॉक्सी बुनियादी ढांचे की समस्या।
dma_k 12

स्प्रिंग स्रोतों को देखते हुए यह पता लगा सकते हैं कि JdkDynamicAopProxyप्रत्येक विधि के आह्वान पर सभी बीन सलाहकारों के माध्यम से चला जाता है (यह भी देखें DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()), जिसमें घोषणात्मक लेनदेन सेटअप के मामले में शामिल हैं BeanFactoryTransactionAttributeSourceAdvisor। इसकी बारी TransactionAttributeSourcePointcut#matches()में इनवॉइस किया गया है, जिसे लेन-देन-संबंधी जानकारी एकत्र करनी चाहिए। इसे टारगेट क्लास पास किया गया है और यह हमेशा इस क्लास के इम्प्लाइज को सभी इंटरफेस में बदल सकता है। अब: क्यों यह मज़बूती से काम नहीं कर सकता है?
dma_k 12

2
@dma_k - जावा रनटाइम केवल सुपरक्लास से विरासत में मिली क्लास एनोटेशन तक सीधी पहुँच देता है, या विधि एनोटेशन जिनमें कोई विरासत नहीं है। तो पॉइंटकटेचेज़ () को वास्तव में सभी इंटरफेस को पुन: प्राप्त करना होगा, प्रतिबिंब के साथ "वास्तविक" ओवरराइड विधि का पता लगाएं, पुल के तरीकों, ओवरलोडिंग, वैरगेज, जेनरिक, प्रॉक्सिस, और निश्चित रूप से यह तेजी से होता है । फिर आपको हीरे की विरासत से निपटना होगा - जो संभवतः कई विरासत वाले @Transactionalएनोटेशन लागू होंगे? इसलिए, हालांकि यह सब संभव है , मैं वसंत को दोष नहीं देता। jira.springsource.org/browse/SPR-975
पीटर डेविस

9

आप उन्हें इंटरफ़ेस पर रख सकते हैं, लेकिन चेतावनी दी जा सकती है कि लेनदेन कुछ मामलों में समाप्त नहीं हो सकता है। स्प्रिंग डॉक्स के सेकंडियन 10.5.6 में दूसरा टिप देखें :

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

मैं उन्हें इस कारण से लागू करने की सलाह दूंगा।

मुझे भी, लेनदेन एक कार्यान्वयन विवरण की तरह लगता है इसलिए उन्हें कार्यान्वयन वर्ग में होना चाहिए। लॉगिंग या परीक्षण कार्यान्वयन (मॉक) के लिए आवरण कार्यान्वयन होने की कल्पना करें जिन्हें लेन-देन करने की आवश्यकता नहीं है।


9

स्प्रिंग की सिफारिश है कि आप इंटरफ़ेस के बजाय कंक्रीट कार्यान्वयन को एनोटेट करें। किसी इंटरफ़ेस पर एनोटेशन का उपयोग करना गलत नहीं है, यह केवल उस सुविधा का दुरुपयोग करना संभव है और अनजाने में आपकी @ घोषणा को बायपास कर सकता है।

यदि आपने किसी इंटरफ़ेस में कुछ लेन-देन को चिह्नित किया है और फिर वसंत में कहीं और इसके कार्यान्वयन वर्गों में से एक को देखें, तो यह बिल्कुल स्पष्ट नहीं है कि वसंत द्वारा बनाई गई वस्तु @Transactional एनोटेशन का सम्मान नहीं करेगी।

व्यवहार में यह कुछ इस तरह दिखता है:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

कंक्रीट वर्गों पर सहायक @ समर्थन:

मैं आम तौर पर 3 वर्गों में एक समाधान आर्किटेक्ट करना पसंद करता हूं: एक एपीआई, एक कार्यान्वयन और एक वेब (यदि आवश्यक हो)। मैं पूरी कोशिश करता हूं कि निर्भरता को कम करके एपीआई को जितना संभव हो उतना हल्का / सरल / पीओजेओ के रूप में रखा जाए। यह विशेष रूप से महत्वपूर्ण है यदि आप इसे वितरित / एकीकृत वातावरण में खेलते हैं जहां आपको एपीआई को बहुत साझा करना होगा।

@Transactional डालने के लिए API सेक्शन में स्प्रिंग लाइब्रेरी की आवश्यकता होती है, जो IMHO प्रभावी नहीं है। इसलिए मैं इसे कार्यान्वयन में जोड़ना पसंद करता हूं जहां लेनदेन चल रहा है।


0

इसे इंटरफ़ेस पर रखना तब तक ठीक है जब तक कि TX डेटा के बारे में आपकी IFC देखभाल के सभी पूर्वाभास योग्य कार्यान्वयनकर्ता (लेन-देन की समस्याएँ नहीं हैं, जो सिर्फ डेटाबेस ही निपटते हैं)। यदि विधि TX के बारे में परवाह नहीं करती है (लेकिन आपको इसे हाइबरनेट या जो भी करने की आवश्यकता है), तो इसे निहित पर रखें।

इसके अलावा, @Transactionalइंटरफ़ेस में तरीकों पर जगह देना थोड़ा बेहतर हो सकता है :

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.