24 Ağustos 2012 Cuma

Hibernate ve Foreign Key

Foreign Key Nedir
Foreign key kısaca Parent ve Child tabloların birbirlerine bağlanması amacını taşır.

Aşağıdaki şekilde grouptable bir Parent, story ise Child olarak kullanılmış. Her iki tabloyu birbirine bağlayan bir de foreign key var.




Bir başka benzer örneği de burada buldum.



Hibernate ve One-to-Many Kullanımı

Hibernate ile iki tablo arasındaki foreign key kullanılarak one-to-many ilişkisi kurulmak isteniyorsa önümüzde iki tane seçenek var.

  1. Çift yönlü (bidirectional) One-to-Many. 
  2. Tek yönlü (unidirectional) One-to-Many kullanımı.


One-To-Many İlişkisinin Çift Yönlü (Bidirectional) olması ve Foreign Key kullanması

Çift Yönlü Olması durumunu aşağıda açıklamaya çalıştım.


@Entity
public class Child{

private Long id;
private String childName;
private Parent parent;

     @ManyToOne
     public Parent getParent() {
         return parent;
     }
}
@Entity
public class Parent{

 private Long id;
 private String name;
 private String description;
 private List<Children> children;

 @Id @GeneratedValue
 public Long getId() {
  return id;
 }

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

 @OneToMany(cascade=CascadeType.ALL, mappedBy="parent")
 public List<Children> getChildren() {
  return children;
 } 
}
One-To-Many ve Foreign Key Sütun İsmi
Bu kodda mappedBy kullanımı şu anlama geliyor. Parent ve Child sınıfları ilişkililer ve ilişki için Child sınıfındaki parent alanı için için üretilen foreign key kullanılacak.

Benim verdiğimden farklı örnek bir kodu burada bulabilirsiniz.

Yukarıda verilen örnek kodda @JoinColumn kullanılmamış. Bu gösterimin kullanılmadığı durumda üretilen foreign key sütununun isminin nasıl üretildiği şöyle açıklanmış.
If no @JoinColumn is declared on the owner side, the defaults apply. A join column(s) will be created in the owner table and its name will be the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column(s) in the owned side.
Bu açıklamada dikkat edilmesi gereken bir nokta şu. Object Oriented dünyada "owning side" Parent iken, SQL dünyasında foreign key'i içeren taraf "owning side" oluyor (yani Child) . Bu durum burada daha iyi açıklanmış.

Yukarıdaki açıklamaya göre Child nesnesi "owning side" olduğu için @JoinColumn kullanılmazsa bizim üretilecek foreign key sütununun ismi parent_id olacaktı.

Böyle bir konfigürasyon ilk defa yaratılıp save() metodu çağrılırsa üretilen SQL şuna benzer bir şey oluyor.

Hibernate: insert into PARENT (x, y, z) values (?)
Hibernate: insert into CHILD (x, y, z, parent_id) values (?, ?, ?, ?, ?)
Hibernate: insert into CHILD (x, y, z, parent_id) values (?, ?, ?, ?, ?)

Bu şekilde oluşturulan ilşkilerde N+1 select problemi de çok rastlanılan bir durum. Bu konuyla ilgili Hibernate ve N+1 Select problemi başlıklı yazıyı da okuyabilirsiniz.

One-To-Many ve CascadeType (Ard arda İşlemler Seçenekleri)

One-To-Many ilişkilerde yeni parent ve child nesneleri kaydederken foreign key kısıtından dolayı hata almamak için asgari CascadeType.PERSIST seçeneğinin tanımlı olması lazım. Böylece yukarıdaki üretilen SQL örneklerinde de görülebileceği gibi önce parent nesnesi kaydedilir ve onun birincil anahtarı child nesnelerde foreign key olarak kullanılır.

One-To-Many İlişkisinin Tek Yönlü (Unidirectional) olması ve Foreign Key kullanması

Bu durumu açıklayan güzel bir örneği burada buldum. Bir başka örnek ise burada.

Parent sınıfa normalde eklenmesi gereken
mappedBy özelliği yerine bu sefer sadece @JoinColumn gösterimi ekleniyor.

@Entity
public class Child{

private Long id;
private String childName;
}
 
@Entity
public class Parent{

 private Long id;
 private String name;
 private String description;
 private List<Children> children;

 @Id @GeneratedValue
 public Long getId() {
  return id;
 }

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

 @OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="parent_id", referencedColumnName="id")
 public List<Children> getChildren() {
  return children;
 } 
  
Bu kullanım şeklinde dikkat edilmesi gereken nokta mappedBy kullanılmadığı için Parent sınfı "owning side"
haline geliyor ve kendi id sütununu Child tablosuna foreign key olarak yazıyor.

Bir diğer nokta ise bu kullanım şekli sadece JPA 2.0 ile destekleniyor.

Bir dikkat edilmesi gereken nokta ise bu kullanım şekil tavsiye edilmiyor.

Kullanılan Collection Sınıfları

Hibernate ile "one-to-many" ilişkisinı kullanırken dikkatimi çeken bir şey oldu.

Kod içinde List, Set, Map gibi collection interface'leri kullanılırken Hibernate bu interfacelerden türeyen kendi sınıflarını kullanıyor. Örneğin org.hibernate.collection.PersistentSet gibi.

2 yorum: