केवल रोलबैक के रूप में चिह्नित लेन-देन: मुझे इसका कारण कैसे पता चलता है


92

मेरे पास मेरे @ ट्रांसेक्शनल विधि में लेनदेन करने के मुद्दे हैं:

methodA() {
    methodB()
}

@Transactional
methodB() {
    ...
    em.persist();
    ...
    em.flush();
    log("OK");
}

जब मैं मेथडब्लू () मेथड ए () से कहता हूं, तो विधि सफल हो जाती है और मैं अपने लॉग में "ओके" देख सकता हूं। लेकिन फिर मुझे मिलता है

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
    at methodA()...
  1. मेथडब का संदर्भ पूरी तरह से अपवाद में गायब है - जो ठीक है मुझे लगता है?
  2. मेथडब के भीतर कुछ () केवल रोलबैक के रूप में लेनदेन को चिह्नित करता है? मैं इसे कैसे पता लगा सकता हूं? उदाहरण के लिए कुछ की जाँच करने का एक तरीका है getCurrentTransaction().isRollbackOnly()?- जैसे मैं इस विधि के माध्यम से कदम उठा सकता हूं और इसका कारण जान सकता हूं।


संबं
धत लं क

ध्यान देने योग्य बातें यह है कि यदि आपकी डेटाबेस तालिका मौजूद नहीं है, तो कुछ समय बाद यह त्रुटि भी दिखाई देगी।
एनजी सेक लॉन्ग

जवाबों:


101

जब आप अपने तरीके को चिह्नित करते हैं @Transactional, तो आपके विधि के अंदर किसी भी अपवाद की घटना केवल रोल-बैक के रूप में आसपास के TX को चिह्नित करेगी (भले ही आप उन्हें पकड़ लें)। आप @Transactionalएनोटेशन की अन्य विशेषताओं का उपयोग कर सकते हैं जैसे कि इसे वापस रोल करने से रोकने के लिए:

@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)

6
खैर, मैंने उपयोग करने की कोशिश की noRollbackFor=Exception.class, लेकिन इसका कोई असर नहीं हुआ - क्या यह विरासत में मिले अपवादों के लिए काम करता है?
वोजटच

6
हाँ यह करता है। अपने स्वयं के उत्तर को देखते हुए, यह सही है (आपने methodCअपनी पहली पोस्ट में प्रदान नहीं किया था )। दोनों methodBऔर methodCएक ही TX का उपयोग करते हैं और हमेशा सबसे विशिष्ट @Transactionalएनोटेशन का उपयोग किया जाता है, इसलिए जब methodCअपवाद फेंकता है , तो आसपास के TX को केवल रोलबैक के रूप में चिह्नित किया जाएगा। इसे रोकने के लिए आप विभिन्न प्रचार मार्करों का भी उपयोग कर सकते हैं।
इयान वी

@ आपके पद्धति के अंदर कोई भी अपवाद आसपास के TX को रोल-बैक के रूप में चिह्नित करेगा केवल क्या यह आसानी से लेनदेन के लिए भी लागू होता है?
मार्को व्रांजकोविक

1
@ लोलोट्रोन @ इयान मैं पुष्टि कर सकता हूं कि यह वास्तव में केवल-पढ़ने के लिए लेनदेन पर लागू होगा। मेरा तरीका EmptyResultDataAccessExceptionकेवल-पढ़ने के लेन-देन पर एक अपवाद को फेंक रहा था और मुझे वही त्रुटि मिली। @Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)समस्या को ठीक करने के लिए मेरे एनोटेशन को बदलना ।
cbmeeks 17

5
यह उत्तर गलत है। वसंत केवल उन अपवादों के बारे में जानता है जो @Transactionalप्रॉक्सी आवरण से गुजरते हैं , अर्थात बिना पढ़े । पूरी कहानी के लिए Vojtěch के अन्य उत्तर देखें। नेस्टेड @Transactionalविधियाँ हो सकती हैं जो आपके लेन-देन रोलबैक को चिह्नित कर सकती हैं।
यारोस्लाव स्टावनीचिए

68

मैंने आखिर समस्या समझी:

methodA() {
    methodB()
}

@Transactional(noRollbackFor = Exception.class)
methodB() {
    ...
    try {
        methodC()
    } catch (...) {...}
    log("OK");
}

@Transactional
methodC() {
    throw new ...();
}

क्या होता है, भले ही methodBसही एनोटेशन हो, methodCनहीं। जब अपवाद को फेंक दिया जाता है, तो दूसरा @Transactionalलेन-देन रोलबैक के रूप में पहला लेनदेन करता है।


5
लेन-देन की स्थिति एक थ्रेड स्थानीय चर में संग्रहीत की जाती है। जब वसंत विधि को स्वीकार करता है और फ्लैग को रोलबैक के रूप में सेट करता है, तो आपका लेनदेन पहले से ही रोल बैक के लिए चिह्नित किया जाता है। अपवाद का कोई और दमन मदद नहीं करेगा क्योंकि जब अंतिम प्रतिबद्ध होता है, तो आपको त्रुटि मिलेगी
जीवन

@ VojtC किसी भी तरह से काल्पनिक रूप से अगर मेथड है propagation=requires_newतो मेथड रोलबैक नहीं होगा?
डेफ्रेइट्स

4
methodCअलग स्प्रिंग बीन / सेवा में होना चाहिए या किसी तरह स्प्रिंग प्रॉक्सी के माध्यम से एक्सेस किया जाना चाहिए। अन्यथा स्प्रिंग को आपके अपवाद के बारे में जानने की कोई संभावना नहीं है। केवल अपवाद जो @Transactionalएनोटेशन से गुजरता है, लेनदेन को केवल रोलबैक के रूप में चिह्नित कर सकता है।
यारोस्लाव स्टावनिचि

43

पुन: कोड या पुनर्निर्माण की आवश्यकता के बिना कारण अपवाद को जल्दी से लाने के लिए , एक ब्रेकपॉइंट सेट करें

org.hibernate.ejb.TransactionImpl.setRollbackOnly() // Hibernate < 4.3, or
org.hibernate.jpa.internal.TransactionImpl() // as of Hibernate 4.3

और स्टैक में ऊपर जाएं, आमतौर पर कुछ इंटरसेप्टर के लिए। वहां आप कुछ कैच ब्लॉक से कारण अपवाद पढ़ सकते हैं।


6
हाइबरनेट 4.3.11 में, यह हैorg.hibernate.jpa.internal.TransactionImpl
विम डेब्लाउवे

बहुत अच्छे मेरे मित्र!
राफेल एंड्रेड

धन्यवाद! हाइबरनेट (5.4.17) के नए संस्करणों में वर्ग है org.hibernate.engine.transaction.internal.TransactionImplऔर विधि है setRollbackOnly
पीटर कैटालिन

11

मैंने अपना आवेदन चलाते समय इस अपवाद से जूझता रहा।

अंत में समस्या sql क्वेरी पर थी । मेरा मतलब है कि क्वेरी गलत है।

कृपया अपनी क्वेरी सत्यापित करें। यह मेरा सुझाव है


1
स्पष्ट करने के लिए: यदि आपके 1. आपके sql सिंटैक्स में त्रुटि है 2. अपवाद पर रोलबैक करने के लिए सेटअप हैं। 3. आसानी से लेन-देन किया है तो आपको यह त्रुटि मिलेगी क्योंकि sql सिंटैक्स एक अपवाद का कारण बनता है जो रोलबैक को ट्रिगर करता है जो विफल रहता है क्योंकि आप "में हैं" आसानी से "मोड।
डेव

6

...अपने कोड के अनुभागों में फेंके और पकड़े जाने वाले अपवादों को देखें । किसी अन्य स्थान पर पकड़े जाने पर भी व्यावसायिक पद्धति से बाहर किए जाने पर रनटाइम और रोलबैक एप्लिकेशन अपवाद रोलबैक का कारण बनते हैं।

आप संदर्भ का उपयोग यह जानने के लिए कर सकते हैं कि क्या लेन-देन रोलबैक के लिए चिह्नित है।

@Resource
private SessionContext context;

context.getRollbackOnly();

1
ऐसा लगता है कि मुझे इसका कारण मिल गया है, लेकिन मुझे समझ नहीं आ रहा है कि ऐसा क्यों हो रहा है। एक आंतरिक विधि एक अपवाद फेंकता है, जिसे मैं पकड़ता हूं, लॉग करता हूं और अनदेखा करता हूं। लेकिन लेन-देन को वैसे भी केवल रोलबैक के रूप में चिह्नित किया जाता है। मैं यह कैसे रोक सकता हूँ? मैं नहीं चाहता कि लेन-देन अपवादों से प्रभावित हो जो मैं ठीक से पकड़ता हूं।
वोजत

है SessionContextवसंत में एक मानक वर्ग? ऐसा लगता है कि यह EJB3 है और यह मेरे स्प्रिंग एप्लिकेशन में निहित नहीं है।
वोजटच

3
मेरा बुरा मैं इस तथ्य से चूक गया कि यह वसंत के बारे में है। वैसे भी TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()उपलब्ध जैसा कुछ होना चाहिए ।
मारेन

2

समाधान के साथ एक अच्छी व्याख्या मिली: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

1) नेस्टेड विधि से @Transacional निकालें यदि इसे वास्तव में लेनदेन नियंत्रण की आवश्यकता नहीं है। तो यहां तक ​​कि इसका अपवाद भी है, यह सिर्फ बुलबुले बनाता है और लेन-देन के सामान को प्रभावित नहीं करता है।

या:

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


1

अपने Bean.xml में लेन-देन करने वाले को अक्षम करें

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

इन पंक्तियों पर टिप्पणी करें, और आपको रोलबैक के कारण अपवाद दिखाई देगा;)


0

ProductRepository में नीचे दिया गया कोड लागू करें

@Query("update Product set prodName=:name where prodId=:id ") @Transactional @Modifying int updateMyData(@Param("name")String name, @Param("id") Integer id);

जबकि जूनियर टेस्ट में कोड के नीचे आवेदन करना होता है

@Test
public void updateData()
{
  int i=productRepository.updateMyData("Iphone",102);

  System.out.println("successfully updated ... ");
  assertTrue(i!=0);

}

यह मेरे कोड के लिए ठीक काम कर रहा है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.