Veritabanı bağlantısı - parametre olarak iletilmeli mi?


11

Ortak bir yöntem kullanarak veritabanı bağlantısının bir kez alındığı ve kullanılacak ilgili sınıf boyunca geçirildiği bir sistemimiz var. Veritabanı sınıfını farklı sınıflara bir parametre olarak geçirirken sorun yaratabileceğinden şüphe var, bu yüzden bu gerçekten uygun olup olmadığını görmek için kontrol ediyorum ve bunu yapmak için daha iyi kalıplar var mı?

Kalıcılığı sağlayacak bazı ORM araçları olduğunu biliyorum ama henüz buna giremeyiz.

Herhangi bir geri bildirim bekliyoruz, teşekkürler.


Ne tür problemlerden bahsediyorsunuz? Kimin bu şüpheleri var? (Sen değil, sanırım.)
Greg Hewgill

1
Geliştirici bağlantıyı kapatmayı unutma neden gibi sorunlar, genellikle sadece bir parametre olarak çeşitli yöntemlere veritabanı bağlantısı etrafında geçmek için iyi bir uygulama olup olmadığını görmek için çalışıyorum. Ya şüpheler başka bir geliştiriciden geliyor.
ipohfly

Yanıtlar:


8

Evet, bir bağlantının etrafından geçmek güvenlidir. Bağlantıyı bir dış kontrol bloğunda idare edersiniz. Bu konuda güvensiz bir şey yok.

Güvensiz olan, bağlantının zamanında uygun şekilde atandığını garanti etmeyen kod yazmaktır. Bir kaynağı temizlemeyi unutmak, onu geçirmeyle ilgisi yoktur. Asılı bir bağlantıyı herhangi bir yere iletmeden bırakan kodu kolayca yazabilirsiniz.

C ++ 'da, yığına ayırırsanız veya akıllı işaretçiler kullanırsanız RAII tarafından korunursunuz. C # 'da tüm tek kullanımlık nesnelerin (bağlantılar gibi) bir "using" bloğunda bildirilmesi zor bir kural oluşturur. Java'da try-finally mantığı ile temizleyin. Bunu sağlamak için tüm veri katmanı kodunda kod incelemeleri yapın.

En yaygın kullanım durumu, birçok permütasyonda birleştirilebilecek birkaç işleminiz olduğu zamandır. Ve bu permütasyonların her birinin bir atomik işlem olması gerekir (hepsi başarılı veya geri dönüş). işlemi (ve dolayısıyla ilgili bağlantıyı) tüm yöntemlere aktarmanız gerekir.

Atomik işlemler olarak çeşitli şekillerde birleştirilebilecek birçok foobar () eylemimiz olduğunu varsayalım.

//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 bağlantıları olabildiğince geç açmak isteyeceksiniz, mümkün olan en kısa sürede atın. Bağlantıları nesne üyeleri olarak ele alıyorsanız, gereksiz durum olarak tanıtırsanız ve bağlantıları gereğinden fazla açık bırakırsanız, takım arkadaşlarınız doğru olabilir. Ancak, bir bağlantıyı veya işlemi parametre olarak iletme eylemi doğası gereği yanlış değildir.

BTW. Dilinizin birinci sınıf işlevleri desteklemesine bağlı olarak foobar () eylemleri listesinde yer alabilirsiniz. Böylece bir işlev, eylemlerin tüm permütasyonlarını kaldırabilir. Her permütasyon için dış kontrol bloğunun çoğaltılmasının ortadan kaldırılması.


durumun nasıl olduğu hakkında bana daha fazla fikir verdiğinden bunu yanıt olarak işaretlemek
ipohfly

6

Bağımlılık Enjeksiyonundan sonra gibisin . Yani, havuzlanmış bağlantı bir kez oluşturulur ve gerektiğinde enjekte edilir. Kesinlikle bir yöntem parametresi aracılığıyla bağlantıya geçmek bağımlılık enjekte etmenin bir yoludur, ancak Guice, PicoContainer veya Spring gibi bir IoC kapsayıcısı bunu yapabileceğiniz başka (daha güvenli) bir yoldur.

