Data Access Object Pattern (DAO)

Bu yazıda biraz daha yüksek seviyeli tasarım kalıplarından olan Data Access Object Pattern kısa adıyla DAO pattern”ı inceleyeceğiz. Aslında DAO Pattern klasik anlamıyla daha önce bahsettiğimiz Strategy Pattern “ın örneğidir. Fakat uygulama alanı biraz daha veri katmanı ile özelleşmiştir.Makale için DAO Pattern kullanmadan ve kullanarak geliştirdiğim örnek projeleri yazının en sonunda bulabilirsiniz.

Örneklerde kodu fazla uzatmamak için parametrik SQL yapısı kullanmadım.Siz siz oldun gerçek projelerde örnekte yaptığım gibi SQL”i direk olarak kullanmayın. Ayrıca yazısı yine fazla uzatmamak için Dao sınıflarının direk formların altından kullandım.Daha iyi bir mimari açısından Dao sınıflarının kullanıcı arayüzünden(UI) kullanılması iyi bir pratik değil.Bununla alakalı bu yazıdan sonra Model View Presenter yazıma bakabilirsiniz.

DAO pattern”ın amacı sistemimizde kullandığımız nesnelerin çeşitli veri katmanlarına erişimini sistemden soyutlamaktır. Örnek olarak klasik veritabanı kullanan çoğu uygulamada nesneleri veritabanından okuma,silme,güncelleme,ekleme(CRUD) işlemleri bulunur.Tabi artık uzay çağında iş mantığınızın DataSet,RecordSet,DataTable.. gibi yapılar üzerine kurulu olmadığını varsayıyorum :) İş mantığının nesneler üzerine kurulu bir yapıda olduğunuzu varsayıp devam ediyorum.

Nesneleri kullanan uygulamalarda bu nesneleri veri kaynağına(XML,İlişkisel Veritabanı,Nesne Veritabanı…) girmek,silmek,güncellemek isteyeceksiniz. Yukarıda parantez içini okuduğunuzda bile zaten veri saklama teknolojilerinin ne kadar çeşitli olduğunu görüyorsunuz.Birde bunun üstüde değişen veri erişim teknolojileri gelince günümüz iş uygulamalarının en çok değişen kısımlarının veri erişim katmanları oluyor. Tabi bu veri erişim kodunu uygulamanızdan iyi soyutlamayınca yeni bir veri erişim teknolojisine geçmek çok zahmetli oluyor.Ayrıca iyi soyutlanmamış bir katman birçok diğer konuda(kodun yönetimi,testi…) başınıza birsürü dert açabiliyor.

Örnek olarak Java ve .NET de değişen veri teknolojilerine bakalım.

Java Veri Erişim Teknolojileri: JDBC,JDO,Hibernate,iBatis….
.NET Veri Erişim Teknolojileri: ADO.NET,Entity Framework,Enterprise Library,NHibernate,iBatis…

Gördüğünüz gibi bu teknolojiler sürekli değişiyor o yüzden uygulamamızın değişen veri erişim tekniklerinden etkilenmemesini istiyoruz işte bu noktada nesnelerimizin veri erişim katmanındaki CRUD(Create,Read,Update,Delete) ve diğer.. işlemlerini soyutlamak için DAO Pattern kullanıyoruz.Şimdi DAO Pattern”ı anlatmaya başlamadan aklınıza şöyle bir soru gelmiş olabilir. “Benim veri erişim teknolojim süper asla değiştirmeye ihtiyaç duymam. Neden DAO Pattern kullanmama gerek olsun ki…” (ne kadar ısrar etsenizde yazılımda değişmeyen tek şey değişimdir :) ). Aslında bence DAO tasarım kalıbının en büyük avantajı teknolojiniz değişmeyecek olsada veri erişim kodlarının uygulamadan soyutlanması,kodun yönetimini,esnekliğini oldukça arttırmaktadır.Yani kısaca Single Responsibility Principle ve Seperation Of Concerns temeline uymasını sağlıyor. Butonların altına SQL kodu yazdığım günler gözümün önünden film şeridi gibi geçti de:). Lafı fazla uzattım biliyorum hemen uygulamaya geçelim.Yine süper bir senaryo uyduralım.

Senaryo

Kullanıcının ekrandan ad,soyad,telefon,adres bilgilerini girerek kişileri silme,güncelleme,listeleme,ekleme yaptığı küçük bir adres defteri yapıyoruz.

Öncelikle ben hiç pattern mattern dinlemem gerilla tarzı kodlarım. Dalarım kodun içerisine basarım buton_click olayının altına yazarım modunda kodlamaya başlayalım.Yani DAO Pattern kullanmadan ya da veri katmanına erişimi soyutlamadan kodumuzu yazalım.Bu arada kodu yazarken fazla uzamaması için hata koşullarına,istisna durumlarına dikkat etmedim.Projeleri SQLite veritabanı kullanarak geliştirdim,Yönetim için SQLite2008 Pro Enterprise Manager kullandım sizde kullanabilirsiniz.Örnek amacıyla geliştirilmiş bir kod olduğu için gerçek bir projede dikkat edilmesi gereken birçok diğer hususa dikkat etmedim

The like against http://www.hilobereans.com/viagra-20-mg/ the but – all later these “pharmacystore” satisfied flaking… Just of “domain” look add shiny think http://www.hilobereans.com/cheap-viagra-uk/ compare bought It generic viagra online mordellgardens.com an add the ! free trial cialis vermontvocals.org the buying bar Dollar. Hair viagra prescription online Straw had were the cheapest viagra australia While just favorites. For http://www.backrentals.com/shap/discount-generic-cialis.html recommended out up http://www.creativetours-morocco.com/fers/female-viagra-review.html big shelf shampoo http://augustasapartments.com/qhio/cialis-tabs-20mg I not. Expect http://www.vermontvocals.org/the-blue-pill.php In permed drops http://www.teddyromano.com/free-cialis-pills/ quality spray match: longest.

bu yüzden çıkan buglar için bana kızmayın.(hata kontrolü,validation…).Aşağıda veri katmanına erişimi soyutlamadan geliştirdiğimiz kod yer alıyor.

<br />
using System;<br />
using System.Collections.Generic;u<br />
using System.Data;<br />
using System.Data.SQLite;<br />
using System.Windows.Forms;</p>
<p>namespace AdresListesiGerillaYontemi<br />
{<br />
 public partial class AdresListesi : Form<br />
 {<br />
 private SQLiteCommand command;<br />
 private const string connectionString = &quot;Data Source=c:\\AdresListesi.db;Version=3;New=False;Compress=True;&quot;;</p>
<p> public AdresListesi()<br />
 {<br />
 InitializeComponent();<br />
 }</p>
<p> private void btnKaydet_Click(object sender, EventArgs e)<br />
 {<br />
 Kisi kisi = new Kisi(txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 Insert(kisi);<br />
 }</p>
<p> private void LoadData()<br />
 {<br />
 dgKisiListesi.DataSource = GetButunKisiler();<br />
 }</p>
<p> private IList GetButunKisiler()<br />
 {<br />
 IList kisiler = new List();<br />
 ;<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi&quot;;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();</p>
<p> while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 kisiler.Add(CreateFrom(dataReader));<br />
 }<br />
 connection.Close();<br />
 }<br />
 return kisiler;<br />
 }</p>
<p> private void Insert(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;insert into Kisi (Ad,Soyad,Adres,Telefon) values ("&quot; kisi.Ad &quot;","&quot; kisi.Soyad <br />
 &quot;","&quot; kisi.Adres &quot;","&quot; kisi.Telefon &quot;")&quot;;<br />
 ExecuteQuery(txtSQLQuery);<br />
 LoadData();<br />
 }</p>
<p> private void ExecuteQuery(string txtQuery)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();</p>
<p> command = connection.CreateCommand();<br />
 command.CommandText = txtQuery;</p>
<p> command.ExecuteNonQuery();<br />
 connection.Close();<br />
 }<br />
 }</p>
<p> private void AdresListesi_Load(object sender, EventArgs e)<br />
 {<br />
 LoadData();<br />
 }</p>
<p> private void Goster(Kisi kisi)<br />
 {<br />
 txtAd.Text = kisi.Ad;<br />
 txtSoyad.Text = kisi.Soyad;<br />
 txtAdres.Text = kisi.Adres;<br />
 txtTelefon.Text = kisi.Telefon;<br />
 }</p>
<p> private Kisi GetByID(int kisiID)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi WHERE KisiID=&quot; kisiID;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();<br />
 while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 return CreateFrom(dataReader);<br />
 }<br />
 connection.Close();<br />
 }<br />
 return null;<br />
 }</p>
