NHibernate

Nhibernate-1

Merhaba…Tahminimce bir kaç bölümden oluşacak yazı dizimizde Nhibernate teknolojilerini uygulamalı olarak incelemeye çalışacağız.Fırsat bulabilirsek ActiveRecord’a da değineceğiz. Basitçe bir web uygulaması yapıp Nhibernate’in çalışma mantığını irdeleyeceğiz.Basit uygulama deyince aklınıza sağda solda rastladığımız tercüme makalelerde geçen console uygulamaları gelmesin. Nesneler arasındaki ilişkilerin nasıl düzenlendiğini,performans karşılaştırmaları,sık rastlanan hataların ne anlama geldikleri en önemlisi web uygulaması içinde session yönetimine değineceğiz.
Nhibernate bildiğiniz gibi çoktan tarih olmuş relational database management systemlerin yerini alan object relational mapping araçlarından biridir.Javada kullanın Hibernate’in .net için yazılmış şeklidir.Sınıfların bir mapping dosyası ile veritabanında yer alan tabloların alanlarıyla ilişkilendirilmesi mantığına dayanır.Nhibernate’i buradan edinebilirsiniz.Ben Nhibernate 1.2.1.400 versiyonunu kullanıyorum.
Neden Nhibernate?
Çevrenizde “Eski köye yeni adet”,”Ben zaten bu işleri kendi veri sınıfımla yapıyorum,”datatable doldur boşalt! en güzeli..” şeklinde gelecek muhtemel tepkilere verilecek bazı cevaplarımız olmalı.Örneğin bir arkadaşınız “ben bu veriyi datatable ile alırım, içinde dönerek verileri sınıfa yazarım böylece nhibernate’in yapmış olduğu işi yaparım sınııfları da kullanarak OOP yapmış olurum” derse o arkadaşınız muhtemelen ORM’nin sadece O’sunu anlamış aradaki R(relational)’ye hiç dikkat etmemiş.İlişkiler gerçek dünyada olduğu gibi yazılımın da en önemli parçalarından biri…Eğer siz bir ORM aracı kullanmadan OOP mantığını sürdürmek istiyorsanız sınıfları elle doldurmanızın yanısıra aradaki bütün ilişkileri de elle oluşturmanız gerekir.
Uygulamamıza geçmeden önce net olarak anlamamız gereken bazı kavramlar var.Nhibernate ile uygulama geliştirirken bu kavramlarla sık sık karşılaşacağız.

  • ISessionFactory: Verilen konfigürasyon dosyasını okuyup size Nhibernate session’ları(ISession) oluşturan yapı.
  • ISession:Uygulamanız ile veritabanızı arasında ilişki kuran içinde ADO.Net bağlantısı bulunduran veritabanı işlemlerini, sayesinde gerçekleştirdiğimiz yapı.
  • ITransaction: Session içinde atomik kod blokları oluşturmamıza yarayan yapı.
  • Persistent Object: Veritabanında karşılığı bulunan ISession içerisinde yaşayan nesnelerimize verilen ad.Örneğin veritabanından çektiğimiz bir ogrenci nesnesi persistent objecttir.Nesnenin o an id alanı doludur.
  • Transient Object: Veritabanında karşılığı bulunmayan ISession ile ilişkisi olmayan muhtemelen uygulama tarafından henüz oluşturulan nesnelerimize verilen ad.Nesnenin henüz id değeri atanmamıştır.
  • Detached Object: Persistent olan nesnenin bağlı olduğu Session’ın kapanması sonucu oluşan durumdur.Nesnenin id alanı doludur ve muhtemelen veritabanında bir kayıda karşılık gelmektedir.

Burda dikkat etmemiz gereken diğer bir nokta ASP.net Session ile Nhibernate Session kavramlarını karıştırmamamız gerektiğidir.Birbirinden farklı olan bu kavramları ileride beraber kullanacağız.
Uygulama
Ufak ufak uygulamamıza geçip yukarıda bahsetteğimiz konuları kod üzerinde görelim.Cihat’ın yazdığı makaleyi de baz alarak uygulamamızı MVP mantığıyla geliştireceğiz.
Bir sanal okul uygulaması geliştirceğiz.Aşağıdaki diagramda da veritabanı tablolarımızın biribirleriyle ilişkilerini görebilirsiniz.İlişkileri kısaca özetlersek;


  • Bir okul içinde birden fazla sınıf olabilir.Bir sınıfın bir okulu vardır.

  • Bir sınıfın birden fazla öğrencisi olabilir.Bir öğrencinin bir sınıfı vardır.

  • Bir öğrencinin birden fazla dersi olabileceği gibi bir dersin birden fazla öğrencisi olabilir.Burda dikkat ederseniz many-to-many ilişki kurmak yerine çapraz tablo kullandık.(n-1<=> 1-n = n-n)


Uygulamamızın Model-View-Presenter yaklaşımı ile geliştireceğiz.Solution içerisinde yer alan projelerimiz şöyle:

SanalOkul: Web uygulamamız
SanalOkul.DAO: Nhibernate ile veritabanı erişim yapacağımız proje.Nhibernate’i kullanabilmemiz için aşağıdaki dll’lerin projeye referans olarak eklenmesi gerekir.
–>Iesi.Collections.dll
–>Castle.DynamicProxy.dll
–>log4net.dll
–>NHibernate.dll
SanalOkul.Domain: Modellerimizin bulunduğu proje
SanalOkul.Presenter:Presenter yapısının bulunduğu proje

Nhibernate’in web uygulamasında çalışabilmesi için hbm.xml uzantılı mapping dosyalarımızın “Build Action” değerini “embedded resource” yapmamız gerekmektedir.Aksi takdirde Nhibernate ilişkilendirmenizi yapamaz.

Şimdi nhibernate konfigurasyon ayarlarımızı set edelim.
Bunun için iki farklı yöntem mevcut.Direk kod içerisinden bu ayarlar set edilebileceği gibi bir xml dosyası üzerinden -ki tavsiye edilen yöntem budur- set edilebiliyor.

using System.Collections.Generic;
 using NHibernate;
 using NHibernate.Cfg;
 using SanalOkul.Domain; 
 namespace SanalOkul.DAO
 {
     public class OkulDAO:IOkulDao
     {
         private static ISessionFactory _sessionFactory;
         public OkulDAO()    
         {
             Configuration config = new Configuration();
             config.SetProperty("hibernate.connection.provider" , "NHibernate.Connection.DriverConnectionProvider");
             config.SetProperty("hibernate.dialect","NHibernate.Dialect.MsSql2005Dialect");
             config.SetProperty("hibernate.connection.driver_class","NHibernate.Driver.SqlClientDriver");
             config.SetProperty
("hibernate.connection.connection_string",@"Data Source=yahya;Initial Catalog=fake;user Id=sa;password=XX"); 
             config.AddAssembly("SanalOkul.Domain");
             _sessionFactory = config.BuildSessionFactory();
         }    
     }
 }

Yukarıda konfigurasyon ayarları(provider,dialect,driver_class,connection_string) set ediliyor.Daha sonra assembly set ediliyor-Bildiğiniz gibi “SanalOkul.Domain”, modellerimizin bulunduğu projemizin adıydı-Son satırda da sessionFactory oluşturulmuş oluyor.

config.AddAssembly("SanalOkul.Domain");
config.AddClass("Ogrenci").AddClass("Sinif").AddClass("Ders");

Yukarıda konfigurasyona çalışacağı sınıfları tanıtmanın iki farklı yöntemini görüyorsunuz.Biz üstteki kullanımı tercih ediyoruz.
Şimdi bu ayarları bir xml dosyası üzerinden yapalım.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="connection.connection_string">Data Source=yahya;Initial Catalog=fake;user Id=sa;password=XXX</property>
    <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
    <mapping assembly="SanalOkul.Domain"/>
  </session-factory>
</hibernate-configuration>

Bu dosyayı(hibernate.cfg.xml) web uygulamamızın \bin klasörü altına koyuyoruz.Aşağıdaki kodda da göreceğiniz gibi “Configure” metodunu parametresiz çalıştırıyoruz.Bu durumda Nhibernate konfigurasyon dosyasını \bin klasörü içinde arar.

