Nhibernate-2

Yazımızın ikinci bölümünde Nhibernate’in derinliklerine doğru yol alalım.Uzunca girizgahlara hiç gerek yok.Hemen başlayalım.

Load(),Get()
Id’sini bildiğimiz nesneleri çekmek için kullanılır.Criteria oluşturmaya gerek yoktur.

Ogrenci ogr=new Ogrenci();
ogr=(Ogrenci)session.Load(typeof(Ogrenci),242);
ogr=(Ogrenci)session.Get(typeof(Ogrenci),242);

Birinci kullanımda nesnenin veritabanında karşılığı yoksa exception fırlatır.Get metodunu kullandığımızda nesnenin veritabanında karşılığı yoksa metod null değeri döndürür.

Lazy
Lazy kavramına daha sonra değinmeyi planlıyordum ama sonraki örneklerde bolca lazım olacağı için anlaşılması gereken bir kavram.Nhibernate’in işinin aslında nesneler(tablolar) arası ilişkileri yönetmek olduğundan bahsetmiştik.Bir önceki yazının son kısmında yaptığımız örnekte veritabanından bir Okul nesnesi çekmiş onun Siniflar listesinin içine dallanmıştık.Şimdi okul sınfıını çektiğimizde onun yavru kayıtlarını o kayıtların da yavru kayıtlarını çektiğimizi hayal edin.İyi ilişkilendirimiş bir veritabanımız varsa nerdeyse tüm veritabanını çekmeye kalkıyoruz demektir.Öyleyse bu işleme bir yerde dur demeliyiz.Burda “lazy” devreye giriyor.Lazy=true ifadesinin ana kaydın collection’ında kullandığımızda “Bu collection’ı doldurma.İhtiyacım olduğunda getir” demiş oluyoruz.Peki nhibernate bizim bu listeye ihtiyacımız olduğundan nasıl haberdar oluyor:Kod satırında nesneyi yazıp o nesnenin bahsi geçen listesine ulaştığımız an Nhibernate o listeyi bizim için dolduruyor.Aşağıdaki sqlserverprofiler aracı ile çalışan sql cümlelerini inceleyelim.

=>Lazy=false değeri set edilmediği durumda

IList<Okul> liste= _dao.TumOkullariGetir();


Gördüğünüz gibi hem Okul sorgusu hem de Sinif sorgusu çalıştı.

=>Lazy=true değeri set edilmediği durumda

IList<Okul> liste= _dao.TumOkullariGetir();

Sadece okul sorgusu çalıştı.
Gelen sonucu watch edip “Sınıflar” listesini açtığımızda


GetAllOkul metedomuzu tekrar gözden geçirelim.Açtığımız session’ı kapatmadık.Şu şekilde olmalıydı.

     public IList<Okul> GetAllOkul()
        {
            ISession session = _sessionFactory.OpenSession();
            ICriteria criteria = session.CreateCriteria(typeof(Okul));
            IList<Okul> liste = criteria.List<Okul>();
            <em>session.close(); </em>
            return liste;  
        }

Kodumuzu bu şekilde değiştirdikten sonra presenter sınıfımızdaki kodumuza geri dönelim.

IList<Okul> liste= _dao.GetAllOkul();
Sinif sinif=liste[0].Siniflar[0];

Üçüncü satıra dikkat edelim.Gelen okulların ilkinin ilk sınıfını almaya çalışıyoruz.Bu arada lazy=true ve session kapatıldı.

Hatırlayacağınız gibi lazy=true ifadesi bize listeye ihtiyacımız olduğu zaman listeyi gidip dolduruyordu.GetAllOkul metodundan çıkıldığı zaman session’ı kapattık.Çok daha sonra Siniflar listesine erişmek istediğimizde açık session olmadığı hatasını aldık.Öyleyse session’ımızın sürekli açık olması gerekiyor.Sesion’ı ve sessionfactory’yi bir yerlerde saklamak gerekiyor.Bunun için bir HTTPModule kullanıyoruz.Bunun için aşağıdaki gibi iki yapı kullanıyoruz.

Önce OKULDAO sınıfının yapıcısında yazdığımız Nhibernate konfigurasyonunu kaldırıyoruz.Çünkü bu nesneden instance alındığında aşağıdaki kod bloğu çalışıyor.Her seferde sessionfactory oluşturuluyor.Bu çok maliyetli bir yaklaşım.Bunun yerine sessionfactory bir defa oluşturulup pekala ASP.Net session’da saklanabilir.

private static ISessionFactory _sessionFactory;
public OkulDAONHibernate()
{
     Configuration config =new Configuration().Configure();
     _sessionFactory = config.BuildSessionFactory();
}

İki sınıfımız bize bu konuda yardımcı olacak: SessionHelper ve NhibernateHttpModule.