<p> private static Kisi CreateFrom(IDataRecord dataReader)<br />
 {<br />
 return new Kisi(Convert.ToInt16(dataReader[&quot;KisiID&quot;].ToString()), dataReader[&quot;Ad&quot;].ToString(),<br />
 dataReader[&quot;Soyad&quot;].ToString(),<br />
 dataReader[&quot;Adres&quot;].ToString(), dataReader[&quot;Telefon&quot;].ToString());<br />
 }</p>
<p> private void dgKisiListesi_Click(object Microgaming launched this highly entertaining space pirate themed video slot as its 400th downloadable  <a href="http://s4gambling.com/fi">kasino</a>  game. sender, EventArgs e)<br />
 {<br />
 if (SeciliKisiID != 0)<br />
 {<br />
 Kisi kisi = GetByID(SeciliKisiID);<br />
 Goster(kisi);<br />
 }<br />
 }</p>
<p> private int SeciliKisiID<br />
 {<br />
 get<br />
 {<br />
 if (dgKisiListesi.CurrentRow != null)<br />
 return Convert.ToInt16(dgKisiListesi.CurrentRow.Cells[0].Value);<br />
 return 0;<br />
 }<br />
 }</p>
<p> private void btnGuncelle_Click(object sender, EventArgs e)<br />
 {<br />
 int ID = SeciliKisiID;<br />
 Kisi kisi = new Kisi(ID, txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 Guncelle(kisi);<br />
 }</p>
<p> private void Guncelle(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;update Kisi Set Ad="&quot; kisi.Ad &quot;",Soyad="&quot; kisi.Soyad &quot;",Adres="&quot; kisi.Adres <br />
 &quot;",Telefon="&quot; kisi.Telefon &quot;" WHERE KisiID=&quot; kisi.KisiID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 LoadData();<br />
 }</p>
<p> private void btnSil_Click(object sender, EventArgs e)<br />
 {<br />
 int ID = SeciliKisiID;<br />
 string txtSQLQuery = &quot;DELETE FROM Kisi WHERE KisiID=&quot; ID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 LoadData();<br />
 }<br />
 }<br />
}<br />

Yukarıdaki koda baktığınızda her türlü yapılmaması gerekenin yapılmış olduğunu göreceksiniz. Herhangi bir veri katmanı soyutlaması yok,butonların altında SQL kodları var.Veritabanına veri erişim teknolojisine tamamen bağımlı.Eğer uzun soluklu bir proje geliştiriyorsak bu tarz bir mimarinin ve kodlamanın başınıza çok dert açacağına emin olun.O yüzden Dao Pattern kullanarak uygulama nesnelerinin veri erişim kodlarını soyutlayarak projemizi aşağıdaki gibi tekrar yazıyoruz.Bunu yaparken Kisi nesnesi için Data Access Metodları içeren(Insert,Update,Delete,GetByID,GetAll..) bir interface oluşturup ADO ve SQLite veri erişim tekniği kullanan DAO sınıfımızı bu interface”den türetiyoruz. Bu arada örnek kodları en sonuna koyacağım tekrar hatırlatayım.

