2016-08-23 5 views
7

मैं एक विधि है, 'databaseChanges' है, जो 2 आपरेशन फोन है। 'ए' पहले, 'बी' आखिरी। 'ए' & 'बी' हो सकता है सी reate, यू pdate डी मेरी स्थायी भंडारण, Oracle डाटाबेस 11g में हटाएं कार्यक्षमताओं।रोल वापस एक अगर B गलत हो जाता है। पुनरावृत्ति तरह से ए, बी: वसंत बूट, jdbctemplate

मान लें,

'ए' तालिका उपयोगकर्ता में एक रिकॉर्ड को अद्यतन, विशेषता ज़िप, जहां आईडी = 1.

'बी' तालिका शौक में एक रिकॉर्ड डालें।

परिदृश्य: डेटाबेस चेंज विधि कहा जाता है, 'ए' रिकॉर्ड संचालित करता है और अद्यतन करता है। 'बी' चलाता है और एक रिकॉर्ड, कुछ होता है सम्मिलित करने के लिए प्रयास करते हैं, एक अपवाद दिया गया है, अपवाद databaseChanges विधि के लिए बुदबुदाती है।

अपेक्षित: 'ए' और 'बी' कुछ भी नहीं बदला। अद्यतन 'ए' किया, रोलबैक होगा। 'बी' ने कुछ भी नहीं बदला, ठीक है ... एक अपवाद था।

वास्तविक: 'ए' अपडेट वापस लुढ़का नहीं लगता है। 'बी' ने कुछ भी नहीं बदला, ठीक है ... एक अपवाद था।

private void databaseChanges(Connection conn) { 
    try { 
      conn.setAutoCommit(false); 
      A(); //update. 
      B(); //insert 
      conn.commit(); 
    } catch (Exception e) { 
     try { 
       conn.rollback(); 
     } catch (Exception ei) { 
        //logs... 
     } 
    } finally { 
      conn.setAutoCommit(true); 
    } 
} 

समस्या:


कुछ कोड

तो मैं संबंध नहीं था, मैं की तरह कुछ करना होगा मैं कनेक्शन (टैग को देखने की जरूरत नहीं है सवाल के साथ कि पोस्ट)

मैंने कोशिश की करने के लिए:

@Service 
public class SomeService implements ISomeService { 
    @Autowired 
    private NamedParameterJdbcTemplate jdbcTemplate; 
    @Autowired 
    private NamedParameterJdbcTemplate npjt; 

    @Transactional 
    private void databaseChanges() throws Exception { 
     A(); //update. 
     B(); //insert 
    } 
} 

मेरे AppConfig वर्ग:

import javax.sql.DataSource; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 

@Configuration 
public class AppConfig {  
    @Autowired 
    private DataSource dataSource; 

    @Bean 
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate() { 
     return new NamedParameterJdbcTemplate(dataSource); 
    } 
} 

'ए' अद्यतन करता है। 'बी' से एक अपवाद फेंक दिया गया है। 'ए' द्वारा किए गए अपडेट को वापस नहीं किया गया है।

मैं क्या पढ़ा से, मैं समझता हूँ कि मैं @Transactional सही ढंग से उपयोग नहीं कर रहा हूँ। मैंने पढ़ा है और मेरी समस्या को हल करने succeess बिना कई ब्लॉग पोस्ट और stackverflow क्यू & एक की कोशिश की।

कोई सुझाव?


संपादित

एक विधि है कि फोन databaseChanges() विधि

public void changes() throws Exception { 
    someLogicBefore(); 
    databaseChanges(); 
    someLogicAfter(); 
} 

जो विधि @Transactional साथ एनोटेट किया जाना चाहिए,

परिवर्तन() नहीं है? databaseChanges()?

उत्तर

1

आपके द्वारा प्रस्तुत पहला कोड UserTransactions के लिए है, यानी एप्लिकेशन को लेनदेन प्रबंधन करना है। आम तौर पर आप कंटेनर को इसका ख्याल रखना चाहते हैं और @ ट्रान्सएक्शनल एनोटेशन का उपयोग करना चाहते हैं। मुझे लगता है कि आपके मामले में समस्या हो सकती है, कि आपके पास निजी विधि पर एनोटेशन है। मैं एनोटेशन को कक्षा स्तर