DI'yi kullanmak, mantığı, temel iş mantığınızdan uzakta, bağlantının oluşturulması, açılması, kullanılması ve kapatılması etrafında düzgün bir şekilde sarabileceğiniz anlamına gelir.

Spring JDBC ve arkadaşları sizin için bu tür davranışları gerçekleştirmenin diğer örnekleridir


Emm gerçekten bağımlılık enjeksiyonuna bakmıyor. Sadece bunu yapmak için genellikle iyi bir uygulama olup olmadığını öğrenmek için çalışıyor ve değilse, veritabanı bağlantısını yönetmek için daha iyi bir yol nedir (DI olsa bunu yapmanın bir yoludur).
ipohfly

-1. Bir bağlantı çok kullanıcılı bir sisteme uymuyor. Düşük kullanıcı hacmi ve hızlı yürütme nedeniyle işe yarayabilir. Havuzlama ile, tek bir kullanıcı sisteminde bile eylem başına bir bağlantı nesnesinin başlatılması daha iyidir.
mike30

2

Veri şeyleri yerine veritabanı şeylerinin etrafından dolaşmak sorunlara yol açabilir. Bu kapsamda, ne zaman pratik olursa olsun, uygun veritabanı hijyenini garanti edemezse bir veritabanı şeyini geçmeyin.

Veritabanı şeyler etrafında geçen sorun özensiz olabilir olmasıdır. Birisi bir veritabanı bağlantısı etrafında geçen biri ile kod birden fazla hata gördüm, biri daha sonra bir sonuç kümesi kapmak ve yerel bir nesnede (sonuç kümesi, hala veritabanına bağlı) saklar ve sonra bir imleç bağlar önemli bir süre için veritabanı. Başka bir örnek, bir başkasına bir sonuç kümesi geçirdi (daha sonra saklandı) ve sonra sonuç kümesini geçen yöntem onu ​​(ve ifadeyi) kapattı ve diğer yöntemler artık olmayan sonuç kümesiyle çalışmaya çalıştığında hatalara yol açtı.

Bütün bunlar veritabanına, bağlantıya, ifadeye, sonuç kümesine ve yaşam döngülerine saygı duymamaktan kaynaklanıyor.

Bundan kaçınmak için, veritabanları ile daha güzel oynayan ve kapalı oldukları sınıflardan çıkmak için veritabanı şeylerine sahip olmayan mevcut desenler ve yapılar vardır.


1
+1 db bağlantıları mümkün olan en kısa süreye sahip olmalıdır. Açın, kullanın, mümkün olduğunca hızlı kapatın. Günümüzde çok sayıda bağlantı havuzu uygulaması var, bu nedenle birden fazla işlem için bir bağlantıyı kullanmak yanlış bir ekonomi. Hatalar veya performans sorunları için bir davetiye (masalarda kilit tutma, bağlantı kaynaklarını kullanma)
jqa

Bu mevcut desen ve yapıların bazılarının isimleri nelerdir?
Daniel Kaplan

@tieTYT Ön plana çıkan birincil olanlar , veritabanını uygulamanın geri kalanından gizlemeye yarayan Veri erişim nesnesidir . Ayrıca

Bu kalıpları düşündüğümde, sorduklarından daha yüksek bir soyutlama düzeyinde olduklarını hissediyorum. Diyelim ki Rootbir Dao'dan almak için bir yolunuz var. Ama sonra Nodetüm Rootnesneyi çekmeden bir yol almak istediğinizi fark edersiniz . RootDao'nun NodeDao kodunu çağırmasını nasıl sağlarsınız (yani: yeniden), ancak NodeDao'nun yalnızca NodeDao doğrudan çağrıldığında bağlantıyı kapattığından ve Dao çağrıldığında bağlantıyı açık tuttuğundan emin olun Root?
Daniel Kaplan

1
Sadece otomatik taahhüt modunda değilseniz, bir bağlantıyı iletmenin bir nesnenin veritabanını güncellediği bir duruma yol açabileceğini, o zaman başka (muhtemelen ilgisiz) bir nesnenin bağlantıyı aldığını, bir hatayla karşılaştığını ve rüzgar aldığını eklemek istedim. ilk nesnenin değişikliklerini geri almak. Bu tür hataların ayıklanması çok zor olabilir .
TMN