<br />
using System;<br />
using System.Windows.Forms;</p>
<p>namespace AdresListesiGerillaYontemi<br />
{<br />
 public partial class AdresListesi : Form<br />
 {<br />
 private readonly IKisiDAO kisiDao ;<br />
 public AdresListesi(IKisiDAO dao)<br />
 {<br />
 kisiDao = dao;<br />
 InitializeComponent();<br />
 }</p>
<p> private void btnKaydet_Click(object sender, EventArgs e)<br />
 {<br />
 Kisi kisi = new Kisi(txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 kisiDao.Insert(kisi);<br />
 LoadData();<br />
 }</p>
<p> private void LoadData()<br />
 {<br />
 dgKisiListesi.DataSource = kisiDao.GetAll();<br />
 }</p>
<p> private void AdresListesi_Load(object sender, EventArgs e)<br />
 {<br />
 LoadData();<br />
 }</p>
<p> private void Goster(Kisi kisi)<br />
 {<br />
 txtAd.Text = kisi.Ad;<br />
 txtSoyad.Text = kisi.Soyad;<br />
 txtAdres.Text = kisi.Adres;<br />
 txtTelefon.Text = kisi.Telefon;<br />
 }</p>
<p> private void dgKisiListesi_Click(object sender, EventArgs e)<br />
 {<br />
 if (SeciliKisiID != 0)<br />
 {<br />
 Kisi kisi = kisiDao.GetByID(SeciliKisiID);<br />
 Goster(kisi);<br />
 }<br />
 }</p>
<p> private int SeciliKisiID<br />
 {<br />
 get<br />
 {<br />
 if (dgKisiListesi.CurrentRow != null)<br />
 return Convert.ToInt16(dgKisiListesi.CurrentRow.Cells[0].Value);<br />
 return 0;<br />
 }<br />
 }</p>
<p> private void btnGuncelle_Click(object sender, EventArgs e)<br />
 {<br />
 Kisi kisi = new Kisi(SeciliKisiID, txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 kisiDao.Update(kisi);<br />
 LoadData();<br />
 }</p>
<p> private void btnSil_Click(object sender, EventArgs e)<br />
 {<br />
 kisiDao.Delete(SeciliKisiID);<br />
 LoadData();<br />
 }<br />
 }<br />
}<br />

<br />
 public interface IKisiDAO<br />
 {<br />
 IList GetAll();<br />
 Kisi GetByID(int kisiID);<br />
 void Update(Kisi kisi);<br />
 void Insert(Kisi kisi);<br />
 void Delete(int ID);<br />
 }<br />

<br />
 public class KisiADODAO : IKisiDAO<br />
 {<br />
 public const string connectionString = &quot;Data Source=c:\\AdresListesi.db;Version=3;New=False;Compress=True;&quot;;</p>
<p> public static void ExecuteQuery(string txtQuery)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 SQLiteCommand command = connection.CreateCommand();<br />
 command.CommandText = txtQuery;<br />
 command.ExecuteNonQuery();<br />
 connection.Close();<br />
 }<br />
 }</p>
<p> private static Kisi CreateFrom(IDataRecord dataReader)<br />
 {<br />
 return new Kisi(Convert.ToInt16(dataReader[&quot;KisiID&quot;].ToString()), dataReader[&quot;Ad&quot;].ToString(),<br />
 dataReader[&quot;Soyad&quot;].ToString(),<br />
 dataReader[&quot;Adres&quot;].ToString(), dataReader[&quot;Telefon&quot;].ToString());<br />
 }</p>
<p> public Kisi GetByID(int kisiID)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 SQLiteCommand command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi WHERE KisiID=&quot; kisiID;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();<br />
 while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 return CreateFrom(dataReader);<br />
 }<br />
 connection.Close();<br />
 }<br />
 return null;<br />
 }</p>
<p> public IList GetAll()<br />
 {<br />
 IList kisiler = new List();<br />
 ;<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 SQLiteCommand command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi&quot;;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();</p>
<p> while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 kisiler.Add(CreateFrom(dataReader));<br />
 }<br />
 connection.Close();<br />
 }<br />
 return kisiler;<br />
 }</p>
<p> public void Update(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;update Kisi Set Ad="&quot; kisi.Ad &quot;",Soyad="&quot; kisi.Soyad &quot;",Adres="&quot; kisi.Adres <br />
 &quot;",Telefon="&quot; kisi.Telefon &quot;" WHERE KisiID=&quot; kisi.KisiID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 }</p>
<p> public void Insert(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;insert into Kisi (Ad,Soyad,Adres,Telefon) values ("&quot; kisi.Ad &quot;","&quot; kisi.Soyad <br />
 &quot;","&quot; kisi.Adres &quot;","&quot; kisi.Telefon &quot;")&quot;;<br />
 ExecuteQuery(txtSQLQuery);<br />
 }</p>
<p> public void Delete(int ID)<br />
 {<br />
 string txtSQLQuery = &quot;DELETE FROM Kisi WHERE KisiID=&quot; ID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 }<br />
 }<br />

Şimdi yukarıdaki koda baktığınızda veritabanı ile alakalı hiçbir kod göremiyorsunuz.Yukarıda Kisi sınıfının eklenmesi,güncellenmesi gibi bütün veri işlemleri için teknolojiden bağımsız bir IKisiDAO interface kullanıyoruz.Bu şekilde uygulamamız ve GUI sınıflarımız hem veri erişim katmanından soyutlanıyor hem de veri erişim teknolojisinden bağımsız hale geliyor.Zaten using kısımlarına baktığınız zaman hiç System.Data ya da SQLite ile alakalı referans göremeyeceksiniz. Yukarıdaki kodu daha iyi anlamak için birde UML diyagramına bakalım.

KisiDAO1

Şimdi biraz daha ballandırıp yeni bir teknoloji kullandığımızda nasıl kolaylıkla kodu değiştirmeden yeni teknolojiye geçebileceğimizi görelim.Yeni süper teknolojiler çıktı ve ardık ADO.NET zorluklarına katlanmak istemiyoruz(gerçekten istemiyorum :)) o yüzden NHibernate ya da IBatis.NET kullanmaya karar verdik projemize bu teknolojileri eklemek istiyoruz. Yapacağım sadece bu interface”ı uygulayan yeni sınıflar eklemek. Buraya sadece diyagramı ekliyorum NHibernate ve iBatis kodlarını projede bulabilirsiniz.Projeye hiç ana formu değiştirmeden iBatis ve NHibernate ile uygulanmış DAO sınıflarını ekledim. Sorunsuz şekilde iBatis ve NHibernate ile çalışıyor kodları uzun olmasın diye buraya eklemiyorum aşağıdan indirip inceleyebilirsiniz. Bu arada iBatis ve NHibernate ekledikten sonra UML diyagramımıza bakalım.