@Transactional 
public class MyFacade { 

public void databaseChanges() throws Exception { 
    A(); //update. 
    B(); //insert 
} 

पर ले जाना चाहिए, तो इसे ठीक से रोलबैक करना चाहिए। आप अधिक विवरण यहाँ Does Spring @Transactional attribute work on a private method?

+0

मैं कोशिश करूँगा, धन्यवाद! – lolo

+0

काम नहीं कर रहा है। अपडेट – lolo

+0

वापस नहीं चलाया गया है आप कक्षा (मेरे उदाहरण MyFacade में) इंजेक्शन कर रहे हैं, है ना? अन्यथा विशेषता सम्मानित नहीं की जाएगी। शायद आप एक बहुत ही सरल उदाहरण बना सकते हैं और सभी वर्गों को पोस्ट कर सकते हैं। फिर यह देखना आसान है कि और क्या गलत हो रहा है। – Guenther

0

इस प्रयास करें पा सकते हैं: एक प्रॉक्सी जो बारी में एक सौदे में @Transactional साथ एनोटेट तरीकों लपेटता में अपनी वस्तु लपेटकर द्वारा वसंत पर काम चल रहा

@TransactionManagement(TransactionManagementType.BEAN) 
public class MyFacade { 

@TransactionAttribute(TransactionAttribute.REQUIRES_NEW) 
public void databaseChanges() throws Exception { 
    A(); //update. 
    B(); //insert 
} 
+0

पर एनोटेशन कोई स्पष्टीकरण क्यों यह सही जवाब होना चाहिए महान होगा! – Patrick

+0

मैंने कोशिश की, प्राप्त: त्रुटि संकलित करें: लेनदेन प्रबंधन को किसी प्रकार से हल नहीं किया जा सकता है। मेरी कक्षा @Service के साथ एनोटेटेड है। – lolo

14

@Transactional एनोटेशन। उस एनोटेशन के कारण निजी तरीकों (जैसे आपके उदाहरण में) पर काम नहीं करेगा क्योंकि निजी विधियों को विरासत में नहीं मिला => उन्हें लपेटा नहीं जा सकता है (यदि आप aspectj के साथ घोषणात्मक लेनदेन का उपयोग करते हैं तो यह सच नहीं है, फिर प्रॉक्सी- नीचे संबंधित चेतावनी लागू नहीं होती हैं)।

यहां बताया गया है कि कैसे @Transactional वसंत जादू कार्य करता है।

आप ने लिखा है:

class A { 
    @Transactional 
    public void method() { 
    } 
} 

लेकिन यह क्या आप वास्तव में जब आप एक सेम इंजेक्षन मिलता है:

class ProxiedA extends A { 
    private final A a; 

    public ProxiedA(A a) { 
     this.a = a; 
    } 

    @Override 
    public void method() { 
     try { 
      // open transaction ... 
      a.method(); 
      // commit transaction 
     } catch (RuntimeException e) { 
      // rollback transaction 
     } catch (Exception e) { 
      // commit transaction 
     } 
    } 
} 

यह सीमाएँ हैं। वे @PostConstruct विधियों के साथ काम नहीं करते हैं क्योंकि ऑब्जेक्ट प्रॉक्सी होने से पहले उन्हें बुलाया जाता है। और यहां तक ​​कि यदि आपने सभी सही तरीके से कॉन्फ़िगर किया है, तो लेन-देन केवल पर अनदेखा अपवाद डिफ़ॉल्ट रूप से वापस ले जाया जाता है। यदि आपको कुछ चेक अपवाद पर रोलबैक की आवश्यकता है तो @Transactional(rollbackFor={CustomCheckedException.class}) का उपयोग करें।

एक और अक्सर सामना करना पड़ा चेतावनी मुझे पता है:

@Transactional विधि केवल काम करेंगे अगर आप इसे "बाहर से" कहते हैं, उदाहरण के b() निम्नलिखित में लेन-देन में लिपटे नहीं किया जाएगा:

class X { 
    public void a() { 
     b(); 
    } 

    @Transactional 
    public void b() { 
    } 
} 

यह भी है क्योंकि @Transactional आपकी ऑब्जेक्ट को प्रॉक्सी करके काम करता है। उदाहरण में a()X.b() को "स्प्रिंग प्रॉक्सी" विधि b() पर कॉल नहीं करेगा, इसलिए कोई लेनदेन नहीं होगा। एक कामकाज के रूप में आपको किसी अन्य बीन से b() पर कॉल करना होगा।

जब आप इन चेतावनियां के किसी भी सामना करना पड़ा है और एक सुझाव दिया समाधान का (विधि गैर-निजी बनाने के लिए या किसी अन्य सेम से b() कहते हैं) आप TransactionTemplate कथात्मक लेनदेन के बजाय का उपयोग कर सकते का उपयोग नहीं कर सकते हैं:

public class A { 
    @Autowired 
    TransactionTemplate transactionTemplate; 

    public void method() { 
     transactionTemplate.execute(status -> { 
      A(); 
      B(); 
      return null; 
     }); 
    } 

... 
} 

अद्यतन

उपरोक्त जानकारी का उपयोग कर ओपी अद्यतन प्रश्न का उत्तर देना।

Which method should be annotated with @Transactional: changes()? databaseChanges()?

@Transactional(rollbackFor={Exception.class}) 
public void changes() throws Exception { 
    someLogicBefore(); 
    databaseChanges(); 
    someLogicAfter(); 
} 

यकीन changes() एक सेम के "बाहर से" कहा जाता है, वर्ग से ही नहीं और बाद संदर्भ instantiated थी (उदा इस afterPropertiesSet() या @PostConstruct एनोटेट तरीका नहीं है) बनाओ। डिफ़ॉल्ट रूप से अनचेक अपवादों के लिए बस वसंत रोलबैक लेनदेन को समझें (रोलबैक में अधिक विशिष्ट होने की कोशिश करें अपवाद सूची के लिए)।

0

जो आप खो रहे हैं वह एक TransactionManager है। TransactionManager का उद्देश्य डेटाबेस लेनदेन प्रबंधित करने में सक्षम होना है। 2 प्रकार के लेनदेन, प्रोग्रामेटिक और घोषणात्मक हैं। जो आप वर्णन कर रहे हैं वह एनोटेशन के माध्यम से एक घोषणात्मक लेनदेन की आवश्यकता है।

वसंत लेनदेन निर्भरता (उदाहरण के रूप में Gradle का उपयोग करना)

compile("org.springframework:spring-tx") 

स्प्रिंग बूट विन्यास में कोई लेन-देन प्रबंधक परिभाषित करें:

तो क्या आप अपनी परियोजना के लिए जगह में होने की जरूरत है निम्नलिखित है

कुछ इस तरह की

@Bean 
public PlatformTransactionManager transactionManager(DataSource dataSource) 
{ 
    return new DataSourceTransactionManager(dataSource); 
} 

आपको @EnableTransactionManagement एनोटेशन जोड़ने की भी आवश्यकता होगी (सुनिश्चित नहीं है कि यह वसंत बूट के नए संस्करणों में निःशुल्क है या नहीं।

@EnableTransactionManagement 
public class AppConfig { 
... 
} 

जोड़े @Transactional

यहाँ आप है कि आप लेन-देन में भाग लेने के

@Transactional 
public void book(String... persons) { 
    for (String person : persons) { 
     log.info("Booking " + person + " in a seat..."); 
     jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person); 
    } 
}; 

ध्यान दें कि यह विधि सार्वजनिक किया जाना चाहिए चाहते हैं विधि के लिए @Transactional एनोटेशन जोड़ना होगा और निजी नहीं है। आप databaseChanges() पर कॉल करने वाली सार्वजनिक विधि पर @Transactional डालने पर विचार करना चाह सकते हैं।

, भी कर रहे हैं के बारे में जहां @Transactional जाना चाहिए और यह कैसे बर्ताव करता है उन्नत विषयों इन सब के बाद इतना बेहतर काम कर पहले कुछ पाने और उसके बाद इस क्षेत्र थोड़ी देर बाद :)

पता लगाने के लिए जगह में (निर्भरता + transactionManager हैं विन्यास + एनोटेशन), तो लेनदेन तदनुसार काम करना चाहिए।

संदर्भ

Spring Reference Documentation on Transactions

Spring Guide for Transactions using Spring Boot - यह नमूना कोड है कि आप के साथ

2

Any RuntimeException triggers rollback, and any checked Exception does not.

यह सब वसंत लेन-देन एपीआई भर में सामान्य व्यवहार है खेल सकते हैं। डिफ़ॉल्ट रूप से, यदि RuntimeException को अंतर्राष्ट्रीय कोड के भीतर से फेंक दिया गया है, तो लेनदेन वापस लुढ़का जाएगा। यदि एक चेक अपवाद (यानी RuntimeException नहीं) फेंक दिया जाता है, तो लेनदेन वापस नहीं किया जाएगा।

यह निर्भर करता है कि आप डेटाबेस चेंज फ़ंक्शन के अंदर कौन से अपवाद प्राप्त कर रहे हैं। तो आदेश सभी अपवादों तुम सब करने की जरूरत है rollbackFor = Exception.class

परिवर्तन सेवा वर्ग पर होना चाहिए जोड़ना है पकड़ने के लिए है, कोड है कि तरह होगा:

@Service 
public class SomeService implements ISomeService { 
    @Autowired 
    private NamedParameterJdbcTemplate jdbcTemplate; 
    @Autowired 
    private NamedParameterJdbcTemplate npjt; 