public class SessionHelper
    {
    private static ISessionFactory _sessionFactory = null;
        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    Configuration config = new Configuration().Configure();
                    _sessionFactory = config.BuildSessionFactory();
                    if (_sessionFactory == null)
                        throw new InvalidOperationException("Could not build SessionFactory");
                }
                return _sessionFactory;
            }
        }
        public static ISession OpenSession()
        {
            ISession session;
            session = SessionFactory.OpenSession();
            if (session == null)
                throw new InvalidOperationException("Cannot open session");
            return session;
        }
    }

Gördüğünüz gibi _SessionFactory bir kere oluşturulup static bir değişkene atılıyor.
NhibernateHttpModule sınıfı çok önemli o yüzden ayrıntılı incelemenizi tavsiye ederim.

  public class NHibernateHttpModule : IHttpModule
     {
         public const string KEY ="_TheSession_";
         private static ISession _session;
 
         private void context_BeginRequest(object sender, EventArgs e)
         {
             HttpApplication application = (HttpApplication)sender;
             HttpContext context = application.Context;
             context.Items[KEY] = SessionHelper.OpenSession();
         }
 
         private void context_EndRequest(object sender, EventArgs e)
         {
             HttpApplication application = (HttpApplication)sender;
             HttpContext context = application.Context;
 
             ISession session = context.Items[KEY] as ISession;
             if (session != null &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp; session.IsOpen)
             {
                 try
                 {
                     session.Flush();
                     session.Close();
                 }
                 catch { }
             }
             context.Items[KEY] = null;
         }
 
         public static ISession CurrentSession
         {
             get
             {
                 if (HttpContext.Current == null)
                 {
                     if (_session != null)
                         return _session;
 
                     _session = SessionHelper.OpenSession();
                     return _session;
                 }
                 HttpContext currentContext = HttpContext.Current;
                 ISession session = currentContext.Items[KEY] as ISession;
                 if (session == null)
                 {
                     session = SessionHelper.OpenSession();
                     currentContext.Items[KEY] = session;
                 }
                 return session;
             }
         }
         #region IHttpModule Members 
         public void Init(HttpApplication context)
         {
             context.BeginRequest += new EventHandler(context_BeginRequest);
             context.EndRequest += new EventHandler(context_EndRequest);
         }

59.-60.) Bu iki satır IHttpModule sınıfınından implement ettiğimiz metodlar.Böylece “open session per request” yapımızı kurmuş olacağız.Her istek geldiğinde (11.) SessionHelper yardımı ile Sessionfactory üzerinden bir session açıyoruz.İstek sonlandığında (24.) session’ımızı flush ediyoruz.Böylece arada kalmış, veritabaınına güncellenmemiş nesnelerimiz kalmıyor.
Bu yapının web uygulamamız tarafından kullanılabilmesi için webconfig ayarlarını aşağıdaki gibi değiştiriyoruz.

<system.webServer>	
    <modules>
      <add type="SanalOkul.DAO.NHibernateHttpModule,SanalOkul.DAO" name="NhibernateHttpModule"/>
   </modules>

Şimdi OkulDAO sınıfımızın son haline göz atalım.

  public IList<Okul> GetAllOkul()
        {
            ISession session = NHibernateHttpModule.CurrentSession;
            ICriteria criteria = session.CreateCriteria(typeof(Okul));
            IList<Okul> liste = criteria.List<Okul>();
            return liste;
        }

Gördüğünüz gibi artık sessionfactory oluşturma session açma,kapatma işlemleriyle biz uğraşmıyoruz.

save(),update(),SaveorUpdate(),SaveorUpdateCopy(),delete()

Save metodunun kullanımına örnek

Ogrenci ogr=new Ogrenci();
ogr.Ad = "taner";
ogr.Soyad = "bozdemir";
session.Save(ogr);
session.Flush();
session.Refresh(ogr);

5.)Nesneyi session’ımıza yolluyoruz.Henüz id alanı set edilmedi.
6.)Session’in içinde yer alan nesne(ler) veritabanına yazılıyor.Burada dikkat etmemiz gereken bir nokta var.Flush metodu sadece ogr nesnesi için çalışmaz.O anda session’da yer alan ve değişikliğe uğramış tüm nesneleri veritabanında güncellemeye çalışır.Daha önce de belirttiğimiz gibi Nhibernate nesneleri id alanalrından tanır.Load ettiği nesneleri session kapanana kadar takip eder.Isdirty diye bir alan üzerinden nesnenin değişikliğe uğrayıp uğramadığını takip eder.
7.)Nesnenin son halini veritabanından alır.veritabanında trigger türü veri üzerinde değişiklik yapacak yapılar bulunuyorsa nesnesinin son halini almak için birebirdir.Şahsen veritabanında stored procedure,function,trigger türü yapıların kullanılmasını pek benimsemiyorum.Bu tür işlemlerin business logic içerisinde gerçekleştirilmesi taraftarıyım.

Update metodunun kullanımına örnek

Ogrenci ogr=new Ogrenci();
session.Load(ogr,242);
ogr.Soyad = "bozdemir";
session.Update(ogr);
session.Flush();

5.)Bu satıra aslında gerek yok çünkü hemen alt satırdaki session.flush() metodu değişen nesneleri belirleyip bunları veritabanında güncelleyecektir.

SaveorUpdate ve SaveorUpdateCopy metodları save ve update metodlarının yerine kullanılır.Eğer nesnenin veritabanında karşılığı varsa(persistent) Nhibernate bu nesneyi günceller.Eğer yeni bir kayıt(Transient) ise nesneyi veri tabanına kaydeder.

Ogrenci ogr=new Ogrenci();
session.Load(ogr,242);
ogr.Soyad = "bozdemir";
session.SaveOrUpdateCopy(ogr);
session.Flush();

5.)242 id’li bir kayıt veritabanında varsa güncellenir,kayıt yoksa yeni bir kayıt olarak veritabanına işlenir.SaveorUpdateCopy, SaveorUpdate metodundan farklı olarak kaydettiği nesnenin bir kopyasını geri döndürür.Ayrıca Update ve SaveorUpdate kullanıldığında aynı id değerini taşıyan başka bir nesne session içerisinde yer alıyorsa “different object with the same identifier value” hatası alırız.SaveorUpdateCopy metodu yeni oluşturlan nesnenin tüm durumunu zaten session içerisinde yer alan aynı id’ye sahip nesnenin üzerine giydirir böylece tek nesne haline getirir.Bu yüzden save/update işlemlerinizde SaveorUpdateCopy metodunu kullanmanızı tavsiye ederim.Bu metodun Hibernate’deki karşılığı da bildiğim kadarıyla “merge” metodur.

Şimdi de Asp.net Session ile Nhibernate’i birlikte kullanalım.Presenter sınıfımızı bir anlığına aradan çıkardığımızı,view’in dao’ya direk eriştiğini varsayalım örneğin daha kolay anlaşılabilmesi için…

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
           OkulDAONHibernate _dao = new OkulDAONHibernate();
           Okul okl = _dao.GetById(1);
           okl.Adres += " Türkiye Dünya";
          _dao.Update(okl);
           Session["okl"] = okl;
         }
     }
    protected void Button1_Click(object sender, EventArgs e)
    {
       OkulDAONHibernate _dao=new OkulDAONHibernate();
       Okul okl = (Okul) Session["okl"];
       Sinif snf= okl.Siniflar[0];
       snf.SinifAdi = "11D";
      _dao.Update(okl);
     }
}

8.)Id’si 1 olan okulu getiriyoruz.
9.-10.)Adres alanını güncelliyoruz.
11.)Nesneyi Asp.Net Session’a atıyoruz.
17.)Buton eventi altında nesneyi Asp.Net Session’ınından geri alıyoruz.
18.)Siniflar listesine ulaşıp ilk sınıfı alıyoruz.
19.-20.)O sınıfın adını “11D” olarak güncelliyoruz.

Peki bu kod düzgün çalışır mı?Hemen söyleyelim çalışmaz.
=>Page_Load eventi altında okul nesnemizi veritabanından çekiyoruz.(buraya dikkat! Siniflar listesi lazy=true)
=>Okul nesnemizin adresini güncelliyoruz.
=>Nesneyi Asp.Net Session’a atıyoruz.
=>PageLoad eventi bitiyor ve NhibernateHttpModule içindeki context_EndRequest metodu çalışıyor.
=>Buton eventi başlıyor context_BeginRequest metodu çalışıyor ve yeni bir session açılıyor.
=>18.satırda kapanan session’a ait nesnemizi geri alıyoruz.
=>19.satırda nesnenin lazy olan listesine erişmek isityoruz.Nhibernate devreye girip listeyi bizim için çekmeye kalkıyor ama bu nesnenin listesi kapanan bir session’a ait.Vee çok sevdiğimiz hatamız gene karşımızda: failed to lazily initialize a collection – no session

Bu durumda elimizde üç seçenek var:
=>Listeyi lazy=false yapmak ama bu değişkeni kontrollü kullanmak lazım zira çok büyük listeler performansı dibe çeker.
=>Page Load eventi altında değiştirmek istediğimiz sınıfın ait olduğu okulu değil de “Sinif” nesnesinin kendisini Asp.Net Session içine gömüp aşağıda gerekli değişikliği yapıp güncelleme yaparız.
=>Okul nesnesini Page Load eventi altında session’a atıp Buton event’i altında erişerek o nesneyi id’siyle yeniden veritabanından getirterek sinifları üzerinde işlem yapmak.

