Yazılım Mühendisliği

Ekim 21, 2008

Nhibernate-5

Filed under: Nhibernate,ORM — Yahya KOÇ @ 8:43 am
Tags: , ,

Bu yazımızda daha spesifik durumlar üzerinde duralım.

Önceki Yazılar


Object references an unsaved transient instance – save the transient instance before flushing: Domain.Kurum hatası:
Aslında hata kendini çok güzel anlatıyor.”Bir nesneyi kaydediyorsun ama onun içinde transient-veritabanında karşılığı olmayan-bir nesne var onun da tipi ‘Kurum’dur.Önce onu kaydet”.
Bu hatayı veren kodumuz şu şekilde:

IKY_UYE uye = new IKY_UYE();
uye.Ad="recep";
uye.Insert();

Uye sınıfında Kurum sınıfına bağlı “Kurumu” adında bir özellik var. Doğrusu şu şekilde olmalıydı.

Kurum krm=KurumDao.GetById(2);
IKY_UYE uye = new IKY_UYE();
uye.Kurumu=krm;
uye.Ad="recep";
uye.Insert();

Class Lazy,List Lazy
Mapping dosyalarını inclediğinizde hem sınıflar için hemd e listeler için bir lazy özelliği geçiyor.

  <class name="SanalOkul.Domain.Okul,SanalOkul.Domain" table="OKUL" lazy="true">
    <bag name="Siniflar" lazy="true" cascade="save-update" inverse="true" >
       <key column="OKULID" />
       <one-to-many class="SanalOkul.Domain.Sinif,SanalOkul.Domain"  />
     </bag></class>

Listeler üzerinde lazy kavramına Nhibernate-2 yazısında değinmiştik.Kısaca tekrar edelim.Öncelikle Lazy özelliğinin varsayılan değeri “true” dur.Yani biz lazy özelliğini xml dosyamızda yazmazsak değerleri otomatik olarak true set edilecektir.Listelerde genel olarak bu özelliği “lazy=true” şeklinde kullanıyoruz.Böylece ihityacımız olduğunda listeyi doldurmuş oluyoruz.Bu şekilde performans düşüşünü engellemiş oluyoruz.Bazı durumalrda bu özelliği “lazy=false” olarak da kullanmak durumunda kalabiliriz.
Şimdi class üzerindeki lazy üzerine yoğunlaşalım.Sınıf üzerindeki lazy özelliği,liste üzerindeki lazy özelliğinden bağımsızdır.Biribirlerini etkilemezler.Sınıf içerisinde “lazy=true” ifadesini kullandığımızda o sınıfı özellik(property) olarak kullanan sınıf o özelliğe erişmek istediğinde devreye girer.bunu bir örnek üzerinde inceleyelim:
=>lazy false:

 <class name="SanalOkul.Domain.Okul,SanalOkul.Domain" table="OKUL" <strong>lazy="false"</strong>

Şöyle bir kodumuz olsun.Sayfa ilk açıldığında bir öğrenciyi bulup Asp.Net Session’a atalım.Başka bir buton eventi altında bu öğrencinin okulunun adına erişelim.

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            Ogrenci ogr = new Ogrenci();
            ogr=OgrenciDao.GetById(75);
            Session["ogr"] = ogr;
        }
    }

   protected void Button1_Click(object sender, EventArgs e)
    {
        Ogrenci ogr = (Ogrenci)Session["ogr"];
        string strOkulAdi = ogr.Okulu.OkulAdi;
    }

Kodu çalıştırıp butona tıkladığımızda 4.satıra gelindiğinde ogr nesnesinin lazy’si false olan Okulu alanına erişerek okul adını okuyabileceğiz.
İkinci duruma geçelim:
=>lazy true:

 <class name="SanalOkul.Domain.Okul,SanalOkul.Domain" table="OKUL" <strong>lazy="true"</strong>

Mapping dosyasını yukarıdaki gibi değiştirelim.Kodumuzu tekrar çalıştırıp butona tıkladığımızda 4.satırda hata alacağız:”Could not initialize proxy-the owning session was closed
Peki bu hatayı alacağımızı bildiğimiz halde neden “lazy=true” kullanıyoruz?Performansı en üstte tutabilmek için.Dikkat edin burda “lazy=false” ifadesi Ogrenci sınıfında değil Okul sınıfında.Hatayı aldığımız yer de ogr.Okulu.OkulAdi;…

Öyleyse bu hatadan nasıl kurtulabiliriz?
Buton click eventinin altındaki kodu şu şekilde değiştiriyoruz.

   protected void Button1_Click(object sender, EventArgs e)
    {
        Ogrenci ogr = (Ogrenci)Session["ogr"];
        OgrendiDao.Refresh(ogr);
        string strOkulAdi = ogr.Okulu.OkulAdi;
    }

4.satırda aslında Session.Refresh(ogr); yapıyoruz.Böylece bir önceki eventte(PageLoad) yer alan Nhibernate session’ı içinde kalan ogr nesnesini tekrar çekiyoruz.Ayrıntlı bilgi için Nhibernate-2 yazısını incelemenizi tavsiye ederim.

A different object with the same identifier value was already associated with the session: 1, of class Kurum
Bu hata da aynı Nhibernate session’ı içinde aynı id’yi taşıyan birden fazla nesne var demektir.falz olan nesneler session’dan uzaklaştırılmalıdır.Bunu da Session.Evict(okl) şeklinde sağlıyoruz.

Composite-Id
Nhibernate, nesneleri primary keyler üzerinden işler.Primary key alanı olmayan tablolar nhibernate için uygun değildir.Id özelliği,tek alandan oluşabileceği gibi birden çok alanı içinde barındıran composite-id şeklinde de olabilir.Fakat composite-id mantığı kötü tasarımın ürünüdür.

  <class name="Kurum,CrmDomain"  table="Kurum">
    <composite-id name="ID" class="Kurum_ID,CrmDomain">
      <key-property name="K_TUR" column="K_TUR" />
      <key-property name="K_KOD" column="K_KOD" />
      <key-property name="K_SEHIR" column="T_SEHIR" />
    </composite-id>
<property name="K_ADRES" column="K_ADRES" />
    <many-to-one name="SEHIR" class="CrmDomain.Sehir,CrmDomain"  update=" false" insert="false"  >
      <column name="K_SEHIR" />
    </many-to-one>
  </class>

Yukarıda görüldüğü gibi Kurum sınıfının id özelliği üç alandan oluşuyor(K_TUR,K_KOD,K_SEHIR).Alt tarafta da o kurumun bağlı olduğu şehir ilişkisi belirtilmiş.Bu ilişki de Kurum sınıfının id’sini oluşturan alanlardan K_SEHIR üzerinden kurulmuş.Bu tür kullanım gayet doğal bir kullanım gibi gözükse de çok tercih edilmemesi gerektiğini düşünüyorum.Zira bu durumların yönetimi zordur.Önce kullanımı inceleyelim sonra da alternatif çözüme bakalım.
Nhibernate içinde, composite-id kullanabilmek için composite-id olarak oluşturduğumuz sınıfın “Equals” ve “GetHashCode” virtual metodlarını override etmesi gerekir.

 public class KURUM_ID
    {
        private string _K_TUR;
        public virtual string K_TUR
        {
            get { return _K_TUR; }
            set { _K_TUR= value; }
        }
        private Nullable<long> _K_KOD;
        public virtual Nullable<long> K_KOD
        {
            get { return _K_KOD; }
            set { _K_KOD= value; }
        }
        private Nullable<long> _K_SEHIR;
        public virtual Nullable<long> K_SEHIR
        {
            get { return _K_SEHIR; }
            set { _K_SEHIR = value; }
        }
        public override bool Equals(object other)
        {
            if ((this == other))
                return true;
            if ((other == null))
                return false;
            if (!(other is KURUM_ID))
                return false;
            KURUM_ID castOther = (KURUM_ID)other;
            return ((this.K_TUR == castOther.K_TUR) && (this.K_KOD == castOther.K_KOD) && (this.K_SEHIR == castOther.K_SEHIR) );
        }
        public override int GetHashCode()
        {
            int result = 17;
            result = 37 * result + this.K_TUR.ToString().Length;
            result = 37 * result + this.K_KOD.ToString().Length;
            result = 37 * result + this.K_SEHIR.ToString().Length;
            return result;
        }
    }

Şimdi de kod yazalım.

    KURUM kurum=new KURUM();
    KURUM_ID id=new KURUM_ID();
    id.K_TUR=2;
    id.K_KOD=2645264;
    id.K_SEHIR=34;
    kurum.Id=id;
    kurum.Sehir=SEHIRDAO.GetById(26);
    KURUMDAO.insert(kurum);

Tekrar mapping dosyamıza dönelim

    <many-to-one name="SEHIR" class="CrmDomain.Sehir,CrmDomain"  update=" false" insert="false" >
      <column name="K_SEHIR" />
    </many-to-one>

update=”false” insert=”false” ifadesine dikkat edelim.Bu ifade bu sınıf için bir insert ya da update cümlesi oluşturulduğunda ilişkide geçen bu alanı(K_SEHIR) alanını sql cümlesi içinde kullanma anlamına gelir. Çünkü bu alan,asıl sınıfın composite-id alanlarından biri olduğu için insert,update cümlesinde zaten geçiyor.insert=”false” update=”false” ifadesini kullanmadığımızda K_SEHIR alanı sql cümlesinde tekrar geçecek ve “duplicate column” hatası alacağız.
Peki ne zaman insert=”true” update=”true” kullanırız? Many-to-one ilişkimizi ana sınıf üzerinde id olmayan sıradan alanlar üzerinden kurduğumuzda -örneğin K_ADRES- bu ifadeyi insert=”true” update=”true” şeklinde kullanırız.Böylece ilişki içerisinde geçen K_ADRES alanını da insert cümlesine katmış oluruz.

Alternatif çözümümüz, composite-id kullanmamaktır.K_TUR,K_KOD,K_SEHIR alanlarının üçünü birden id yapmak yerine üç alanı karşılayan K_ID adında yeni bir alan oluşturulur geri kalan bu üç alan kendi içlerinde UNIQUE yapılır.

PK UNIQUE
K_ID K_TUR,K_KOD,K_SEHIR

Böylece ilişkileri de hep id alanı üzerinden kurarak,yönetilebilir bir koda kavuşmuş olacağız.

Yorum yapın »

Henüz yorum yapılmamış.

Bu yazıya yapılan yorumlar için RSS beslemeleri. URI'nin geri izlemesini yap.

Yorum yapın

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Değiştir )

Twitter picture

You are commenting using your Twitter account. Log Out / Değiştir )

Facebook photo

You are commenting using your Facebook account. Log Out / Değiştir )

Connecting to %s

Theme: Rubric. WordPress.com'dan blog alın.

Follow

Get every new post delivered to your Inbox.