Menu

9 Mayıs 2009 Cumartesi

Entity`de inheritance

Java Entity classları, Java Object Mapping teknolojisi sayesinde database deki tabloları oluşturmakta, bu da aslında object oriented ın nimetlerinden olan polymorphism’in nasıl tablolara yansıdığı sorusunu akla getirmekte. Teorik bilgim olmasına rağmen, bu yazımı yazarken bazı küçük testler yaparak teorik bilgimin ne kadar doğru olduğunu göreceğim.
Şimdi işe deneme amaçlı bir entity sınıfı yazarak başlayayım ve jpa daki default inheritance türü olan SINGLE TABLE ı incelemeye başlayalım.
AbstractEntity sınıfı
package Entities;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name="Abstract_Entity")
@Entity
public class AbstractEntity implements java.io.Serializable {

@Id
private Long id;

private String commonAttribute;


public String getCommonAttribute() {
return commonAttribute;
}

public void setCommonAttribute(String commonAttribute) {
this.commonAttribute = commonAttribute;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

}
yukarıda basit bir sınıf yarattım, bu sınıf diğer sınıfların “extend” edeceği, super sınıf. Annotation’larla default değer değiştirilmediyse jpa nın kullanacağı inheritance strategisi single table, şimdi diğer alt sınıfları da yaratalım.
package Entities;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.OneToMany;

@Entity
public class ATypeEntity extends AbstractEntity implements java.io.Serializable{
@OneToMany
private List attributes = new ArrayList() {};

public List getAttributes() {
return attributes;
}

public void setAttributes(List attributes) {
this.attributes = attributes;
}

}

package Entities;

import javax.persistence.Entity;
@Entity
public class BTypeEntity extends AbstractEntity implements java.io.Serializable{
private int bTypeEntityAttribute;

public int getBTypeEntityAttribute() {
return bTypeEntityAttribute;
}

public void setBTypeEntityAttribute(int bTypeEntityAttribute) {
this.bTypeEntityAttribute = bTypeEntityAttribute;
}
}
package Entities;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class AttributeEntity implements java.io.Serializable {
@Id
private Long id;

private String attribute;

public String getAttribute() {
return attribute;
}

public void setAttribute(String attribute) {
this.attribute = attribute;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}

ATypeEntity ile BTypeEntity AbstractEntity sınıfından türetildiler, yani AbstractEntity nin id ve commonAttribute değişkenleri her ikisinde de var. ATypeEntity nin bunun yanında one to many bir ilişkisi var, BTypeEntity nin ise kendine özel bTypeEntityAttribute adında bir değişkeni var.
Single Table stratejisinden beklediklerim!
Tüm bu ilişkileri tek bir tabloda toplamalı
Tablo içinde bir sütunla tablodaki satır’ın hangi objeye ait olduğunu söylemeli
  • Tüm sınıflar aynı tabloda tutulduğu için mesela BTypeEntity e ait bir değişken, ATypeEntity e ait bir satırda boş bırakılmalı.
Şimdi programı çalıştırıp, görelim

database schema
Yukarıda tam istediğim şekilde database yapısını oluşturdu. Bir kaç değer girdiğimde ise Dtype sütunun satırın ait olduğu objenin sınıfını belirtmek için kullanıldığını, diğer alanların ise boş kaldığını gördüm, böylece “single table” aslında tam beklediğim gibi çalısıyor.
Şimdi biraz bu strateginin avantajları dezavantajları üzerinde düşünelim,
Buradaki en büyük dezavantaj sınıfların sahip olmadıkları değişkenler içinde database de yer tutmaları ayrıca database deki satırların büyük boyutlu olması da iyi birşey değil. Ama yine de bu yöntem gayet basit ve tek satırla tüm bilgi alınabiliyor.
Not nullable sütunlar kavramı
Eğer her sınıfın değişkenleri tek tabloda tutuluyorsa, bir sınıf kendisine ait olmayan değişkenleri null yani boş olarak bırakıyor, bu durumda “not nullable” yani boş bırakılamaz değişkenler tanıtamıyoruz çünkü diğer bir sınıf kendisine ait olmayan bu değişken için “null” olarak tabloya girecek.
Simdi ikinci yönteme geçelim, “JOINED”
Burada yöntemimizi değiştirebilmek için tek yapmamız gereken, AbstractEntity sınıfına @Inheritance annotation ı ile strategimizi değiştirmek.
package Entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import  javax.persistence.InheritanceType;
import javax.persistence.Table;

@Table(name="Abstract_Entity")
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class AbstractEntity implements java.io.Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String commonAttribute;

public String getCommonAttribute() {
return commonAttribute;
}

public void setCommonAttribute(String commonAttribute) {
this.commonAttribute = commonAttribute;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

}

Şimdi benim JOINED strategisinden beklediklerim,
  • Her sınıfın ayrı bir tablosu olacak
  • Her sınıfın ayrı tablosunda sadece kendisiyle ilgili değişkenler tutulacak
  • Tablolar kendi aralarında JOIN sütunuyla ilişkilendirilecek.
Kodu çalıştırdığımda karşıma şu şekil bir database yapısı çıkıyor.

Database Schema Joined

Resimden görüldüğü gibi her sınıfın ayrı tabloları oluşmuş ve her tablo kendi sınıfıyla ilgili değişkenleri tutuyor. Bu yapıda mesela ATypeEntity sınıfına ait bir obje istendiğinde, AbstractEntity ve ATypeEntity e ait tablolar aralarında join yaparak bu obje oluşturulacaktır.

Bence bu yöntem object oriented mantığına daha çok uymakta, fakat her obje için join yapılmakta, eğer sınıflar arasında fark fazlaysa bence bu yöntem ilk yöntemimiz olan “single table” a göre daha uygun. Fakat her obje için sorgunun maliyeti joinlerden dolayı biraz fazla.

JPA nın inheritance için sunduğu son yöntem ise “Table Per Class” yani Türkçesiyle her sınıf için bir tablo. Bu yöntemden beklediklerim,
  • Tüm sınıfların ayrı tablosu olması fakat ayrıca bir sınıfa ait bir tablonun türediği sınıfa ait olup kendisine geçen değişkenler içinde yer tutmasıdır.
Burada aklıma gelen soru ise aslında eğer gerçekten her sınıfın tablosu üretilecekse bizim AbstractEntity sınıfınında tablosu üretilecek mi? Benim örneğimde soyut (abstract) bir sınıf değil ama gerçekte bu sınıfta soyut olabilirdi. Burada AbstractEntity sınıfını soyut sınıf yapıp, inheritance strategimi değiştirmeye karar veriyorum ki sorularım cevap bulabilsin ve kodumu aşağıdaki şekilde değiştiriyorum.
package Entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import  javax.persistence.InheritanceType;
import javax.persistence.Table;
@Table(name="Abstract_Entity")
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractEntity implements java.io.Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String commonAttribute;

public String getCommonAttribute() {
return commonAttribute;
}

public void setCommonAttribute(String commonAttribute) {
this.commonAttribute = commonAttribute;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

}

Table per class inheritance is not supported. Entity class [class Entities.AbstractEntity]. Hatasını alıyorum. Bu mesaj bana toplink in “table_per_class” ı desteklemediği izlenimine kapılıyorum. Yazımı burada sonlandırıyorum, çünkü elimde toplink ten başka jpa implementasyon paketi yok ve sorularım cevapsız kalıyor ne yazık ki.

1 yorum: