Makale Özeti

NHibernate - Dinamik Kayıt Güncellemesi/Girişi

Makale

NHibernate - Dinamik Kayıt Günce

NHibernate - Dinamik Kayıt Güncellemesi/Girişi

Dinamik Güncelleme (Dynamic Update)

NHibernate'in eleştirildiği noktaların başında stored procedure desteklememesi ve sorguları uygulama tarafında oluşturup veritabanı yönetim sistemine gönderip o şekilde çalıştırmasıdır. Ayrıca NHibernate'in .NET 2.0 uyumlu olarak çıkacak olan sonraki sürümde stored procedurelerin destekeleneceği NHibernate proje liderleri tarafından açıklanmıştır.

Ancak stored procedure'ler ile çalışırken elde etmek için çok fazla gereksiz iş yapmamız ve mantıklı bir çözümle yapamayacağımız bazı işlevleri NHibernate bize sunmaktadır. Örneğin önceki makalede gördüğümüz gibi aşağıdaki kod bloğu sadece kişinin Ad özelliğini değiştirmemize rağmen tüm özelliklerini güncelleyen bir sql sorgusu oluşturuyordu, SQL Server üzerinde çalıştırıyordu.

      NHibernate.Cfg.Configuration cf=new NHibernate.Cfg.Configuration();

      cf.AddAssembly(this.GetType().Assembly);

      NHibernate.ISessionFactory f=cf.BuildSessionFactory();

      NHibernate.ISession s=f.OpenSession();

      Musteri m;

      m=s.Get(typeof(Musteri),1) as Musteri;

      if (m!=null)

      {

        m.Ad="Ali Kemal";

        s.Update(m);

        s.Flush();

      }

      s.Close();

 

Elde edilen sql sorgusu aşağıdaki gibiydi.

 

exec sp_executesql N'UPDATE Musteri SET Ad = @p0, KayitTarihi = @p1, Aktif = @p2, Soyad = @p3 WHERE MusteriID = @p4', N'@p0 nvarchar(4000),@p1 datetime,@p2 bit,@p3 nvarchar(4000),@p4 int', @p0 = N'Ali Kemal', @p1 = 'Mar 3 2005 12:00:00:000AM', @p2 = 1, @p3 = N'HAN', @p4 = 1

 

Dikkat edilirse sorguda sadece Musteri nesnesinin Ad özelliği değiştirilmiş olmasına rağmen tüm alanlar üzerinde tekrar güncelleme yapılmaktadır. Bu durum ilk bakışta problem olmasa da gereksiz yere veri transferine sebep olmaktadır. Örneğin update sorgusu sadece Ad alanını güncelleyecek şekilde oluşturulup çalıştırılmış olsaydı bu uygulama ile veritabanı sunucusu arasında daha az verinin transfer edilmesini sağlardı.

 

NHibernate bu işlevi yerine getirebilir. Yani sadece değeri değişen alanları güncelleyecek bir sql sorgusu oluşturup bu sorguyu çalıştırabilir. Bunun için hbm.xml dokumanında <class> elementinin dynamic-update özelliği true yapılmalıdır.

 

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">

  <class name="NHOrnek.Musteri, NHOrnek" table="Musteri" dynamic-update="true">

    <id name="MusteriID" column="MusteriID" type="Int32" unsaved-value="0">

      <generator class="identity" />

    </id>

    <property name="Ad" column="Ad" type="String" length="50" />

    <property name="Soyad" column="Soyad" type="String" length="50" />

    <property name="KayitTarihi" column="KayitTarihi" type="DateTime" />

    <property name="Aktif" column="Aktif" type="Boolean" />

  </class>

</hibernate-mapping>

 

 

dynamic-update="true" olarak belirlendikten sonra aynı c# kodunu tekrar çalıştırır ve profiler ile çalıştırılan sorguları izlersek istediğimiz sonucu aldığımızı gözlemleyebiliriz.

 

 

 

exec sp_executesql N'UPDATE Musteri SET Ad = @p0 WHERE MusteriID = @p1', N'@p0 nvarchar(4000),@p1 int', @p0 = N'Ali Kemal', @p1 = 1

 

Görüldüğü gibi NHibernate sadece değişen alanları update edecek bir  sql sorgusu oluşturdu ve bunu çalıştırdı. Diğer alanların verilerinin sorguya parametre olarak dahil olmamış olması ile istemci ve sunucu arasında daha az veri transferi yapılmış olmasını sağladı.

 

 

Dinamik Kayıt Ekleme

 

NHibernate'in Dinamik güncelleme gibi dinamik kayıt ekleme özelliği de vardır. Bu özellik ile null geçilebilen alanlara karşılık gelen classlardaki propertylere değer atanmadığı takdirde oluşturulan insert sorgusu için bu alanlar için bilgi yer almaz.

 

dynamic-insert senaryosunda kullanmak üzere Musteri adındaki tablomuza Adres adında bir alan ekleyelim.

 

ALTER TABLE dbo.Musteri ADD Adres varchar(50) NULL

 

Musteri sınıfına Adres adında bir property ekleyelim.

 

using System;

namespace NHOrnek

{

  public class Musteri

  {

    private int _MusteriID;

    private string _Ad;

    private string _Soyad;

    private DateTime _KayitTarihi;

    private bool _Aktif;

   private string _Adres;

    public int MusteriID

    {

      get { return _MusteriID; }

      set { _MusteriID = value; }

    }

    public string Ad

    {

      get { return _Ad; }

      set { _Ad = value; }

    }