Değişikliklerden sonra OkulDao sınıfımıza bir göz atalım.

using System.Collections.Generic;
using NHibernate;
using NHibernate.Cfg;
using SanalOkul.Domain;

namespace SanalOkul.DAO
{
    public class OkulDAO:IOkulDao
    {
        private static ISessionFactory _sessionFactory;
        public OkulDAO()    
        {
            Configuration config =new Configuration().Configure();
            _sessionFactory = config.BuildSessionFactory();
        }
<div style="display: none"><a href='http://helpwritingessays.net/'>help writing a essay for college</a></div>     }
}

Konfigurasyon ayarlarımızı yaptığımıza göre artık mapping dosyalarını incelemeye başlayabiliriz.Aşağıda Okul sınıfını ve mapping dosyasını görebilirsiniz.

using System.Collections.Generic;
namespace SanalOkul.Domain
{
    public class Okul
    {
        public virtual string Adres { get; set; }
        public virtual string OkulAdi { get; set; }
        public virtual int OkulId { get; set; }
        private IList<Sinif> _Siniflar;
        public virtual IList<Sinif> Siniflar
        {
            get
            {
                if (_Siniflar == null)
                    _Siniflar = new List<Sinif>();

                return _Siniflar;
            }
            set
            {
                _Siniflar = value;
            }
        }
    }
}
 <?xml version="1.0" encoding="utf-8" ?>
 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" >
   <class name="SanalOkul.Domain.Okul,SanalOkul.Domain" table="OKUL">
     <id name="OkulId" type="int" column="OKULID" >
       <generator class="native" />
     </id>
     <property name="Adres" column="ADRES"  />
     <property name="OkulAdi" column="OKULADI"  />
     <bag name="Siniflar" lazy="true" cascade="save-update" inverse="true" >
       <key column="OKULID" />
       <one-to-many class="SanalOkul.Domain.Sinif,SanalOkul.Domain"  />
     </bag>
  </class>
 </hibernate-mapping>

3.)Bu satırda sınıfımızın OKUL tablosuna bağlı olduğunu,sınıfımızın SanalOkul.Domain assembly’si altında olduğunu belirtiyoruz.
4.)Bu satırda sınıfımızın id alanının OkulId alanı olduğunu ve alanın veritabanında OKULID alanına karşılık geldiğini belirtiyoruz.
5.)Bu satırda id alanının set edilme metodunu belirtiyoruz.Aklıma gelmişken önemli bir kuralı söyleyelim.Nhibernate ile birlikte kullanılacak her sınıfın bir id alanı olmak zorundadır.Nhibernate, sınıfları bu id alanları üzerinden takip eder.
Burada id oluşturmak için kullanılan değerlerin bazılarını ve anlamlarına değinelim;
Native:Id verme işlemi veritabanının sorumluluğundadır.Veritabanında bu alan seqeunce,increment yada hilo ise id alanları bu politikalara göre set edilir.
Assigned:Id verme işlemi tamamen geliştiricinin insiyatifindedir.Nesne session’a gönderilmeden önce(session.save) id alanı mutlaka set edilmelidir.
Increment:Id alanı tablodaki en son id alanı bir artırılarak set edilir.
Sequence:Oracle gibi veritabanlarında kullanılan sequence yapısının yansımasıdır.Aşağıdaki gibi kullanılır.

<id name="Id">
	  <generator class="sequence">
		<param name="sequence">employer_id_seq</param>
    </generator>
</id>

Buradaki “employer_id” Veritabanındaki sequence’in adıdır.
Şahsen id verme işleminin veritabanına bırakılması taraftarıyım.Bu yüzden generator class olarak “native” kullanmayı tercih ediyorum.
7.-8.)Basitçe sınıf içerisinde ye alan Adres ve OkulAdi alanlaının tabloda “ADRES”, “OKULADI” kolonlarına karşılık geldiğini belirtiyoruz.
9.)Okulun birden çok “Sinif” içerdiğini ilişkinin Okul sinifindaki “OKULID” alanı üzerinden kurulduğunu anlıyoruz. “Lazy” kavramına performans kapsamında değineceğiz.
Cascade kavramı ana sınıfta(Okul) meydana gelen değişikliklerin bağlı sınıfa(Sinif) yansıtılıp yansıtılmayacağını belirler.
Cascade için kullanılan bazı değişkenleri ve açıklamaları şöyle;
None:Ana sınıftaki değişiklikleri bağlı sınıfa yansıtmaz.
Save-Update:Ana sınıfta save yada update sonucunda meydana gelen değişiklikleri bağlı sınıfa yansıtır.
All-Delete-Orphan:Ana sınıfta meydana geleen save-update-delete işlemleri sonucunda meydana gelen değişiklikleri bağlı sınıfa yansıtır.Ana sınıf silindiğinde bağlı tüm yavru kayıtlar da silinir.Dikkatli kullanılması gereken bir yöntemdir.
Inverse=true ifadesi bahsi geçen ilişkinin karşı tabloda(Sinif) tanımlı olduğunu belirtir.Foreign key ilişkisinin Sinif tablosunda yer aldığını gösterir.

Şimdi de ilişkinin öbür tarafına geçip “Sinif” sınıfını inceleyelim

using System.Collections.Generic;
namespace SanalOkul.Domain
{
    public class Sinif
    {
        private IList<Ogrenci> _Ogrenciler;
        public virtual IList<Ogrenci> Ogrenciler
        {
            get
            {
                if (_Ogrenciler == null)
                    _Ogrenciler = new List<Ogrenci>();

                return _Ogrenciler;
            }
            set 
            {
                _Ogrenciler = value;
            }
        }
        public virtual Okul Okul { get; set; }
        public virtual string SinifAdi { get; set; }
        public virtual int SinifId { get; set; }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" >
   <class name="SanalOkul.Domain.Sinif,SanalOkul.Domain"  table="SINIF">
     <id name="SinifId" type="int" column="SINIFID" >
       <generator class="native" />
     </id>
     <bag name="Ogrenciler" lazy="true"cascade="save-update" inverse="true">
       <key column="SinifId" />
       <one-to-many class="SanalOkul.Domain.Ogrenci,SanalOkul.Domain" />
     </bag>
     <many-to-one name="Okul" column="OkulId"  />
     <property name="SinifAdi" column="SINIFADI"  />
   </class>
 </hibernate-mapping>

Burada Okul sıfınının mapping dosyasından farklı olarak 10.satırı inceleyeceğiz.
10.)Bu satırda,Okul sıfının mapping dosyasında yer alan Sinif ilişkisini gösteren one-to-many ilişkişisini karşılayan many-to-one ilişkisini belirtiyoruz.

Artık kod yazmaya başlayalım.
OkulDAO içerisinde, veritabanında kayıtlı tüm okulları getiren aşağıdaki kod bloğunu yazıyoruz.

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

3.)Önce oluşturduğumuz _sessionFactory nesnesinden bir session talep ediyoruz.
4.)Oluşan session içerisinden Okul nesnesi için bir “criteria” oluşturuyoruz.
5.)Criteria’yı çalıştırıp dönen değeri de Okul tipinde IList’e atıyoruz.
Burada neden List değil de IList kullanıyoruz diye sorabilirsiniz.Nhibernate’in dönüş değeri IList olduğu için bu şekilde kullanıyoruz.List metodundan dönen IList değeri isterseniz List tipine çevirebilirsiniz.

Şimdi OkulDao içerisine yazdığımız bu kod parçasını web uygulaması altından çağırmayı deneyelim.

using System;
using System.Collections.Generic;
using SanalOkul.DAO;
using SanalOkul.Domain;
using SanalOkul.Presenter;

public partial class _Default : System.Web.UI.Page,IOkulView 
{
    private OkulPresenter presenter;
    protected void Page_Load(object sender, EventArgs e)
    {
       presenter=new OkulPresenter(new OkulDAO(),this);
       presenter.Init();
    }
}

Şimdi presenter sınıfımıza bakalım.

 using System.Collections.Generic;
 using SanalOkul.DAO;
 using SanalOkul.Domain;

 namespace SanalOkul.Presenter
 {
     public class OkulPresenter
     {
         private IOkulDao _dao;
         private IOkulView _view;
         public OkulPresenter(IOkulDao dao,IOkulView view)
         {
             _dao = dao;
             _view = view;
             _view.Presenter = this;
         }
         public Init()
         {
             IList<Okul> liste= _dao.GetAllOkul();
         }
     }
 }

19.)Bu satırda kendisine default.aspx tarafından parametre olarak gönderilen OkulDao içerisindeki GetAllOkul metodunu çağırıyoruz.

İşlem sonunda gelende değerleri watch edelim:


Yukarıda gördüğünüz gibi iki adet okul nesnesi döndü.Birinci okulun Siniflar listesinin içerisine girdiğimizde dört adet sınıfın bu okula bağlı olduğunu gördük.Dikkat edilmesi gereken diğer nokta ise “Sinif” nesnesinin “Okul” property’si olması ve bu property’nin de ait olduğu okulu göstermesi durumudur.Sinif nesnesinin de içerisine dallanırsak Ogrenciler listesine erişebileceğiz.“İlişki” kelimesinin bize söylediği şey tam olarak bu…

Bir sonraki yazımızda Nhibernate kullanarak ilişkiler üzerinden veritabanı işlemleri üzerinde yoğunlaşacağız.

23 thoughts on “NHibernate

  1. Pingback: Yazılım Mühendisliği » Nhibernate-4

  2. Barbaros

    Bu makaleyi okuduktan sonra denemeler yaparken aklıma bir soru takıldı.

    Mesela bir House nesnesi var ve bu nesnenin harita üstündeki konumunu tutan bir MapLocation nesnesi var.
    Db de House tablosunda MapLocationId_FK yok fakat MapLocation tablosunda HouseId_FK var. Bu durumda “owner of the relationship” MapLocation nesnesi oluyor, FK yı taşıdığı için.

    Ben bir tane House nesnesi çektiğim zaman onun içinde MapLocation ında getirmeye çalışıyorum.
    Bir evin bir tane koordinatı olur, bir koordinatında bir evi olur.
    Sanki one-to-one ilişki gibi ama one-to-one hiç önerilmeyen birşey, okuduğum kadarıyla.
    Yani Ev nesnesini çekerken MapLocation ını da getirmek için Bag de oluşturmam saçma. Çünkü evin birden fazla MapLocation ını olmayacak yani koleksiyon da değil.

    İşin içinden çıkamadım, yardımcı olursanız sevinirim.

  3. Tuna Toksoz

    NHibernate sessionunu dao icine gommek iyi bir fikir degil. Farkli daolar olunca farkli farkli sessionfactory olmasi gerektigi gibi bir izlenim oluyor fakat sessionfactory’nin yaratilma islemi pahali bir islem.

  4. yahya koç

    Muhakkak Nhibernate session’ı DAO içine gömülmüyor.Yazının ilerleyen bölümlerini okursanız kaynak koda
    ulaşabilirsiniz.Bir sonraki yazıda Session Yönetimi anlatılıyor.

  5. Pingback: Çok .Net : Alt.Net Başlangıç Klavuzu | Can HANHAN

  6. Ercüment Pullukçu

    Merhabalar ben yeni bir (C# Vs2005 WinForms) projemde NHibernate (2.1.0.1002) kullanıyorum. Lakin session.CreateQuery(“Update TABLE_1 Set Aktif=1 Where Id=1″).ExecuteUpdate() komutu kullanamadım. Kullanım amacım Update veya delete statement leri kendim uygulamak istiyorum. Aksi takdirde komut işlem süresi epeyce uzun sürüyor. İnternete de bakındım fakat örnek birşeyler bulamadım. Yardımlarınız için şimiden teşekkürler.

  7. Emre

    Hocam “.Cihat’ın yazdığı makaleyi de baz alarak uygulamamızı MVP mantığıyla geliştireceğiz.” burda MVC mi demek istedin acaba ?

  8. Pingback: » Nhibernate-4

  9. Cenk

    Slm
    IList altkumesi olan bi POCO var.Runtimeda FetchMode.Select ile cekiyorum
    Sıkıntım indexten kaynaklanıyor.Altnesnemin Id si autoinc ve index olarak bu degerı verıyorum
    Master Nesneyi doldurdugumda Detailde listede Nulllar bulunuyor nası bi mapping yapmalıyım

    HasMany(o => o.CListe)
    .KeyColumn(“SysFrmId”)
    //.AsIndexedCollection(“Id”,o=>o.GetIndexMapping())
    //.AsSet();
    .Inverse()
    .NotFound.Ignore()
    .AsList(index => index.Column(“Id”));

  10. Eren

    Nhibernate’in web uygulamasında çalışabilmesi için hbm.xml uzantılı mapping dosyalarımızın “Build Action” değerini “embedded resource” yapmamız gerekmektedir.Aksi takdirde Nhibernate ilişkilendirmenizi yapamaz.

    Şu kısım kaybettiğim 3 saati bana geri verdi. Teşekkürler.

  11. Yasin ORUÇ

    Bu nhibarnate biraz zor bir kullanıma sahip değil mi, açıkçası hbm dosyası konfigürasyon filan, kullanmadım henüz belki ama, açıkçası bana biraz fazla abartılmış gibi geldi, arka planda bence çok fazla performans kaybı yaşanıyor gibi ve bence herkes bu tip çalışmaları yapabilir…

  12. M. Cihat Altuntaş Post author

    @Yasin
    Hbm işlemleri yapmaya FluentNHibernate kullanarak artık gerek kalmıyor mapping işlemleri çok kolay ve rahat.
    Ayrıca teknolojiyi kullanmadan yorum yapmak doğru değil bu yüzden NHibernate’i enterprise uygulamalarda kullanan biri olarak düşüncenin yanlış olduğunu söyleyebilirim.

    Ayrıca daha iyisini yazarım demek lafta çok kolay ama işin içine girince ne kadar zor olduğunu göreceksindir. NHibernate’i ile aynı kalitede bir ürünü ne kadar sürede yazarsın bir fikir olsun diye şu linke bakabilirsin https://www.ohloh.net/p/nhibernate. Yaklaşık olarak 600.000 satır ve 162 adam yılı. Ben tekrar düşün derim :)

  13. Yasin ORUÇ

    Zaten kullanmadığımı bilerek yazdım ki kimsenin yanlış bir fikire kapılmasını sağlamayım diye :), kullanmadan yorum yapmak da istemem aslında ama genel de nhibernate i araştırdım FluentNHibernate hakkında da pek bi bilgim yok, okuduğum kadarıyla da kullanımı daha basitmiş, ancak ben kendim için geliştirdiğim bir dll im vardı (sonra kullanmaktan vazgeçip yapısını bozdum, şimdi tekrar ve daha gelişmiş bir şekilde yapmaya karar verdim :) ) ve onu generic kullanarak geliştirdim ve açıkçası kullanımı da çok basit, yapılması gereken tek şey bi nesne örneği oluşturmak ve crud işlemlerini rahatça x.Insert, x.Select, x.Update, x.Delete şeklinde metodlar ile yapıyordum…
    Hani ben geçen yıl csharp’ı geçen yıl bi kursa giderek tanıdım ve ben bile daha 3. ayımda öyle bir şey yapabiliyorsam birilerinin bu tip uygulamaları çok çok daha kullanışlı hale getirmesi lazım…

  14. huseyin

    hocam
    ogrenci tablosu icin ogrenci.cs ve ogrenci.hbm.xml yok.ayrıca ders.cs ve ders.hbm.xml de yok
    bilierek mi yazmadınız?_

  15. Görkem

    Hocam elinize sağlık çok faydalı bir makale, acaba solution dosyasını hala mevcutsa almamız mümkünmü?

  16. enginneer

    ayrıca şunuda belirtmek isterim ki şuana kadar gördüğüm okuduğım en iyi nhibarnate makalesidir.teşekkürler cihat altuntaş…

  17. MEhmet

    Okumuş olduğum en mükemmel ORM giriş dökümanı. Diğerleride çok kaliteli. Ülkenin bu şekilde özgün ve kaliteli bloglara ihtiyacı var. Elinize sağlık.

Comments are closed.