Transaksi hanya ditandai sebagai rollback: Bagaimana cara menemukan penyebabnya

95

Saya mengalami masalah dalam melakukan transaksi dalam metode @Transactional saya:

methodA() {
    methodB()
}

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

Ketika saya memanggil methodB () dari methodA (), metode tersebut berhasil lolos dan saya dapat melihat "OK" di log saya. Tapi kemudian saya mengerti

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. Konteks methodB benar-benar hilang dalam pengecualian - yang tidak masalah menurut saya?
  2. Sesuatu di dalam methodB () menandai transaksi sebagai rollback saja? Bagaimana saya bisa menemukannya? Apakah ada misalnya cara untuk memeriksa sesuatu seperti getCurrentTransaction().isRollbackOnly()?- seperti ini saya bisa melangkah melalui metode dan menemukan penyebabnya.
Vojtěch
sumber
terkait: stackoverflow.com/q/25322658/697313
Yaroslav Stavnichiy
Hal menarik yang perlu diperhatikan adalah, jika tabel database Anda tidak ada, terkadang error ini juga akan ditampilkan.
Ng Sek Long

Jawaban:

101

Saat Anda menandai metode Anda sebagai @Transactional, terjadinya pengecualian apa pun di dalam metode Anda akan menandai TX di sekitarnya hanya sebagai roll-back (bahkan jika Anda menangkapnya). Anda dapat menggunakan atribut @Transactionalanotasi lainnya untuk mencegahnya bergulir kembali seperti:

@Transactional(rollbackFor=MyException.class, noRollbackFor=MyException2.class)
Ean V
sumber
6
Ya, saya mencoba menggunakan noRollbackFor=Exception.class, tetapi tampaknya tidak berpengaruh - apakah berfungsi untuk pengecualian yang diwariskan?
Vojtěch
6
Ya, itu benar. Melihat jawaban Anda sendiri, itu benar (Anda belum memberikan methodCdi posting pertama Anda). Keduanya methodBdan methodCmenggunakan TX yang sama dan selalu menggunakan @Transactionalanotasi paling spesifik , jadi ketika methodCmelempar pengecualian, TX di sekitarnya akan ditandai sebagai rollback-only. Anda juga dapat menggunakan penanda propagasi yang berbeda untuk mencegahnya.
Ean V
1
@lolotron @Ean Saya dapat mengonfirmasi bahwa itu memang akan berlaku untuk transaksi hanya-baca. Metode saya memberikan EmptyResultDataAccessExceptionpengecualian pada transaksi hanya-baca dan saya mendapatkan kesalahan yang sama. Mengubah anotasi saya untuk @Transactional(readOnly = true, noRollbackFor = EmptyResultDataAccessException.class)memperbaiki masalah.
cbmeeks
5
Jawaban ini salah. Spring hanya mengetahui tentang pengecualian yang melewati @Transactionalpembungkus proxy, yaitu tidak tertangkap . Lihat jawaban lain dari Vojtěch untuk cerita lengkapnya. Mungkin ada @Transactionalmetode bertingkat yang dapat menandai transaksi Anda sebagai rollback-only.
Yaroslav Stavnichiy
1
noRollbackForbekerja hanya jikaglobalRollbackOnParticipationFailure=false
amir110
69

Saya akhirnya mengerti masalahnya:

methodA() {
    methodB()
}

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

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

Yang terjadi adalah meskipun methodBanotasi memiliki anotasi yang benar, methodCnamun tidak. Saat pengecualian dilemparkan, yang kedua @Transactionalmenandai transaksi pertama sebagai Rollback saja.

Vojtěch
sumber
5
Status transaksi disimpan dalam variabel lokal utas. Saat pegas memotong methodC dan menyetel bendera sebagai rollback, transaksi Anda sudah ditandai untuk rollback. Setiap lebih dari penindasan pengecualian tidak akan membantu karena ketika final komit terjadi, Anda akan mendapatkan error
tinggal
@ Vojtěch Dengan cara apapun secara hipotetis jika methodC memiliki propagation=requires_newmaka methodB tidak akan melakukan rollback?
deFreitas
4
methodCharus berada dalam Spring bean / service yang berbeda atau diakses melalui proxy Spring. Jika tidak, Spring tidak akan memiliki kemungkinan untuk mengetahui tentang pengecualian Anda. Hanya pengecualian yang melewati @Transactionalanotasi yang dapat menandai transaksi sebagai rollback-only.
Yaroslav Stavnichiy
Itu bukanlah solusi. Ini hanya tentang kesalahpahaman dan penggunaan yang salah dari mekanisme AOP Transaksi Musim Semi. Anda harus menggunakan anotasi Transaksional hanya jika Anda yakin Anda memerlukan konteks Transaksi atau penyebaran terpisah untuk tindakan yang Anda terapkan di tempat itu. Dalam kasus lain, Anda dapat mengatur Propagasi Transaksi dengan benar. -1
Mykyta Chelombitko
43

Untuk mengambil pengecualian penyebab dengan cepat tanpa perlu membuat kode ulang atau membuat ulang, aktifkan breakpoint

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

dan naik di tumpukan, biasanya ke beberapa Interceptor. Di sana Anda dapat membaca pengecualian penyebab dari beberapa blok tangkapan.

FelixJongleur42
sumber
6
Dalam Hibernate 4.3.11, ituorg.hibernate.jpa.internal.TransactionImpl
Wim Deblauwe
Sangat baik teman saya!
Rafael Andrade
Terima kasih! Dalam versi Hibernate (5.4.17) yang lebih baru, kelasnya adalah org.hibernate.engine.transaction.internal.TransactionImpldan metodenya adalah setRollbackOnly.
Peter Catalin
11

Saya berjuang dengan pengecualian ini saat menjalankan aplikasi saya.

Akhirnya masalahnya ada pada query sql . Maksud saya kueri itu salah.

harap verifikasi kueri Anda. Ini saran saya

Kumaresan Perumal
sumber
1
Untuk memperjelas: jika Anda 1. memiliki kesalahan dalam sintaks sql Anda 2. diatur untuk melakukan rollback pada pengecualian 3. memiliki readHanya transaksi Anda akan mendapatkan kesalahan ini karena sintaks sql menyebabkan pengecualian yang memicu rollback yang gagal karena Anda berada dalam " mode hanya baca.
Dave
7

Cari pengecualian yang dilempar dan ditangkap di ...bagian kode Anda. Pengecualian aplikasi runtime dan rollback menyebabkan rollback saat dikeluarkan dari metode bisnis bahkan jika tertangkap di beberapa tempat lain.

Anda dapat menggunakan konteks untuk mengetahui apakah transaksi ditandai untuk rollback.

@Resource
private SessionContext context;

context.getRollbackOnly();
Mareen
sumber
1
Sepertinya saya menemukan penyebabnya, tetapi saya tidak mengerti mengapa ini terjadi. Metode batin melempar pengecualian, yang saya tangkap, catat, dan abaikan. Tetapi Transaksi hanya ditandai sebagai Rollback saja. Bagaimana cara mencegahnya? Saya tidak ingin Transaksi dipengaruhi oleh pengecualian yang saya tangkap dengan benar.
Vojtěch
Apakah SessionContextkelas standar di Spring? Menurut saya ini agak EJB3 dan tidak terdapat dalam Aplikasi Musim Semi saya.
Vojtěch
3
Kesalahanku, aku merindukan fakta bahwa ini tentang Musim Semi. Pokoknya harus ada sesuatu seperti TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()tersedia.
Mareen
3

Menemukan penjelasan yang bagus dengan solusi: https://vcfvct.wordpress.com/2016/12/15/spring-nested-transactional-rollback-only/

1) Hapus @Transacional dari metode bersarang jika tidak benar-benar memerlukan kontrol transaksi. Jadi meski ada pengecualian, itu hanya menggelembung dan tidak memengaruhi hal-hal transaksional.

ATAU:

2) jika metode bersarang memang membutuhkan kontrol transaksi, jadikan sebagai REQUIRE_NEW untuk kebijakan penyebaran seperti itu bahkan jika melontarkan pengecualian dan ditandai sebagai rollback saja, pemanggil tidak akan terpengaruh.

aquajach
sumber
1

nonaktifkan transactionmanager di Bean.xml Anda

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

komentari baris ini, dan Anda akan melihat pengecualian yang menyebabkan rollback;)

rémy
sumber
0

terapkan kode di bawah ini di productRepository

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

sedangkan di tes junit terapkan kode di bawah ini

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

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

}

itu berfungsi dengan baik untuk kode saya

Asif Raza
sumber