Di mana saya harus meletakkan anotasi @Transactional: pada definisi antarmuka atau pada kelas implementasi?

91

Pertanyaan dari judul dalam kode:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

vs.

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}
Roma
sumber

Jawaban:

121

Dari http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

Rekomendasi tim Spring adalah Anda hanya memberi anotasi pada kelas konkret dengan @Transactionalanotasi, bukan antarmuka anotasi. Anda pasti dapat menempatkan @Transactionalanotasi pada antarmuka (atau metode antarmuka), tetapi ini hanya akan berfungsi seperti yang Anda harapkan jika Anda menggunakan proxy berbasis antarmuka. Fakta bahwa anotasi tidak diwariskan berarti bahwa jika Anda menggunakan proxy berbasis kelas maka pengaturan transaksi tidak akan dikenali oleh infrastruktur proxy berbasis kelas dan objek tidak akan dibungkus dalam proxy transaksional (yang jelas akan buruk ) . Jadi tolong ikuti saran tim Spring dan hanya beri anotasi kelas konkret (dan metode kelas konkret) dengan @Transactionalanotasi.

Catatan: Karena mekanisme ini didasarkan pada proxy, hanya panggilan metode 'eksternal' yang masuk melalui proxy yang akan dicegat. Ini berarti bahwa 'self-invocation', yaitu metode di dalam objek target yang memanggil beberapa metode lain dari objek target, tidak akan mengarah ke transaksi aktual pada waktu proses meskipun metode yang dipanggil ditandai dengan @Transactional!

(Penekanan ditambahkan pada kalimat pertama, penekanan lain dari aslinya.)

Romain Hippeau
sumber
Big +1 BTW, saya memberanikan diri membuat kutipan tersebut, sehingga orang tidak bingung, dan menambahkan kembali huruf miring dan semacamnya dari aslinya.
TJ Crowder
Saya telah membaca pernyataan ini sebelumnya di Spring docu, tetapi saya masih tidak dapat memahami mengapa pengaturan transaksi tidak akan dikenali oleh infrastruktur proxy berbasis kelas ? Saya pribadi berpikir, ini hanya batasan implementasi Spring, bukan masalah infrastruktur proxy yang mendasarinya.
dma_k
Melihat sumber Spring seseorang dapat mengetahui bahwa JdkDynamicAopProxymelewati semua penasihat kacang pada setiap pemanggilan metode (lihat juga DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()), yang dalam kasus pengaturan transaksi deklaratif, termasuk BeanFactoryTransactionAttributeSourceAdvisor. Pada gilirannya TransactionAttributeSourcePointcut#matches()dipanggil, yang seharusnya mengumpulkan informasi transaksi-relatif. Itu melewati kelas target dan selalu dapat melintasi semua antarmuka yang diimplementasikan kelas ini. Sekarang: mengapa ini tidak dapat bekerja dengan andal?
dma_k
2
@dma_k - Waktu proses Java hanya memberikan akses langsung ke anotasi kelas yang diwarisi dari superclass, atau anotasi metode tanpa pewarisan sama sekali. Jadi Pointcut.matches () harus benar-benar melintasi semua antarmuka secara rekursif, menemukan metode yang diganti "nyata" dengan refleksi, menangani metode jembatan, kelebihan beban, varargs, generik, proxy, dan tentu saja melakukannya dengan cepat . Maka Anda harus berurusan dengan warisan berlian - manakah dari banyak @Transactionalanotasi yang mungkin diwariskan yang akan berlaku? Jadi, meskipun itu semua mungkin , saya tidak menyalahkan Spring. jira.springsource.org/browse/SPR-975
Peter Davis
9

Anda dapat meletakkannya di antarmuka tetapi diperingatkan bahwa transaksi mungkin tidak terjadi dalam beberapa kasus. Lihat tip kedua di Secion 10.5.6 dari Spring dokumen:

Spring merekomendasikan agar Anda hanya memberi anotasi kelas konkret (dan metode kelas konkret) dengan anotasi @Transactional, sebagai lawan dari antarmuka anotasi. Anda tentunya dapat menempatkan anotasi @Transactional pada sebuah antarmuka (atau metode antarmuka), tetapi ini hanya berfungsi seperti yang Anda harapkan jika Anda menggunakan proxy berbasis antarmuka. Fakta bahwa anotasi Java tidak diwarisi dari antarmuka berarti bahwa jika Anda menggunakan proxy berbasis kelas (proxy-target-class = "true") atau aspek berbasis tenun (mode = "aspectj"), maka pengaturan transaksinya adalah tidak dikenali oleh infrastruktur proksi dan tenun, dan objek tidak akan dibungkus dalam proksi transaksional, yang jelas-jelas buruk.

Saya akan merekomendasikan menempatkan mereka pada implementasi karena alasan ini.

Juga, bagi saya, transaksi tampak seperti detail implementasi sehingga seharusnya ada di kelas implementasi. Bayangkan memiliki implementasi wrapper untuk logging atau implementasi uji (tiruan) yang tidak perlu transaksional.

AngerClown
sumber
9

Rekomendasi Spring adalah Anda menganotasi implementasi konkret alih-alih antarmuka. Tidak salah menggunakan anotasi pada sebuah antarmuka, itu hanya mungkin untuk menyalahgunakan fitur itu dan secara tidak sengaja mengabaikan deklarasi @Transaction Anda.

Jika Anda telah menandai sesuatu yang transaksional dalam sebuah antarmuka dan kemudian merujuk ke salah satu kelas implementasinya di tempat lain di musim semi, tidak terlalu jelas bahwa objek yang dibuat spring tidak akan menghormati anotasi @Transactional.

Dalam praktiknya terlihat seperti ini:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}
zmf
sumber
1

Mendukung @Transactional pada kelas beton:

Saya lebih suka merancang solusi dalam 3 bagian secara umum: API, Implementasi, dan Web (jika diperlukan). Saya mencoba yang terbaik untuk menjaga API seringan / sederhana / POJO mungkin dengan meminimalkan ketergantungan. Ini sangat penting jika Anda memainkannya di lingkungan terdistribusi / terintegrasi di mana Anda harus banyak berbagi API.

Menempatkan @Transactional membutuhkan pustaka Spring di bagian API, yang IMHO tidak efektif. Jadi saya lebih suka menambahkannya dalam Implementasi tempat transaksi berjalan.

Firdous Amir
sumber
0

Menempatkannya di antarmuka baik-baik saja selama semua pelaksana IFC Anda yang dapat diprediksi peduli tentang data TX (transaksi bukan masalah yang hanya ditangani oleh database). Jika metode tidak peduli tentang TX (tetapi Anda harus meletakkannya di sana untuk Hibernate atau apa pun), letakkan di impl.

Selain itu, mungkin sedikit lebih baik untuk menempatkan @Transactionalmetode di antarmuka:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
engfer
sumber