@Transactional
anotasi di pegas bekerja dengan membungkus objek Anda dalam proxy yang pada gilirannya membungkus metode yang dianotasi dengan @Transactional
dalam sebuah transaksi. Karena itu anotasi tidak akan berfungsi pada metode pribadi (seperti pada contoh Anda) karena metode pribadi tidak dapat diwariskan => mereka tidak dapat dibungkus (ini tidak benar jika Anda menggunakan transaksi deklaratif dengan aspekj, maka peringatan terkait proxy di bawah ini tidak berlaku).
Berikut adalah penjelasan dasar tentang cara @Transactional
keajaiban musim semi bekerja.
Anda menulis:
class A {
@Transactional
public void method() {
}
}
Tapi inilah yang sebenarnya Anda dapatkan saat menyuntikkan kacang:
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
}
}
}
Ini memiliki keterbatasan. Mereka tidak bekerja dengan @PostConstruct
metode karena mereka dipanggil sebelum objek diproksi. Dan bahkan jika Anda mengonfigurasi semuanya dengan benar, transaksi hanya dibatalkan pada tidak dicentang pengecualian secara default. Gunakan @Transactional(rollbackFor={CustomCheckedException.class})
jika Anda perlu rollback pada beberapa pengecualian yang diperiksa.
Peringatan lain yang sering ditemui yang saya tahu:
@Transactional
metode hanya akan berfungsi jika Anda menyebutnya "dari luar", dalam contoh berikut b()
tidak akan dibungkus dalam transaksi:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Itu juga karena @Transactional
bekerja dengan mem-proxy objek Anda. Pada contoh di atas a()
akan memanggil X.b()
bukan metode "proksi musim semi" yang disempurnakan b()
jadi tidak akan ada transaksi. Sebagai solusinya, Anda harus memanggil b()
dari kacang lain.
Saat Anda mengalami salah satu peringatan ini dan tidak dapat menggunakan solusi yang disarankan (jadikan metode non-pribadi atau panggil b()
dari kacang lain) Anda dapat menggunakan TransactionTemplate
alih-alih transaksi deklaratif:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Perbarui
Menjawab pertanyaan OP yang diperbarui menggunakan info di atas.
Metode mana yang harus dijelaskan dengan @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Pastikan changes()
disebut "dari luar" kacang, bukan dari kelas itu sendiri dan setelah konteks dipakai (mis. ini bukan afterPropertiesSet()
atau @PostConstruct
metode beranotasi). Pahami bahwa transaksi spring rollback hanya untuk pengecualian yang tidak dicentang secara default (coba lebih spesifik dalam daftar pengecualian rollbackFor yang dicentang).