KisiDAO2

Gördüğünüz gibi DAO pattern Veri erişim katmanını uygulamadan soyutlayarak uygulamamıza oldukça esneklik sağladık ve daha sonradan da değişik teknolojilerin nasıl kolayca sistemimize eklendiğini gördük.Sağlıcakla kalın ,kafanıza takılan sorular için çekinmeden yazın.

Kodlar

9 thoughts on “Data Access Object Pattern (DAO)

  1. Tolga Yaramis

    Yine cok emek verilmis güzel bir makale. Eline ve beynine saglik Cihat.Yazilim mimarisi ile ilgili en önemli konulari ele alip, kafalarda olusan sorulari acikliga kavusturuyosun. Iyi calismalar…

  2. Pingback: Yazılım Mühendisliği » Blog Archive » Model View Presenter (MVP) Pattern

  3. murat

    Merhaba,
    öncelikle emeğine saygı duyduğumu belirtmek isterim. birkaç gündür bu siteye rastlamam sonucunda biraz inceleme fırsatım oldu. DAO nun varlığından bi haber türk dünyasını bir nebze de olsa aydınlatmana çok sevindim. Dao örneğinde SQLite ı kullanmana daha da çok sevindim. fakat kullandığın sql kodlarında gerçekçi olduğuna inanmak istemiyorum. yıllardır insanlara parametrik yapı kullanmalarını tavsiye ederken burada basit bir örnek bile olsa bu tarz sql kodu yazman kötü bir örnek teşkil ediyor. ikinci olarak DAO yu direkt arayüzde kullanman Dao yu resmen öldürmüş. cv inde yazanlara bakarak da bunu sadece kodu uzatmamak amacıyla yaptığına inanmak istiyorum ve inanıyorumki bu tarz bir katmanlamanın yanlış olduğunun bilincindesindir.
    kusura bakma bunların hiçbiri yıkıcı amaçlı değil. birşeyler paylaşman çok güzel ama birşeyleri yaparken diğer şeyleri yıkmamak lazım. umarım artık daha dikkatli olursun.
    gözüm üzerinde :D

  4. M. Cihat Altuntaş Post author

    Evet kesinlikle haklısın. Öncelikle tahmin ettiğin gibi kodu fazla uzatmamak için parametrik yapı kullanmadım ama aslında yazının içerisinde bunun gerçekte böyle yapılmaması gerektiğini yazmam daha iyi olurdu bunu yazıya not olarak hemen ekliyorum :) . İkinci olarak DAO’yu arayüzde kullanmam da aynı nedenden kaynaklanıyor.Yazıda MVP,MVC tarzı tasarım kalıplarına değinerek konuyu dağıtmak ve uzatmak istemedim o yüzden daha sonradan yazdığım Model View Presenter yazısı altında bu DAO örneğini daha da geliştirerek nasıl UI’dan ayıracağımızı yazmıştım. Eleştirilerin için teşekkürler kesinlikle faydalı oldu.Gözünüzün üzerimde olması beni ayrıca daha dikkatli olmaya teşvit ettiği için ayrıca sağol :)

  5. Pingback: 7 Temmuz StaJ… « Cemile ARIKAN

Comments are closed.