Di mana anotasi @Transaksional berada?

528

Haruskah Anda menempatkan @Transactionaldi DAOkelas dan / atau metode mereka atau lebih baik untuk membubuhi keterangan kelas Layanan yang memanggil menggunakan objek DAO? Atau masuk akal untuk membubuhi keterangan kedua "lapisan"?

Thomas Einwaller
sumber

Jawaban:

537

Saya pikir transaksi milik pada lapisan Layanan. Itu yang tahu tentang unit kerja dan kasus penggunaan. Ini jawaban yang tepat jika Anda memiliki beberapa DAO yang disuntikkan ke dalam Layanan yang perlu bekerja sama dalam satu transaksi.

Duffymo
sumber
Saya setuju dengan itu. Kadang-kadang tidak masalah, tetapi kadang-kadang Anda dapat mengambil manfaat dari hal itu misalnya sesi Hibernasi direntang untuk transaksi sementara, jadi semua objek yang dimuat berada di cache tingkat 1 dan Anda tidak perlu memasang kembali objek ke sesi lagi, ditambah dengan malas dimuat fungsi berfungsi tanpa fuzz.
dma_k
5
Bisakah transaksi global terdiri dari lebih dari satu dari ini @Transaksional (propagasi = Propagasi.REQUIRED)? Atau @Transaksional selalu menjadi batasan untuk transaksi? Saya tidak yakin saya mendapatkannya dari dokumentasi, tetapi sepertinya Anda dapat membuat bahkan transaksi yang terdiri dari metode @Transaksional dan semua yang berjalan di dalamnya
lisak
1
Ya, saya pikir @Transactional terluar adalah yang menjadi batas transaksi jika Propagation.REQUIRED dihidupkan.
duffymo
309

Secara umum saya setuju dengan yang lain yang menyatakan bahwa transaksi biasanya dimulai pada tingkat layanan (tergantung pada rincian yang Anda butuhkan tentu saja).

Namun, sementara itu saya juga mulai menambahkan @Transactional(propagation = Propagation.MANDATORY)ke lapisan DAO saya (dan lapisan lain yang tidak diizinkan untuk memulai transaksi tetapi memerlukan yang sudah ada) karena jauh lebih mudah untuk mendeteksi kesalahan di mana Anda lupa memulai transaksi di pemanggil ( misalnya layanan). Jika DAO Anda dijelaskan dengan propagasi wajib Anda akan mendapatkan pengecualian yang menyatakan bahwa tidak ada transaksi aktif ketika metode dipanggil.

Saya juga memiliki tes integrasi di mana saya memeriksa semua kacang (prosesor pos kacang) untuk anotasi ini dan gagal jika ada @Transactionalanotasi dengan propagasi selain Wajib dalam kacang yang bukan milik lapisan layanan. Dengan cara ini saya memastikan kami tidak memulai transaksi pada lapisan yang salah.

mnp
sumber
3
Haruskah ini dilakukan di lapisan dao (antarmuka), di dao impl. lapisan atau keduanya?
Johan
3
Saya tidak tahu apakah itu cocok dengan diskusi ini, tetapi tip lain dapat ditambahkan @Transaksional (readOnly = true) di dao impl dalam operasi non-penulisan.
maxivis
10
@Johan Spring menyarankan untuk menempatkan anotasi Transaksi pada kelas implementasi alih-alih antarmuka.
Mathias G.
Saya sangat suka ide ini, mencoba itu untuk proyek saya juga
Thomas Einwaller
1
Biar saya mengerti apakah saya mengerti ... apakah Anda mengatakan saya harus memakai @Transactionalkelas implementasi Layanan, dan saya harus memakai implementasi kelas @Transactional(propagation = MANDATORY)DAO (repositori)?
John Henckel
93

Anotasi Transaksional harus ditempatkan di sekitar semua operasi yang tidak dapat dipisahkan.

Misalnya, panggilan Anda adalah "ubah kata sandi". Itu terdiri dari dua operasi

  1. Ubah kata sandi.
  2. Audit perubahannya.
  3. Email klien bahwa kata sandi telah berubah.

Jadi di atas, jika audit gagal, maka haruskah perubahan kata sandi juga gagal? Jika demikian, maka transaksi harus sekitar 1 dan 2 (demikian pada lapisan layanan). Jika email gagal (mungkin harus memiliki beberapa jenis gagal aman sehingga ini tidak akan gagal) maka haruskah mengembalikan perubahan kata sandi dan audit?

Ini adalah jenis pertanyaan yang perlu Anda tanyakan ketika memutuskan di mana harus meletakkan @Transactional.

Michael Wiles
sumber
41

Jawaban yang benar untuk arsitektur Spring tradisional adalah menempatkan semantik transaksional pada kelas layanan, untuk alasan yang telah dijelaskan orang lain.

Tren yang muncul di Spring adalah ke arah desain berbasis domain (DDD). Spring Roo mencontohkan tren dengan baik. Idenya adalah untuk membuat objek domain POJO jauh lebih kaya daripada yang ada pada arsitektur Spring khas (biasanya mereka anemia ), dan khususnya untuk menempatkan transaksi dan semantik bertahan pada objek domain itu sendiri. Dalam kasus di mana semua yang diperlukan adalah operasi CRUD sederhana, pengontrol web beroperasi langsung pada objek domain POJO (mereka berfungsi sebagai entitas dalam konteks ini), dan tidak ada tingkat layanan. Dalam kasus di mana ada semacam koordinasi yang diperlukan antara objek domain, Anda dapat memiliki pegangan layanan kacang itu, dengan@Transactionsesuai tradisi. Anda dapat mengatur propagasi transaksi pada objek domain ke sesuatu seperti REQUIREDsehingga objek domain menggunakan transaksi yang ada, seperti transaksi yang dimulai pada kacang layanan.

Secara teknis teknik ini memanfaatkan AspectJ dan <context:spring-configured />. Roo menggunakan definisi antar-tipe AspectJ untuk memisahkan entitas semantik (transaksi dan kegigihan) dari hal-hal objek domain (pada dasarnya bidang dan metode bisnis).

naXa
sumber
38

Kasus normal adalah memberi keterangan pada tingkat lapisan layanan, tetapi ini benar-benar tergantung pada kebutuhan Anda.

Membuat anotasi pada lapisan layanan akan menghasilkan transaksi yang lebih lama daripada membuat anotasi pada level DAO. Tergantung pada tingkat isolasi transaksi yang dapat menyebabkan masalah, karena transaksi bersamaan tidak akan melihat perubahan satu sama lain dalam misalnya. BACA ULANG.

Membuat anotasi pada DAO akan membuat transaksi sesingkat mungkin, dengan kelemahan yang memperlihatkan fungsi lapisan layanan Anda tidak akan dilakukan dalam satu transaksi (dapat dikembalikan).

Tidak masuk akal untuk membubuhi keterangan kedua lapisan jika mode propagasi diatur ke default.

hammarback
sumber
31

Saya meletakkannya @Transactionaldi @Servicelayer dan mengatur rollbackForpengecualian dan readOnlyuntuk mengoptimalkan transaksi lebih lanjut.

Secara default @Transactionalhanya akan mencari RuntimeException(Pengecualian tidak dicentang), dengan mengatur rollback ke Exception.class(Pengecualian Tercentang) itu akan mengembalikan untuk setiap pengecualian.

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

Lihat Pengecualian Dicentang vs. Tidak Dicentang .

pengguna2601995
sumber
17

Atau masuk akal untuk membubuhi keterangan kedua "lapisan"? - tidak masuk akal untuk membubuhi keterangan lapisan layanan dan lapisan dao - jika seseorang ingin memastikan bahwa metode DAO selalu disebut (diperbanyak) dari lapisan layanan dengan propagasi "wajib" dalam DAO. Ini akan memberikan beberapa batasan untuk metode DAO agar tidak dipanggil dari lapisan UI (atau pengontrol). Juga - ketika unit menguji lapisan DAO khususnya - memiliki catatan DAO juga akan memastikan diuji untuk fungsionalitas transaksional.

tweekran
sumber
Tidak akan melakukan ini menghasilkan transaksi bersarang? Dan semua masalah halus yang menyertainya?
HDave
11
Tidak, tidak ada transaksi bersarang di JPA. Menempatkan keduanya di keduanya akan baik-baik saja - jika Anda sudah melakukan transaksi ketika Anda menekan DAO, transaksi itu akan dilanjutkan.
fool4jesus
4
Transaksi bersarang dapat terjadi jika Anda menggunakan propagation=Propagation.REQUIRES_NEW. Kalau tidak untuk sebagian besar kasus, termasuk propogasi = wajib, DAO hanya akan berpartisipasi dalam transaksi yang sudah ada yang dimulai oleh lapisan layanan.
dan carter
15

Untuk Transaksi di tingkat basis data

sebagian besar saya gunakan @Transactionaldi DAO hanya pada level metode, jadi konfigurasi dapat secara khusus untuk metode / menggunakan default (diperlukan)

  1. Metode DAO yang mendapatkan pengambilan data (pilih ..) - tidak perlu @Transactional ini dapat menyebabkan beberapa overhead karena pencegat transaksi / dan proxy AOP yang perlu dieksekusi juga.

  2. Metode DAO yang melakukan insert / update akan mendapatkan @Transactional

blog yang sangat bagus di transkripsional

Untuk tingkat aplikasi -
Saya menggunakan transaksional untuk logika bisnis, saya ingin dapat mengembalikan jika terjadi kesalahan tak terduga

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}
lukass77
sumber
6
+1 artikel yang sangat bagus tentang TransactionalinJava
oak
11

Biasanya, seseorang harus melakukan transaksi pada lapisan layanan.

Tetapi seperti yang dinyatakan sebelumnya, atomicity dari operasi adalah apa yang memberi tahu kita di mana anotasi diperlukan. Jadi, jika Anda menggunakan kerangka kerja seperti Hibernate, di mana satu operasi "simpan / perbarui / hapus / modifikasi" pada objek memiliki potensi untuk memodifikasi beberapa baris dalam beberapa tabel (karena kaskade melalui grafik objek), dari Tentu saja harus ada manajemen transaksi pada metode DAO khusus ini.

yannick555
sumber
10

@TransactionalAnotasi harus ditempatkan di sekitar semua operasi yang tidak dapat dipisahkan. Menggunakan @Transactionalpropagasi transaksi ditangani secara otomatis. Dalam hal ini jika metode lain dipanggil dengan metode saat ini, maka metode itu akan memiliki opsi untuk bergabung dengan transaksi yang sedang berlangsung.

Jadi mari kita ambil contoh:

Kami memiliki 2 model yaitu Countrydan City. Pemetaan Relasional Countrydan Citymodel seperti seseorang Countrydapat memiliki beberapa Kota sehingga pemetaan seperti,

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Di sini Negara dipetakan ke beberapa kota dengan menjemputnya Lazily. Jadi, inilah peran @Transactinalketika kita mengambil objek Negara dari basis data maka kita akan mendapatkan semua data objek Negara tetapi tidak akan mendapatkan Kumpulan kota karena kita mengambil kota LAZILY.

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

Ketika kita ingin mengakses Set of Cities dari objek negara maka kita akan mendapatkan nilai null di Set itu karena objek Set dibuat hanya Set ini tidak menginisialisasi dengan data yang ada untuk mendapatkan nilai dari Set yang kita gunakan @Transactionalyaitu,

//with @Transactional
@Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
   Object object = country.getCities().size();   
}

Jadi pada dasarnya @TransactionalLayanan dapat melakukan beberapa panggilan dalam satu transaksi tanpa menutup koneksi dengan titik akhir.

Harshal Patil
sumber
2
sangat informatif, terima kasih! Hanya apa yang saya cari, penjelasan tentang apa yang @Transactionalsebenarnya
CybeX
6

Lebih baik memilikinya di lapisan layanan! Ini jelas dijelaskan pada salah satu artikel yang saya temui kemarin! Inilah tautan yang bisa Anda periksa!

jam matahari
sumber
5

masukkan deskripsi gambar di sini

The @Transactionalharus digunakan pada lapisan layanan karena mengandung logika bisnis. Lapisan DAO biasanya hanya memiliki operasi CRUD basis data.

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

Spring doc: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html

Alin
sumber
5

Lapisan layanan adalah tempat terbaik untuk menambahkan @Transactionalanotasi karena sebagian besar logika bisnis hadir di sini, ini berisi perilaku kasus penggunaan tingkat detail.

Misalkan kita menambahkannya ke DAO dan dari layanan kita memanggil 2 kelas DAO, satu gagal dan sukses lainnya, dalam hal ini jika @Transactionaltidak pada layanan satu DB akan melakukan dan lainnya akan rollback.

Karenanya rekomendasi saya adalah gunakan anotasi ini dengan bijak dan gunakan hanya pada lapisan Layanan.

Github project- java-algos

VishalTambe
sumber
Pengecualian seperti ObjectOptimisticLockingFailureException terjadi hanya setelah transaksi selesai. Jika Anda memiliki utas terpisah untuk operasi lain seperti layanan email, desain ini benar-benar gagal. Kami menderita sekarang. Hanya solusi kiri yang akan menjadi AOP.
Nabin Kumar Khatiwada
4

Pertama-tama mari kita tentukan di mana kita harus menggunakan transaksi ?

Saya pikir jawaban yang benar adalah - ketika kita perlu memastikan bahwa urutan tindakan akan diselesaikan bersama sebagai satu operasi atom atau tidak ada perubahan yang akan dilakukan bahkan jika salah satu tindakan gagal.

Ini adalah praktik terkenal untuk memasukkan logika bisnis ke dalam layanan. Jadi metode layanan dapat berisi tindakan berbeda yang harus dilakukan sebagai unit kerja logis tunggal. Jika demikian - maka metode tersebut harus ditandai sebagai Transaksional . Tentu saja, tidak setiap metode memerlukan batasan seperti itu, jadi Anda tidak perlu menandai seluruh layanan sebagai transaksional .

Dan bahkan lebih - jangan lupa untuk mempertimbangkan bahwa @Transactional jelas, dapat mengurangi kinerja metode. Untuk melihat seluruh gambar Anda harus mengetahui tingkat isolasi transaksi. Mengetahui hal itu dapat membantu Anda menghindari penggunaan @Transaksional di tempat yang belum tentu diperlukan.

smaiakov
sumber
3

Lebih baik menyimpan @Transaksional di lapisan tengah yang terpisah antara DAO dan Lapisan Layanan. Karena, rollback sangat penting, Anda dapat menempatkan semua manipulasi DB di lapisan tengah dan menulis logika bisnis di Service Layer. Lapisan tengah akan berinteraksi dengan lapisan DAO Anda.

Ini akan membantu Anda dalam banyak situasi seperti ObjectOptimisticLockingFailureException - Pengecualian ini terjadi hanya setelah Transaksi Anda selesai. Jadi, Anda tidak dapat menangkapnya di lapisan tengah tetapi Anda dapat menangkapnya di lapisan layanan Anda sekarang. Ini tidak akan mungkin terjadi jika Anda memiliki @Transactional di lapisan Layanan. Meskipun Anda dapat menangkap Controller tetapi Kontroler harus sebersih mungkin.

Jika Anda mengirim surat atau sms di utas terpisah setelah menyelesaikan semua opsi simpan, hapus, dan perbarui, Anda dapat melakukan ini dalam layanan setelah Transaksi selesai di lapisan tengah Anda. Sekali lagi, jika Anda menyebut @Transactional di lapisan layanan, email Anda akan masuk walaupun transaksi Anda gagal.

Jadi memiliki layer @Transaction tengah akan membantu membuat kode Anda lebih baik dan mudah ditangani. Jika tidak, Jika Anda menggunakan lapisan DAO, Anda mungkin tidak dapat mengembalikan semua operasi. Jika Anda menggunakan lapisan Layanan, Anda mungkin harus menggunakan AOP (Pemrograman Berorientasi Aspek) dalam kasus-kasus tertentu.

Nabin Kumar Khatiwada
sumber
2

Idealnya, lapisan Layanan (Manajer) mewakili logika bisnis Anda dan karenanya harus dijelaskan dengan. @TransactionalLapisan layanan dapat memanggil DAO yang berbeda untuk melakukan operasi DB. Mari kita asumsikan situasi di mana Anda memiliki sejumlah operasi DAO dalam metode layanan. Jika operasi DAO 1 Anda gagal, orang lain mungkin masih lulus dan Anda akan berakhir dengan kondisi DB yang tidak konsisten. Lapisan Layanan Annotating dapat menyelamatkan Anda dari situasi seperti itu.

salsinga
sumber
1

Saya lebih suka menggunakan @Transactionallapisan layanan di tingkat metode.

Spark-Beginner
sumber
1

@Transactionalmenggunakan lapisan layanan yang disebut dengan menggunakan lapisan pengontrol ( @Controller) dan panggilan lapisan layanan ke lapisan DAO ( @Repository) yaitu operasi terkait basis data.

duduk
sumber