Yalnızca geri alma olarak işaretlenen işlem: Nedenini nasıl bulabilirim


97

@Transactional yöntemimle bir işlem gerçekleştirme konusunda sorun yaşıyorum:

methodA() {
    methodB()
}

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

MethodA () 'dan methodB ()' yi çağırdığımda, metot başarılı bir şekilde geçiyor ve günlüklerimde "Tamam" görüyorum. Ama sonra anladım

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. Yöntem B'nin bağlamı istisnada tamamen eksik - sanırım sorun yok?
  2. MethodB () içindeki bir şey, işlemi yalnızca geri alma olarak mı işaretledi? Nasıl bulabilirim? Örneğin, böyle bir şeyi kontrol etmenin bir yolu var mı getCurrentTransaction().isRollbackOnly()?- bunun gibi, yöntemi adım adım atabilir ve sebebini bulabilirim.



Dikkat edilmesi gereken ilginç şeyler, veritabanı tablonuz yoksa bazen bu hata da gösterilecektir.
Ng Sek Long

Yanıtlar:


102

Yönteminizi olarak işaretlediğinizde, yönteminizin @Transactionaliçinde herhangi bir istisnanın meydana gelmesi, çevreleyen TX'i yalnızca geri alma olarak işaretleyecektir (onları yakalasanız bile). Aşağıdakiler @Transactionalgibi geri dönmesini önlemek için ek açıklamanın diğer özelliklerini kullanabilirsiniz :

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

6
Kullanmaya çalıştım noRollbackFor=Exception.class, ama etkisi yok gibi görünüyor - kalıtımsal istisnalar için çalışıyor mu?
Vojtěch

6
Evet öyle. Kendi cevabınıza baktığınızda, bu doğru ( methodCilk gönderinizde belirtmemişsiniz ). Hem methodBve methodCaynı TX kullanmak ve her zaman en özel @Transactional, bu nedenle zaman ek açıklama kullanılır methodCTX geri alma okunur olarak işaretlenir çevreleyen, istisna atar. Bunu önlemek için farklı yayılma işaretçileri de kullanabilirsiniz.
Ean V

1
@lolotron @Ean Bunun gerçekten salt okunur bir işlem için geçerli olacağını doğrulayabilirim. Yöntemim EmptyResultDataAccessExceptionsalt okunur bir işlemde bir istisna atıyordu ve ben de aynı hatayı aldım. Ek açıklamamı @Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)sorunu düzeltmek için değiştiriyorum .
cbmeeks

5
Bu cevap yanlış. Spring, yalnızca @Transactionalproxy paketleyiciden geçen, yani yakalanmayan istisnaları bilir . Hikayenin tamamı için diğer Vojt fromch yanıtına bakın. @TransactionalYalnızca işlem geri dönüşünüzü işaretleyebilecek iç içe geçmiş yöntemler olabilir.
Yaroslav Stavnichiy

1
noRollbackForsadece eğer çalışırglobalRollbackOnParticipationFailure=false
amir110

71

Sonunda sorunu anladım:

methodA() {
    methodB()
}

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

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

Olan şu ki methodB, doğru ek açıklamaya sahip olsa bile , methodCyok. İstisna atıldığında, ikincisi @Transactionalilk işlemi yine de yalnızca Geri Alma olarak işaretler.


6
İşlemin durumu bir iş parçacığı yerel değişkeninde saklanır. Yay yöntemi C'yi kestiği ve bayrağı geri alma olarak ayarladığında, işleminiz geri alma için zaten işaretlenmiştir. İstisnanın daha fazla bastırılması yardımcı olmayacaktır çünkü son kesinleştirme gerçekleştiğinde, hatayı alacaksınız
yaşıyor

@ Vojtěch Varsayımsal olarak, eğer methodC varsa, propagation=requires_newo zaman methodB geri gelmez mi?
deFreitas

4
methodCfarklı Spring bean / hizmetinde olmalı veya bir şekilde Spring proxy aracılığıyla erişilmelidir. Aksi takdirde, Bahar'ın istisnanızı bilmesine imkân olmayacaktır. Yalnızca @Transactionalek açıklamadan geçen istisna, işlemi yalnızca geri alma olarak işaretleyebilir.
Yaroslav Stavnichiy

Bu bir çözüm değil. Bu sadece Spring Transaction AOP mekanizmasının yanlış anlaşılması ve yanlış kullanımı ile ilgilidir. İşlem açıklamasını yalnızca, o yerde uyguladığınız eylemler için ayrı İşlem bağlamına veya yayılmaya ihtiyaç duyduğunuzdan eminseniz kullanmalısınız. Diğer herhangi bir durumda, İşlem Yayılımını doğru bir şekilde ayarlayabilirsiniz. -1
Mykyta Chelombitko

ilk işlemi işaretler . Birinci veya ikinci işlem yoktur - orada sadece bir işlem vardır. Çünkü varsayılan olarak @Transactionalyayılma REQUIRED("varsa mevcut işlem içinde çalıştır" olarak kabul edilir)
Lu55

44

Yeniden kodlamaya veya yeniden oluşturmaya gerek kalmadan neden olan istisnayı hızlı bir şekilde getirmek için ,

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

ve yığında yukarı, genellikle bir Interceptor'a gidin. Orada bazı catch bloğundan neden olan istisnayı okuyabilirsiniz.


6
Hazırda Bekletme org.hibernate.jpa.internal.TransactionImpl
4.3.11'de

Çok iyi dostum!
Rafael Andrade

Teşekkürler! Hibernate'in (5.4.17) daha yeni sürümlerinde, sınıf org.hibernate.engine.transaction.internal.TransactionImplve yöntemdir setRollbackOnly.
Peter Catalin

org.hibernate.jpa.internal.TransactionImpl.setRollbackOnlyHazırda Bekletme yöntemi
5.0.12

11

Uygulamamı çalıştırırken bu istisna ile mücadele ettim.

Sonunda sorun sql sorgusundaydı . demek istediğim sorgu yanlış.

lütfen sorgunuzu doğrulayın. Bu benim önerim


1
Açıklığa kavuşturmak için: eğer 1. sql sözdiziminizde bir hata varsa 2. istisna durumunda geri alınacak şekilde ayarlandıysanız 3. readOnly işlemleriniz bu hatayı alırsınız çünkü sql sözdizimi, içinde olduğunuz için başarısız olan bir geri dönüşü tetikleyen bir istisnaya neden olur " salt okunur "modu.
Dave

7

...Kodunuzun bölümlerinde atılan ve yakalanan istisnaları arayın . Çalışma zamanı ve geri alma uygulaması istisnaları, başka bir yerde yakalanmış olsa bile bir iş yönteminden atıldığında geri dönüşe neden olur.

İşlemin geri alma için işaretlenip işaretlenmediğini öğrenmek için bağlamı kullanabilirsiniz.

@Resource
private SessionContext context;

context.getRollbackOnly();

1
Bana öyle geliyor ki sebebini buldum, ama bunun neden olduğunu anlamıyorum. İçsel bir yöntem yakaladığım, kaydettiğim ve görmezden geldiğim bir istisna atıyor. Ancak İşlem yine de yalnızca Geri Alma olarak işaretlenir. Nasıl önleyebilirim? İşlemlerin, düzgün bir şekilde yakaladığım istisnalardan etkilenmesini istemiyorum.
Vojtěch

SessionContextBaharda bir standart sınıf? Bana öyle geliyor ki daha çok EJB3 ve Bahar Başvurumda yer almıyor.
Vojtěch

3
Benim hatam, Bahar ile ilgili olduğu gerçeğini kaçırdım. Her neyse, mevcut gibi bir şey olmalı TransactionAspectSupport.currentTransactionStatus().isRollbackOnly().
Mareen

5

Çözümlerle ilgili iyi bir açıklama buldum: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

1) gerçekten işlem kontrolü gerektirmiyorsa @Transacional'ı iç içe yerleştirilmiş yöntemden kaldırın. Bu yüzden istisnası olsa bile, sadece ortaya çıkıyor ve işlemsel şeyleri etkilemiyor.

VEYA:

2) iç içe geçmiş yöntem işlem kontrolüne ihtiyaç duyuyorsa, istisna atsa ve yalnızca geri alma olarak işaretlense bile, yayılma ilkesi için bunu REQUIRE_NEW yapın, arayan etkilenmez.


Teşekkür ederim! İç içe geçmiş yöntemde YENİ bir işlem gerektirmek sorunumun
çözümüydü

1

Bean.xml dosyanızda işlem yöneticisini devre dışı bırakın

<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>

bu satırları yorumlayın ve geri dönüşe neden olan istisnayı göreceksiniz;)


0

aşağıdaki kodu productRepository'ye uygulayın

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

Junit testinde aşağıdaki kodu uygulayın

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

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

}

kodum için iyi çalışıyor

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.