    @Transactional(rollbackFor = Exception.class) 
    private void databaseChanges() throws Exception { 
     A(); //update 
     B(); //insert 
    } 
} 

इसके अलावा आप क्या कर सकते हैं इसके साथ कुछ अच्छा है इसलिए आपको हर बार rollbackFor = Exception.class लिखना होगा। आप अपने स्वयं के कस्टम एनोटेशन लिख कर कि प्राप्त कर सकते हैं:

@Transactional(propagation=Propagation.REQUIRES_NEW, rollbackFor = {Exception.class}) 
public void databaseChanges() throws Exception { 
    A(); //update. 
    B(); //insert 
} 

@Transactional(propagation=Propagation.REQUIRED, rollbackFor = {Exception.class}) 
public void A() throws Exception { 
// update 
} 

@Transactional(propagation=Propagation.REQUIRED, rollbackFor = {Exception.class}) 
public void B() throws Exception { 
// insert 
} 

निर्दिष्ट प्रचार:

@Service 
public class SomeService implements ISomeService { 
    @Autowired 
    private NamedParameterJdbcTemplate jdbcTemplate; 
    @Autowired 
    private NamedParameterJdbcTemplate npjt; 

    @CustomTransactional 
    private void databaseChanges() throws Exception { 
     A(); //update 
     B(); //insert 
    } 
} 
0

आपको क्या करना होगा कुछ इस तरह है:

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) 
@Retention(RetentionPolicy.RUNTIME) 
@Transactional(rollbackFor = Exception.class) 
@Documented 
public @interface CustomTransactional { 
} 

अंतिम कोड कि तरह होगा REQUIRES_NEW सुनिश्चित करता है कि databaseChanges() विधि और A() और B() भाग लेने के लिए एक नया लेनदेन शुरू किया गया है उनके प्रसार के साथ उसी लेनदेन में REQUIRED के रूप में निर्दिष्ट किया गया है।

आपको यह सुनिश्चित करने की ज़रूरत है कि @Transactional एनोटेशन के साथ आप जिन विधियों को एनोटेट कर रहे हैं वे सार्वजनिक हैं क्योंकि लेनदेन संबंधी सलाह केवल सार्वजनिक विधियों पर लागू होती है। नहीं एक त्रुटि फेंकने के रूप में एनोटेटेड एक निजी विधि होगी लेकिन NEITHER लेनदेन संबंधी व्यवहार प्रदर्शित करेगा।

अब, जब B() में डालने के दौरान अपवाद होगा, लेनदेन प्रबंधक आंतरिक रूप से रोल-बैक नियमों (जो रोलबैक द्वारा निर्दिष्ट हैं) के लिए जांच करता है; यह Exception.class पाता है और लेन-देन को चिह्नित करता है (डेटाबेस चेंज() पर रोलबैक के रूप में शुरू होता है और इसके साथ ए() को वापस ले जाएगा क्योंकि ए() उसी लेनदेन में भाग ले रहा है क्योंकि

यदि यह आपके हल नहीं करता है मुद्दा, springframework ट्रेस लॉग सक्षम करें और मुझे वह प्रदान करते हैं। सक्षम होने पर इन लॉग में लेनदेन सीमाएं और घटनाएं ठीक से लॉग होती हैं।

यदि आपको उचित लेनदेन लॉगिंग नहीं दिखाई देती है, तो जांचें कि क्या आपके आवेदन पर लेनदेन प्रबंधन वास्तव में सक्षम है या नहीं। कॉन्फ़िगरेशन क्लास पर @EnableTransactionManagement जोड़ना इसे सक्षम बनाता है।

लिंक:
Propagation
RollbackFor
Transactional on private methods
@EnableTransactionManagement

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