    public string Soyad

    {

      get { return _Soyad; }

      set { _Soyad = value; }

    }

    public DateTime KayitTarihi

    {

      get { return _KayitTarihi; }

      set { _KayitTarihi = value; }

    }

    public bool Aktif

    {

      get { return _Aktif; }

      set { _Aktif = value; }

    }

    public string Adres

    {

      get { return _Adres; }

      set { _Adres = value; }

    }

    public override string ToString()

    {

      return this.Ad + " " + this.Soyad + "\n" + this.Aktif.ToString() + "\n" + this.KayitTarihi.ToShortDateString();

    }

 

  }

}

 

 

hbm.xml dosyasına adres alanı için gerekli mapping tanımlamasını ekleyelim.

   

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">

  <class name="NHOrnek.Musteri, NHOrnek" table="Musteri" dynamic-update="true">

    <id name="MusteriID" column="MusteriID" type="Int32" unsaved-value="0">

      <generator class="identity" />

    </id>

    <property name="Ad" column="Ad" type="String" length="50" />

    <property name="Soyad" column="Soyad" type="String" length="50" />

    <property name="KayitTarihi" column="KayitTarihi" type="DateTime" />

    <property name="Aktif" column="Aktif" type="Boolean" />

    <property name="Adres" column="Adres" type="String" />

  </class>

</hibernate-mapping>

 

 

Ardından yeni kayıt eklemek için aşağıdaki C# kodunu yazıp çalıştıralım.

 

      NHibernate.Cfg.Configuration cf=new NHibernate.Cfg.Configuration();

      cf.AddAssembly(this.GetType().Assembly);

      NHibernate.ISessionFactory f=cf.BuildSessionFactory();

      NHibernate.ISession s=f.OpenSession();

      Musteri m=new Musteri();

      m.MusteriID=0;

      m.Ad="Doğukan";

      m.Soyad="Türkoğlu";

      m.KayitTarihi=DateTime.Now;

      m.Aktif=true;

      s.Save(m);

      s.Flush();

      s.Close();

 

Yukarıdaki kod parçasından yeni bir musteri nesnesi oluşturuluyor oluşturulan nesne Save metodu ile kaydediliyor. Ancak nesnenin Adres özelliğine herhangi bir değer ataması yapılmamış.

 

Nesneyi kaydetmek için kodu çalıştırıp, profiler ile izleme yaptığımızda aşağıdaki sql sorgusunu oluşturulduğunu gözlemleyebiliriz. Adres property'sine bir değer atanmadığı için sorguda Adres alanına karşılık gelecek parametre (@p3)  null  olarak geçilmiş.

 

exec sp_executesql N'INSERT INTO Musteri (Ad, KayitTarihi, Aktif, Adres, Soyad) VALUES (@p0, @p1, @p2, @p3, @p4); select SCOPE_IDENTITY()', N'@p0 nvarchar(4000),@p1 datetime,@p2 bit,@p3 nvarchar(4000),@p4 nvarchar(4000)', @p0 = N'Doğukan', @p1 = 'Feb 17 2006 11:22:14:000AM', @p2 = 1, @p3 = NULL, @p4 = N'Türkoğlu'

 

dynamic-insert özelliği

 

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">

  <class name="NHOrnek.Musteri, NHOrnek" table="Musteri" dynamic-update="true" dynamic-insert="true">

    <id name="MusteriID" column="MusteriID" type="Int32" unsaved-value="0">

      <generator class="identity" />

    </id>

    <property name="Ad" column="Ad" type="String" length="50" />

    <property name="Soyad" column="Soyad" type="String" length="50" />

    <property name="KayitTarihi" column="KayitTarihi" type="DateTime" />

    <property name="Aktif" column="Aktif" type="Boolean" />

    <property name="Adres" column="Adres" type="String" />

  </class>

</hibernate-mapping>

 

dynamic-insert özelliği true olarak belirlendiğinden NHibernate değer atamamış property'lere karşılık gelen alanları için insert sorgusunda kullanmaz.

Aynı kod bloğunu hbm.xml'i değiştirdikten sonra tekrar çalıştırır ve profiler ile sql sorgusunu gözlemlersek, NHibernate'in çalıştırıdğı sql sorgusunda Adres alanına ile ilgili herhangi bir işlem yapmadığını anlarız.

 

exec sp_executesql N'INSERT INTO Musteri (Ad, KayitTarihi, Aktif, Soyad) VALUES (@p0, @p1, @p2, @p3); select SCOPE_IDENTITY()', N'@p0 nvarchar(4000),@p1 datetime,@p2 bit,@p3 nvarchar(4000)', @p0 = N'Doğukan', @p1 = 'Feb 17 2006 11:20:01:000AM', @p2 = 1, @p3 = N'Türkoğlu'

 

 

Not: sadece hbm.xml dosyasını değiştirip uygulamayı tekrar çalıştırmak istediğinizde, Visual Studio kaynak kod dosyalarında değişiklik algılamayıp önceki build de oluşturduğu .exe dosyasını çalıştırabilir. Bu duruma çözüm olarak kaynak kod (.vb) dosyalarınızdan birinde ufak bir değişiklik (bir boşluk karakteri ekleyip silmek bile olabilir) yapıp kaydetmeniz yada daha uygun olacak şekilde build menüsünden rebuild seçeneğini seçtikten sonra F5 ile uygulamayı çalıştırmanız olacaktır.

 

Cengiz HAN

Microsoft ASP.NET MVP
cengiz@cengizhan.com