Koneksi basis data - haruskah mereka dilewatkan sebagai parameter?

11

Kami memiliki sistem di mana koneksi basis data dapat digunakan sekali menggunakan metode umum, dan dilewatkan di seluruh kelas yang relevan untuk digunakan. Ada keraguan bahwa melewatkan koneksi database sebagai parameter ke kelas yang berbeda akan menyebabkan masalah, jadi saya memeriksa di sini untuk melihat apakah ini benar-benar layak, dan apakah ada pola yang lebih baik untuk melakukannya?

Saya tahu ada beberapa alat ORM untuk melakukan kegigihan tetapi kita belum bisa membahasnya, belum ..

Setiap umpan balik disambut, terima kasih.

ipohfly
sumber
Masalah apa yang Anda maksudkan? Siapa yang memiliki keraguan ini? (Bukan Anda, saya kira.)
Greg Hewgill
1
Masalah seperti menyebabkan pengembang lupa untuk menutup koneksi, umumnya saya hanya mencoba untuk melihat apakah itu praktik yang baik untuk meneruskan koneksi database ke berbagai metode sebagai parameter. Ya keraguan berasal dari pengembang lain.
ipohfly

Jawaban:

8

Ya itu aman untuk melewati koneksi. Anda menangani koneksi di blok kontrol luar. Tidak ada yang tidak aman tentang itu.

Apa yang tidak aman adalah menulis kode yang tidak menjamin koneksi dibuang dengan benar tepat waktu. Lupa membersihkan sumber daya tidak ada hubungannya dengan menyebarkannya. Anda bisa dengan mudah menulis kode yang meninggalkan koneksi yang menggantung tanpa melewati di mana saja.

Di C ++, Anda dilindungi oleh RAII jika Anda mengalokasikan pada stack atau menggunakan smart pointer. Dalam C # buat aturan keras bahwa semua objek pakai (seperti koneksi) dideklarasikan di blok "using". Di Jawa bersih dengan logika try-akhirnya. Memiliki ulasan kode pada semua kode lapisan data untuk memastikan hal ini.

Kasus penggunaan yang paling umum adalah ketika Anda memiliki beberapa operasi yang dapat digabungkan dalam banyak permutasi. Dan masing-masing permutasi ini harus berupa transaksi atom (semua berhasil atau kembalikan). maka Anda harus melewati transaksi (dan karena itu koneksi yang sesuai) berkeliling ke semua metode.

Misalkan kita memiliki banyak tindakan foobar () yang dapat digabungkan dalam berbagai cara sebagai transaksi atom.

//example in C#
//outer controlling block handles clean up via scoping with "using" blocks.
using (IDbConnection conn = getConn())
{
    conn.Open();
    using (IDbTransaction tran = conn.BeginTransaction())
    {
        try
        {//inner foobar actions just do their thing. They don't need to clean up.
            foobar1(tran);
            foobar2(tran);
            foobar3(tran);
            tran.Commit();
        }
        catch (Exception ex)
        { tran.Rollback(); }
    }
}//connection is returned to the pool automatically

BTW Anda akan ingin membuka koneksi selambat mungkin, buang mereka sesegera mungkin. Rekan satu tim Anda mungkin benar jika Anda memperlakukan koneksi sebagai anggota objek, memperkenalkan mereka sebagai keadaan yang tidak perlu, dan membiarkan koneksi terbuka lebih lama dari yang diperlukan. Tetapi tindakan melewati koneksi atau transaksi sebagai parameter tidak salah secara inheren.

BTW. Bergantung pada dukungan bahasa Anda untuk fungsi kelas satu Anda dapat mengambil dalam daftar tindakan foobar (). Jadi satu fungsi bisa menangani semua permutasi dari tindakan. Menghilangkan duplikasi blok kontrol luar untuk setiap permutasi.

mike30
sumber
menandai ini sebagai jawaban karena itu memberi saya lebih banyak ide tentang bagaimana situasinya
ipohfly
6

Sepertinya Anda mencari Dependency Injection . Artinya, koneksi gabungan akan dibuat sekali dan disuntikkan di mana pun dibutuhkan. Tentu saja meneruskan koneksi melalui parameter metode adalah salah satu cara untuk menyuntikkan ketergantungan, tetapi wadah IoC seperti Guice, PicoContainer atau Spring adalah cara lain (lebih aman) yang dapat Anda lakukan.

Menggunakan DI berarti Anda dapat dengan rapi membungkus logika seputar penciptaan, pembukaan, penggunaan, dan penutupan koneksi - jauh dari logika bisnis inti Anda.

Spring JDBC et al adalah contoh lain dari melakukan perilaku semacam ini untuk Anda

Martijn Verburg
sumber
Emm tidak benar-benar melihat injeksi ketergantungan. Hanya mencoba mencari tahu apakah umumnya itu praktik yang baik untuk melakukan itu, dan jika tidak, apa cara yang lebih baik untuk mengelola koneksi basis data (DI adalah salah satu cara melakukannya).
ipohfly
-1. Satu koneksi tidak cocok dengan sistem multi-pengguna. Tampaknya berfungsi karena volume pengguna yang rendah dan eksekusi yang cepat. Dengan pooling, lebih baik untuk instantiate objek koneksi per tindakan bahkan dalam sistem pengguna tunggal.
mike30
2

Melewatkan hal-hal basis data daripada data hal-hal yang dapat menyebabkan masalah. Sejauh itu, kapan pun praktis, jangan lulus dari basis data kecuali seseorang dapat menjamin kebersihan basis data yang benar.

Masalah dengan melewati hal-hal basis data adalah bahwa hal itu dapat menjadi ceroboh. Saya telah melihat lebih dari satu bug dalam kode dengan seseorang yang melewati koneksi basis data, bahwa seseorang kemudian mengambil hasil yang diatur ke dan menyimpan di objek lokal (hasil set, masih terhubung ke database) dan kemudian mengikat kursor di database untuk waktu yang signifikan. Contoh lain seseorang melewati set hasil ke orang lain (yang kemudian disimpan) dan kemudian metode yang melewati set hasil menutupnya (dan pernyataan) mengarah ke kesalahan ketika metode lain mencoba untuk bekerja dengan set hasil yang tidak lagi.

Semua ini bermula dari tidak menghormati database, koneksi, pernyataan, set hasil, dan siklus hidup mereka.

Untuk menghindari hal ini, ada pola dan struktur yang ada yang bermain lebih baik dengan database dan tidak memiliki hal-hal database yang harus keluar dari kelas yang mereka kuasai. Data masuk, data keluar, basis data tetap ada.


sumber
1
Koneksi +1 db harus memiliki rentang waktu sesingkat mungkin. Buka, gunakan, tutup secepat mungkin. Saat ini ada banyak implementasi kumpulan koneksi sehingga menggunakan koneksi untuk beberapa operasi ekonomi palsu. Dan undangan untuk bug atau masalah kinerja (memegang kunci di atas meja, menggunakan sumber daya koneksi)
jqa
Apa nama dari beberapa pola dan struktur yang ada ini?
Daniel Kaplan
@tieTYT Yang utama yang datang ke garis depan adalah objek akses Data yang berfungsi untuk menyembunyikan database dari sisa aplikasi. Lihat juga Data Access Layer dan Object-Relational Mapping
Ketika saya memikirkan pola-pola itu saya merasa mereka berada pada tingkat abstraksi yang lebih tinggi daripada apa yang dia tanyakan. Katakanlah Anda punya cara untuk mendapatkan Rootdari seorang Dao. Tetapi kemudian Anda menyadari bahwa Anda juga menginginkan cara untuk mendapatkan Nodetanpa menarik seluruh Rootobjek dengannya. Bagaimana Anda membuat RootDao memanggil Nodekode Dao (yaitu: menggunakan kembali), tetapi pastikan NodeDao hanya menutup koneksi ketika NodeDao secara langsung dipanggil dan membuat koneksi tetap terbuka ketika RootDao dipanggil?
Daniel Kaplan
1
Hanya ingin menambahkan bahwa jika Anda tidak berada dalam mode komit otomatis, melewatkan koneksi di sekitar dapat menyebabkan situasi di mana satu objek memperbarui database, maka objek lain (mungkin tidak terkait) mendapatkan koneksi, memiliki kesalahan, dan berakhir memutar kembali perubahan objek pertama. Jenis kesalahan ini bisa sangat sulit untuk di-debug.
TMN
2