protected void Button1_Click(object sender, EventArgs e)
{
    OkulDAONHibernate _dao=new OkulDAONHibernate();
    Okul okl = (Okul) Session["okl"];
    okl=_dao.GetById(okl.OkulId);
    Sinif snf= okl.Siniflar[0];
    snf.SinifAdi = "11Z";
   _dao.UpdateSinif(snf);
}

6.)Bu satırda önceki sesssion’a kalan nesnemizi bu sessionda tekrar çekiyoruz.Nhibernate bu işlem için Refresh() metodunu sunuyor.

protected void Button1_Click(object sender, EventArgs e)
{
     OkulDAONHibernate _dao=new OkulDAONHibernate();
     Okul okl = (Okul) Session["okl"];
     _dao.RefreshOkul(okl);
     Sinif snf= okl.Siniflar[0];
     snf.SinifAdi = "11Z";
    _dao.UpdateSinif(snf);
}

5.)Bu satırdaki RefreshOkul metodunun içeriği de şöyle

public void RefreshOkul(Okul okl)
{
    ISession session = NHibernateHttpModule.CurrentSession;
    session.Refresh(okl);
}

Şimdi biraz daha kapsamlı bir örnek yapalım.Bir Okul oluşturalım.Ona bağlı iki sınıf ve o sınıflara bağlı birer öğrenci kaydedelim.

     OkulDAONHibernate _dao = new OkulDAONHibernate();
     Okul okl=new Okul();
     okl.OkulAdi = "Refet Bele Lisesi";
     okl.Adres = "Tuzla/İstanbul";

     Sinif snf1=new Sinif();
     snf1.Okul = okl;
     snf1.SinifAdi = "9A";
     Sinif snf2=new Sinif();
     snf2.Okul = okl;
     snf2.SinifAdi = "9B";

     Ogrenci ogr1=new Ogrenci();
     ogr1.Ad = "İsmail";
     ogr1.Soyad = "Koç";
     ogr1.Sinif = snf1;

     Ogrenci ogr2 = new Ogrenci();
     ogr2.Ad = "Emrah";
     ogr2.Soyad ="Bozdağ";
     ogr2.Sinif = snf2;

     snf1.Ogrenciler.Add(ogr1);
     snf2.Ogrenciler.Add(ogr2);
     okl.Siniflar.Add(snf1);
     okl.Siniflar.Add(snf2);

   _dao.Insert(okl);

Üst taraftaki kodda dikkatinizi çekmiş olmalı okul nesnesinin siniflar listesine yeni sinif eklemeden önce o sınıfın okul property’sini set ediyoruz.

    snf2.Okul = okl;
    okl.Siniflar.Add(snf2);

Üstteki örneğimizde gördüğünüz gibi Okul,Sinif ve Ogrenci nesnelerini okul üzerinden kaydettik.Yani ilişkinin en başındaki nokta üzerinden kaydettik.Bir de olaya tersten bakalım:

     OkulDAONHibernate _dao = new OkulDAONHibernate();
     Okul okl=new Okul();
     okl.OkulAdi="Beşiktaş Anadolu Lisesi";
    
     Sinif snf=new Sinif();
     snf.SinifAdi="7A";
     snf.Okul=okl;
     okl.Siniflar.Add(snf);
     snf.Insert();

Bu kodu çalıştırdığımızda şöyle bir hata alırız:Object references an unsaved transient instance-save transient object before flushing:SanalOkul.Domain.Okul

Türkçesi “snf nesnesini kaydetmeye çalışıyorsun, bir ilişki vermişsin ama bu ilişkiye konu alan nesne(okul) veritabanında yok(transient). Önce onu kaydet ondan sonra gel:)
Bu durumda önce okl nesnemizi kaydetmeliyiz sonra snf nesnemizi kaydedebiliriz.

    _okulDao.Insert(okl);
    _oklDao.Insert(snf);

Aradaki fark anlaşıldı umarım..

Bir yazımızın daha sonuna geldik. Bir sonraki yazımızda expressionlar ve girift sorgular üzerinde yoğunlaşacağız.

2 thoughts on “Nhibernate-2

  1. Barbaros

    Update işlemleri için ASP.net Session kullanımı ne kadar sağlıklı ?
    Sayfa ilk Load olurken nesneyi bir kere çekip Session a atıyoruz.
    Sonra Button_Click de session dan alıp refresh yapıp tekrar elde ediyoruz. Yani db ye bir kere daha gidip geliyoruz, refresh etmesi için.

    Ben sayfada id üstünden kaydetip tekrar çekiyorum.
    Yani aslında sormak istediğim şey ikisi arasında bir performans farkı var mı ?
    Teşekkürler

Comments are closed.