JPA Fetch Stratejileri ve Lazy Loading
JPA içerisinde nesneler arasındaki ilişkilerde verinin getirilmesi ile ilgili davranışlar kendi aralarında farklılık gösterebilmektedir. Bu özelliği sağlayan fetch stratejileridir.
Örnek verecek olursak OneToOne ve ManyToOne ilişkili nesneler asıl nesnenin çağırımıyla birlikte direkt olarak getirilirken, OneToMany ve ManyToMany ilişkili nesnelerde ise ilişkili nesnenin çağırımı ile bilgi çekilmesi sağlanmaktır. Fetch stratejilerinde OneToOne ve ManyToOne default olarak EAGER iken, ManyToMany ve OneToMany ise LAZY stratejisindedir.
Buna göre
LAZY : Tembel yükleme, asıl nesne çağırımı sonrasında lazy ilişkili nesnelerden bilgi getirilmez. Bilgiyi ancak ilişkili nesnenin çağırımı ile birlikte getirir. Burada get metodunun çağırımı ile proxy devreye girer ve ilgili sorguyu gerçekleştirir.
EAGER : Direkt yükleme, asıl nesne çağırımı ile birlikte ilişkili nesne bilgiside getirilir.
Buna örnek verecek olursak Document ve SubDocument isimli iki adet entity miz bulunuyor ve bu entityler kendi aralarinda aşağıdaki gibi ilişkili.
Bu ilişkinin kod tanımı aşağıdaki gibidir.
Buna göre SubDocument tipinde bir nesne getirecek olursa Document nesnesi otomatik olarak yüklenecektir.
Bunu örnekleyecek olursak aşağıdaki örnekte SubDocument tipinde bir nesne getiriyoruz.
Bu durumda yeni sorgumuz aşağıdaki gibi olacaktır.
Bunun sebebi ilişkinin default olarak EAGER olması. Ancak bizim parentDocument a ihtiyacımız yoksa ne yapmalıyız ? Bu durumda ilişki belirtimini aşağıdaki örnekteki gibi LAZY olarak belirlememiz gerekiyor.
Bu durumda yeni sorgumuz aşağıdaki gibi olacaktır.
Aradaki fark parentDocument bilgisinin çekilmemesidir.
Sorgularda Fetch Davranışları
Peki lazy olarak tanımladığımız ilişkiyi bir sorgu içerisinde kullanırsak ne olur ? Örneğin aşağıdaki gibi bir SubDocument listesi alıyoruz ve SubDocument -> Document ilişkisi EAGER durumda.
Bu durumda üretilen sorgu aşağıdaki gibidir.
Peki buradaki sorguda EAGER stratejisi kullanmamıza karşın neden parentDocument lar ayrı sorgular ile getirildi ?
Bu aslında performansa bağlı bir durum. Sorgu içerisinde nesnelerin tekrar edilmemesi için ilişkili Document nesneleri ayrı olarak çekiliyor. Ancak her SubDocument için bu işlem tekrarlanmıyor. Burada 6 tane SubDocument olmasına karşın Document sorgu sayısı 2 dir.
Bunu detaylandıracak olursak subDocument içerisinde 1 id li bir document nesnesi var bu durumda bir sorgu oluşturuluyor ve bu document nesnesi cekiliyor. Ardından ikinci subDocument nesnesine geçiliyor ve buradaki document nesneside yine 1 numaralı id ye sahip. Bu durumda nesne entity context içerisinde managed durumda yer aldığı için tekrar veritabanına gidilmez yani yeni sorgu oluşturulmaz.
Kısacası 100 subDocument nesnenisi çektiniz ve bunlar sadece 2 Document nesnesi ile ilişkili. Bu durumda document için sadece 2 defa veritabanına gidiyor olacaktır.
Fetch Sorgular
Bir ilişkiyi lazy yaptınız ancak tek sorguda çekmek istiyorsanız duruma göre fetch sorgular kullanabilirsiniz. Örnek olarak aşağıdaki sorguda SubDocument ve Document lazy olmasına karşın aşağıdaki fetch query ile EAGER olarak çekiliyor.
Burada oluşan sorgu aşağıdaki gibidir.
Tam tersi olarak Document listesini alırken ilişkili olarak subDocument listesini almak istersek yine fetch join i aşağıdaki gibi bir yöntem ile kullanabiliriz.
LazyInitializationException Hatası
Seam entity yaşam döngüsünü yönetsede bu hata zaman zaman karşımıza çıkabilir. Bunun nedeni lazy yüklenen nesneler bir proxy vasıtasıyla talep edildikleri anda bilgiyi getirir. Ancak bu arada entityManager kapanmışsa veya nesne ile bağı kalmamışsa sorgulama yapamayacağı icin LazyInitializationException hatası üretilir.

Son Yorumlar