Melewati Connectioncontoh di sekitar biasanya tidak menjadi masalah, meskipun dalam kebanyakan situasi hanya implementasi DAO yang ada hubungannya dengan mereka. Sekarang, dengan masalah Anda tentang koneksi yang tidak ditutup setelah digunakan, sebenarnya mudah diperbaiki: Connectionobjek harus ditutup pada level yang sama seperti dibuka, yaitu dengan metode yang sama. Saya pribadi menggunakan pola kode berikut:

final Connection cnx = dataSource.getConnection();
try {
    // Operations using the instance
} finally {
    cnx.close();
}

Dengan begitu saya memastikan semua koneksi selalu ditutup, bahkan jika ada pengecualian yang dilemparkan ke dalam blok. Saya benar-benar pergi selama menggunakan pola Statementdan ResultSetcontoh yang sama persis , dan semuanya berjalan lancar sejauh ini.

Sunting 2018-03-29: Seperti yang ditunjukkan oleh user1156544 dalam komentar di bawah ini, dimulai dengan Java 7 penggunaan konstruk try-with-resources harus disukai. Dengan menggunakannya, pola kode yang saya berikan dalam jawaban awal saya dapat disederhanakan seperti:

try (final Connection cnx = dataSource.getConnection()) {
    // Operations using the instance
}
KevinLH
sumber
1
Saya menggunakan sesuatu yang serupa. Saya memiliki fungsi doInTransaction (tugas DbTask), di mana DbTask adalah antarmuka saya dengan metode dengan parameter koneksi. doInTransaction mendapatkan koneksi, panggilan tugas dan komit (atau kembalikan jika ada pengecualian) dan tutup koneksi itu.
user470365
menilai dari contoh Anda, itu berarti bahwa objek DataSource adalah singleton?
ipohfly
@ipohfly Sebenarnya saya seharusnya menamai objek itu dataSourcedaripada DataSource(saya akan memperbaiki jawaban saya mengenai hal itu). Jenis yang tepat dari objek itu adalah javax.sql.DataSource. Dalam kode lama saya dulu memiliki seorang lajang yang mengelola semua sumber data yang tersedia dalam aplikasi saya. DAO saya tidak perlu tahu tentang itu, karena DataSourceinstance disediakan melalui injeksi ketergantungan.
KevinLH
Jika Anda menggunakan skema ini, gunakan try-with-resources lebih baik
user1156544
Kembali ketika saya menjawab, saya belum menggunakan Java 7. Tapi Anda benar bahwa ini harus menjadi cara yang disukai hari ini. Saya akan memperbarui jawaban saya untuk memasukkan saran Anda.
KevinLH
0

ada tradeoff untuk melakukan hal-hal seperti ini daripada menggunakan singleton yang bisa Anda dapatkan sesuai kebutuhan. Saya telah melakukan berbagai hal di masa lalu.

Secara umum, Anda perlu memikirkan konsekuensi dari manajemen koneksi database, dan ini mungkin atau mungkin tidak ortogonal untuk penggunaan query database. Sebagai contoh, jika Anda memiliki satu koneksi db untuk instance aplikasi yang diberikan dan akan ditutup saat tidak digunakan, itu akan menjadi ortogonal. Letakkan manajemen di kelas tunggal dan jangan menyebarkannya. Ini memungkinkan Anda untuk mengelola koneksi db sesuai kebutuhan. Misalnya, jika Anda ingin menutup koneksi pada setiap commit (dan membuka kembali pada panggilan berikutnya) ini lebih mudah dilakukan pada singleton karena API untuk ini dapat terpusat.

Di sisi lain, anggaplah Anda perlu mengelola kumpulan koneksi di mana panggilan yang diberikan mungkin perlu menggunakan koneksi yang sewenang-wenang. Ini mungkin terjadi ketika melakukan transaksi terdistribusi di beberapa server, misalnya. Dalam hal ini Anda biasanya jauh lebih baik melewati objek koneksi db daripada Anda bekerja dengan lajang. Saya pikir ini biasanya merupakan kasus yang lebih jarang, tetapi tidak ada yang salah dengan melakukannya ketika Anda perlu.

Chris Travers
sumber