Anotasi @Transactional. Bagaimana cara melakukan rollback?

91

Saya menggunakan anotasi ini dengan sukses untuk kelas Dao. Dan rollback berfungsi untuk pengujian.

Tapi sekarang saya perlu mengembalikan kode asli, bukan hanya tes. Ada penjelasan khusus untuk digunakan dalam tes. Tapi anotasi mana yang untuk kode non-tes? Ini pertanyaan besar bagi saya. Aku sudah menghabiskan satu hari untuk itu. Dokumentasi resmi tidak memenuhi kebutuhan saya.

class MyClass { // this does not make rollback! And record appears in DB.
        EmployeeDaoInterface employeeDao;

        public MyClass() {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    new String[] { "HibernateDaoBeans.xml" });
            employeeDao = (IEmployeeDao) context.getBean("employeeDao");
         }

        @Transactional(rollbackFor={Exception.class})
    public void doInsert( Employee newEmp ) throws Exception {
        employeeDao.insertEmployee(newEmp);
        throw new RuntimeException();
    }
}

pegawai Dao adalah

@Transactional
public class EmployeeDao implements IEmployeeDao {
    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void insertEmployee(Employee emp) {
        sessionFactory.getCurrentSession().save(emp);
    }
}

Dan berikut ini adalah tes yang anotasinya berfungsi dengan baik:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/HibernateDaoBeans.xml" })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
@Transactional
public class EmployeeDaoTest {

    @Autowired
    EmployeeDaoInterface empDao;

    @Test
    public void insert_record() {
       ...
       assertTrue(empDao.insertEmployee(newEmp));
    }

HibernateDaoBeans.xml

   ...
<bean id="employeeDao" class="Hibernate.EmployeeDao">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
    <tx:annotation-driven transaction-manager="txManager"/>

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
   ...



** YA, saya membatalkan transaksi. Saya baru saja menambahkan BEAN untuk layanan ... dan kemudian anotasi @Transactional mulai berfungsi :-) **

<bean id="service" class="main.MyClass">
    <property name="employeeDao" ref="employeeDao" />
</bean>

Terima kasih semuanya, Rusia tidak akan melupakan Anda!

Bocah Moskow
sumber

Jawaban:

163

Buang saja RuntimeExceptiondari metode yang ditandai sebagai @Transactional.

Secara default semua RuntimeExceptiontransaksi rollback sedangkan pengecualian yang dicentang tidak. Ini adalah warisan EJB. Anda dapat mengonfigurasinya dengan menggunakan rollbackFor()dan noRollbackFor()parameter anotasi:

@Transactional(rollbackFor=Exception.class)

Ini akan mengembalikan transaksi setelah mengeluarkan pengecualian apa pun .

Tomasz Nurkiewicz
sumber
1
Saya mencobanya. Tidak bekerja! Saya menceritakan tentang operasi Rollback tidak langsung di objek Dao, itu mungkin akan berhasil. Saya menceritakan tentang objek lain yang menggunakan urutan pemanggilan metode Dao yang menggunakan @Transactional. Dan saya coba tambahkan anotasi yang sama untuk kelasku yang menyebutnya Dao. Tapi itu tidak berhasil di sini.
Moscow Boy
5
Ini benar-benar berfungsi seperti ini :-). Jika Anda memiliki layanan yang memanggil beberapa DAO, layanan tersebut juga perlu memiliki @Transactionalanotasi. Jika tidak, setiap panggilan DAO dimulai dan melakukan transaksi baru sebelum Anda melempar pengecualian dalam layanan. Intinya adalah: pengecualian harus meninggalkan (melarikan diri) metode yang ditandai sebagai @Transactional.
Tomasz Nurkiewicz
Lihat kode yang ditambahkan di posting pertama. Ada bagian dari kode yang saya miliki. Dan setelah eksekusi saya memiliki catatan di DB. => rollback belum bekerja.
Bocah Moskow
Apakah melempar pengecualian dari DAO juga tidak membatalkan transaksi? Apakah Anda telah org.springframework.orm.hibernate3.HibernateTransactionManagermengonfigurasi dalam konteks Spring Anda? Jika Anda mengaktifkan org.springframework.transactionlogger, apakah itu menunjukkan sesuatu yang menarik?
Tomasz Nurkiewicz
1
Jika Anda menggunakan Spring Boot dan membiarkannya mengkonfigurasi DataSource secara otomatis, ia akan menggunakan HikariDataSource yang memiliki set komit otomatis ke true secara default. Anda perlu menyetelnya ke false: hikariDataSource.setAutoCommit (false); Saya membuat perubahan ini di kelas @Configuration saat mengkonfigurasi kacang DataSourceTransactionManager.
Alex
83

atau secara terprogram

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Stefan K.
sumber
12
Iya. Karena terkadang Anda perlu melakukan rollback tanpa menimbulkan error.
Erica Kane
2
Anda adalah penyelamat.
Dinding
Ini adalah cara yang lebih tepat untuk menjalankan rollback imo, idealnya Anda tidak ingin memberikan pengecualian untuk kondisi yang diketahui atau terkontrol.
jalpino
2
Terkadang tidak berhasil. Misalnya, dalam kasus saya, saya memiliki layanan transaksional dan repositori non-transaksional. Saya menyebut repositori dari layanan, jadi, itu harus berpartisipasi dalam transaksi yang dibuka oleh layanan. Tetapi jika saya memanggil kode Anda dari repositori non-transaksional saya, NoTransactionException akan dilontarkan terlepas dari kenyataan bahwa transaksi itu ada.
Victor Dombrovsky
3
transaksi mana yang ingin Anda putar kembali ke "penyimpanan non-transaksional"?
Stefan K.
5

Anda dapat membuat pengecualian yang tidak dicentang dari metode yang ingin Anda kembalikan. Ini akan terdeteksi pada musim semi dan transaksi Anda akan ditandai sebagai rollback saja.

Saya berasumsi Anda menggunakan Spring di sini. Dan saya berasumsi bahwa anotasi yang Anda rujuk dalam pengujian Anda adalah anotasi berbasis uji musim semi.

Cara yang disarankan untuk menunjukkan infrastruktur transaksi Spring Framework bahwa pekerjaan transaksi akan dibatalkan adalah dengan membuang Exception dari kode yang saat ini dijalankan dalam konteks transaksi.

dan perhatikan bahwa:

harap dicatat bahwa kode infrastruktur transaksi Spring Framework, secara default, hanya akan menandai transaksi untuk rollback dalam kasus runtime, pengecualian yang tidak dicentang; yaitu, ketika pengecualian yang ditampilkan adalah turunan atau subkelas dari RuntimeException.

Alex Barnes
sumber
1

Bagi saya rollbackFor tidak cukup, jadi saya harus meletakkan ini dan berfungsi seperti yang diharapkan:

@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)

Saya harap ini membantu :-)

Roberto Rodriguez
sumber
1
tidak, itu tidak membantu sama sekali di TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();bawah Anda membantu
Enerccio