Saya tahu scala, sebagai bahasa fungsional, seharusnya bekerja secara berbeda dari bahasa OO umum, seperti Java, tapi saya yakin harus ada cara untuk menggabungkan sekelompok perubahan database dalam satu transaksi, memastikan atomisitas sebagai serta setiap properti ACID lainnya.
Seperti yang dijelaskan dalam dokumen apik (http://slick.lightbend.com/doc/3.1.0/dbio.html), DBIOAction memungkinkan pengelompokan operasi db dalam transaksi seperti ini:
val a = (for {
ns <- coffees.filter(_.name.startsWith("ESPRESSO")).map(_.name).result
_ <- DBIO.seq(ns.map(n => coffees.filter(_.name === n).delete): _*)
} yield ()).transactionally
val f: Future[Unit] = db.run(a)
Namun, kasus penggunaan saya (dan sebagian besar contoh dunia nyata yang dapat saya pikirkan), saya memiliki struktur kode dengan Pengontrol, yang memaparkan kode untuk titik akhir REST saya, pengontrol tersebut memanggil beberapa layanan dan setiap layanan akan mendelegasikan operasi basis data ke DAO.
Contoh kasar dari struktur kode saya yang biasa:
class UserController @Inject(userService: UserService) {
def register(userData: UserData) = {
userService.save(userData).map(result => Ok(result))
}
}
class UserService @Inject(userDao: UserDao, addressDao: AddressDao) {
def save(userData: UserData) = {
for {
savedUser <- userDao.save(userData.toUser)
savedAddress <- addressDao.save(userData.addressData.toAddress)
} yield savedUser.copy(address = savedAddress)
}
}
class SlickUserDao {
def save(user: User) = {
db.run((UserSchema.users returning UserSchema.users)).insertOrUpdate(user)
}
}
Ini adalah contoh sederhana, namun sebagian besar memiliki logika bisnis yang lebih kompleks di lapisan layanan.
Saya tidak ingin:
- DAO saya memiliki logika bisnis dan memutuskan operasi database mana yang akan dijalankan.
- Kembalikan DBAction dari DAO saya dan ekspos kelas persistensi. Hal ini sepenuhnya menggagalkan tujuan penggunaan DAO dan membuat pemfaktoran ulang lebih lanjut menjadi jauh lebih sulit.
Tapi saya pasti ingin transaksi di seluruh Pengontrol saya, untuk memastikan bahwa jika ada kode yang gagal, semua perubahan yang dilakukan dalam eksekusi metode itu akan dibatalkan.
Bagaimana saya bisa mengimplementasikan transaksionalitas pengontrol penuh dengan Slick di aplikasi Scala Play? Sepertinya saya tidak dapat menemukan dokumentasi apa pun tentang cara melakukan itu.
Juga, bagaimana saya bisa menonaktifkan komit otomatis di slick? Saya yakin ada jalan dan saya hanya melewatkan sesuatu.
Sunting:
Jadi membaca lebih banyak tentang hal itu, saya merasa sekarang saya lebih memahami bagaimana apik menggunakan koneksi ke database dan sesi. Ini sangat membantu: http://tastefulcode.com/2015/03/19/modern-database-access-scala-slick/.
Apa yang saya lakukan adalah menulis di masa depan dan, berdasarkan artikel ini, tidak ada cara untuk menggunakan koneksi dan sesi yang sama untuk beberapa operasi semacam itu.
Masalahnya adalah: Saya benar-benar tidak bisa menggunakan komposisi lain. Saya memiliki banyak logika bisnis yang perlu dieksekusi di antara pertanyaan.
Saya kira saya dapat mengubah kode saya untuk memungkinkan saya menggunakan komposisi tindakan, tetapi seperti yang saya sebutkan sebelumnya, hal itu memaksa saya untuk mengkodekan logika bisnis saya dengan mempertimbangkan aspek-aspek seperti transaksionalitas. Itu seharusnya tidak terjadi. Ini mencemari kode bisnis dan membuat tes menulis menjadi lebih sulit.
Adakah solusi untuk masalah ini? Adakah proyek git di luar sana yang menyelesaikan masalah ini yang saya lewatkan? Atau, yang lebih drastis, adakah kerangka persistensi lain yang mendukung hal ini? Dari apa yang saya baca, Anorm mendukung ini dengan baik, tetapi saya mungkin salah memahaminya dan tidak ingin mengubah kerangka kerja untuk mengetahui bahwa ternyata tidak (seperti yang terjadi dengan Slick).