2

Geçme Connectionetrafında örneklerini en durumda tek DAO uygulamaları onlarla ilgisi olmalı rağmen genellikle bir sorun değildir. Şimdi, probleminiz kullandıktan sonra bağlantıların kapatılmamasıyla ilgili, aslında düzeltilmesi kolaydır: Connectionnesnenin açıldığı aynı seviyede, yani aynı yöntemde kapatılması gerekir. Şahsen aşağıdaki kod desenini kullanıyorum:

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

Bu şekilde, blok içinde bir istisna atılsa bile, tüm bağlantıların her zaman kapalı olduğundan emin olurum. Aslında, aynı örnek Statementve ResultSetörnekler için kullandığım sürece devam ediyorum ve her şey şimdiye kadar pürüzsüz yelken oldu.

Edit 2018-03-29: Kullanıcı1156544 tarafından aşağıdaki yorumlarda belirtildiği gibi, Java 7 ile başlayarak, kaynaklarla deneme yapısının kullanılması tercih edilmelidir. Bunu kullanarak, ilk cevabımda verdiğim kod deseni şöyle basitleştirilebilir:

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

1
Benzer bir şey kullanıyorum. DbTask bağlantı parametresi yöntemiyle benim arabirim olduğu işlevi doInTransaction (DbTask görev) var. doInTransaction bağlantıyı alır, görevi çağırır ve commit (veya istisna varsa geri alma) ve bu bağlantıyı kapatır.
user470365

örneğinize bakılırsa, DataSource nesnesinin bir singleton olduğu anlamına mı gelir?
ipohfly

@ipohfly Aslında nesne olduğunu adlandırılmış olması gereken dataSourceyerine DataSource(Ben bu noktada ilgili benim cevap düzeltmek gerekir). O nesnenin tam türü olurdu javax.sql.DataSource. Eski kodda, uygulamalarımdaki mevcut tüm veri kaynaklarını yöneten bir singleton vardı. DAO'larım, DataSourceörneğin bağımlılık enjeksiyonu yoluyla sağlandığından bunu bilmek zorunda değildi .
KevinLH

Bu şemayı kullanın deneyin-ile-kaynaklar daha iyi kullanırsanız
user1156544

Yanıt verdiğimde, henüz Java 7 kullanmıyordum. Ancak bu günlerde bunun tercih edilen yol olması gerektiği konusunda haklısınız. Önerinizi eklemek için cevabımı güncelleyeceğim.
KevinLH

0

gerektiğinde alabileceğiniz bir singleton kullanmak yerine işleri bu şekilde yapma dengesi vardır. Geçmişte her iki şekilde de işler yaptım.

Genel olarak, veritabanı bağlantı yönetiminin sonuçlarını düşünmeniz gerekir ve bu, veritabanı sorgusu kullanımına dik olabilir veya olmayabilir. Örneğin, belirli bir uygulama örneği için bir db bağlantınız varsa ve kullanılmadığında kapatılırsa, bu dikeydir. Yönetimi tek bir sınıfa koyun ve onu aktarmayın. Bu, ihtiyacınız olan db bağlantısını yönetmenizi sağlar. Örneğin, her bağlantıda bir bağlantıyı kapatmak (ve bir sonraki çağrıda yeniden açmak) istiyorsanız, bunun tek birtonda yapılması daha kolaydır, çünkü bunun için API merkezi olabilir.

Öte yandan, belirli bir aramanın rasgele bir bağlantı kullanması gerekebileceği bir bağlantı havuzunu yönetmeniz gerektiğini varsayalım. Bu, örneğin birden çok sunucuda dağıtılmış işlemler yaparken olabilir. Bu durumda, db bağlantı nesnesini geçerek tekil nesnelerle çalışmanız genellikle çok daha iyidir. Bence bu genellikle daha nadir bir durumdur, ancak gerektiğinde bunu yapmakta yanlış bir şey yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.