JPQL sorgulama dili
JPQL (Java Persistence Query Language) , JPA standardının Entity nesnelerini sorgulamak üzerine tanımladığı bir dil. JPQL, HQL (Hibernate Query Language) ‘e fazlasıyla benzeşir. Bu diller SQL (Structured Query Language) diline hemen hemen benzemelerine karşın, kullandığı argümanlar veritabanı tabloları yerine Entity nesneleridir.
Bu yazıda JPQL dilinden bahsetmek ve örnek sorgulamalarla anlaşılırlığını artırmayı amaçlıyorum.
Şimdi en temel sorgu örneğiyle başlayalım;
Bu sorgu, FROM tümcesinde belirtilen kaynağı (“k” karakteriyle simgelenmiş Kitap türündeki tüm entity nesneleri) tanımlamakta ve SELECT tümcesinden sonraki “k” karakteriyle tüm entity nesnelerini veritabanından çekmeyi sağlamaktadır.
Entity nesneleri yönetilirken JPA standardında yer alan EntityManager ya da Hibernate’ de olduğu gibi Session nesneleri kullanılıyor. Benzer görevleri üstlenen bu nesneler persist(..), merge(..), remove(..), find(…) gibi yordamlar sunarken, aynı zamanda entity nesnelerini sorgulamak amacıyla çeşitli yordamlar da sağlıyor.
Örnek olarak yukarıdaki gösterimde yer alan EntityManager yordamları, JPQL tümceleriyle sorgu nesneleri üretmektedir. Burada iki tip sorgu nesnesi olduğu görülebilir, Query ve TypedQuery<T> . Bunlardan ilki, tipi belirtilmeyen sorgu nesnesi iken, diğeri sorgu nesnesinin Entity tip bilgisini de tanımlamaktadır (Class<T> type gücüyle).
JPQL sorguları eğer NamedQuery değillerse çalışma anında değerlendirmeye alınacaklarından dolayı, çalışma anı istisnaları alınabilecektir, JPQL sözdizimine bu sebeple dikkat edilmelidir.
JPQL sorgulama dili kullanım örnekleri
1) SELECT, FROM : Bu sorgu örneği temel olarak veritabanında bulunan Kitap türündeki entity kayıtlarını sorgular. Bu sorguda tipi belirtilmemiş bir Query nesnesi elde edildiği için, verinin dönüş tipi Object sınıfı türünden olmaktadır. (Çoklu kayıt setleri için List<Object>, tek kayıt için Object veya List<Object[ ]>)
Query kitapSorgusu1 = em.createQuery("SELECT k FROM Kitap k"); List<Object> kitapSorgusuListesi1 = kitapSorgusu1.getResultList(); for(Object li:kitapSorgusuListesi1) logger.log(Level.INFO, "Tüm kitaplar sorgusu 1 : {0}", (Kitap)li);
Bu örnekte birden fazla Kitap entity kümesi elde edileceği için, dönüş türü List arayüzü türünden olmaktadır. Bunu da aslında Query#getResultList yordamı sağlamaktadır. Eğer sorgu tümcesinden birden fazla kayıt hüzmesi döndürülecekse bu yordam kullanılmalıdır.
2) SELECT, FROM : Bu örnek aslında bir yukarıdaki örnekle aynı görevi yürütüyor. Buradaki fark ise elde edilen sorgu nesnesinin tipi belirtilmiş bir TypedQuery<tip> nesnesi olması. Bu sayede elde edilen nesneden doğrudan entity tipine dönük kayıt(lar) döndürülmüş oluyor.
TypedQuery<Kitap> kitapSorgusu2 = em.createQuery("SELECT k FROM Kitap k", Kitap.class); List<Kitap> kitapSorgusuListesi2 = kitapSorgusu2.getResultList(); for(Kitap ki:kitapSorgusuListesi2) logger.log(Level.INFO, "Tüm kitaplar sorgusu 2 : {0}", ki);
3) COUNT tümcesi : JPQL dilinde, bu örnekte olduğu gibi seçici kısımda çeşitli JPQL fonksiyonları kullanılabiliyor. Bu örnekteki COUNT fonksiyonu dönen nesneleri sayarak sonucu geri döndürmektedir. Burada sadece tek bir tamsayı nesnesi elde edileceği için Query#getSingleResult metodu kullanılmaktadır. Dönen tamsayı değeri, gerçek türüne (Long) dönüştürülerek ekrana çıktılanmaktadır.
Query countSorgu = em.createQuery("select COUNT(k) from Kitap k"); Long toplam = (Long)countSorgu.getSingleResult(); logger.log(Level.INFO, "Toplam kitap entity sayisi : {0}", toplam);
4) MAX tümcesi : Bu fonksiyon, sorgudaki dönüş bileşeninin en yüksek değerlikli değerini döndürmektedir. Bu örnekteki k.fiyat alanı Double sınıfı türünde olduğu için, gerçek türüne ayrıca dönüştürülmektedir.
Query maxSorgu=em.createQuery("select MAX(k.fiyat) from Kitap k"); logger.log(Level.INFO, "Max Sorgusu : {0}", (Double)maxSorgu.getSingleResult());
5) WHERE tümcesi : WHERE ifadesi olağan SQL sözdiziminde olduğu gibi dönüş yapılacak nesnelere dönük kısıt getirmektedir. Örneğin: Kitap fiyati 30TL‘ den yüksek tüm kitaplar gibi.
Query buyukturSorgusu = em.createQuery("select k from Kitap k where k.fiyat>30"); List otuzdanBuyuklerListesi=buyukturSorgusu.getResultList(); logger.info("Otuzdan büyük kitaplar "); for(Object otuz:otuzdanBuyuklerListesi) { Kitap kitap=(Kitap)otuz; System.out.println("->"+ kitap); }
6) Çoklu sorgular : JPQL sorgularından seçici kısımda (select) tekil bileşenler çekilebileceği gibi çoklu seçim de kullanılabilir. Örneğin Kitap entity’sinin id ve kitapAdi bilgisi gibi. Çoklu seçim yapılacak bileşenler ( , ) karakteriyle istenilen sayıda belirtilebilir. JPA standardına göre eğer sorgu nesnelerinde çoklu sorgular kullanılırsa, sorgudan dönüş türü Object[] dizisi olarak elde edilmektedir.
Query baziSorgusu = em.createQuery("select k.id,k.kitapAdi from Kitap k"); List<Object[]> baziKitaplar=baziSorgusu.getResultList(); for(Object[] ki:baziKitaplar) logger.log(Level.INFO, "-> {0} -> {1}", new Object[]{ki[0], ki[1]});
7) Çoklu kaynaklardan çoklu sorgu : Bir önceki örnekte olduğu gibi çoklu sorgular tek bir entity kaynağından alınabileceği gibi, FROM ifadesinden sonra belirlenen kaynağa göre farklı kaynaklardan da elde edilebilir. Burada kayıtlar yine Object[] dizisi halinde elde edilir. Fakat bu örnekte olduğu gibi Kitap ve YayinEvi entity nesneleri arasındakiilişki (varsa) sorguda gözetilmez. Eğer entity nesneleri arasınadaki ilişki gözetilerek çoklu sorgular elde edilmek isteniyorsa JPQL dilinin JOIN ifadeleri kullanılmalıdır.
Query hemOndanHemSundan = em.createQuery("select k, y from Kitap k,YayinEvi y"); List<Object[]> hemOHemSuListesi = hemOndanHemSundan.getResultList(); for(Object[] o:hemOHemSuListesi) for(Object oo:o) { if (oo instanceof Kitap) logger.log(Level.INFO, "?> {0}", (Kitap)oo); if(oo instanceof YayinEvi) logger.log(Level.INFO, "?> {0}", (YayinEvi)oo); }
8) GROUP BY ifadesi : GROUP BY ifadesi sorgu cümlelerinde belirli bir entity alanına dönük gruplama işlemini yürütür.
TypedQuery<YayinEvi> grupSorgusu = em.createQuery("SELECT y FROM YayinEvi y GROUP BY y.yayineviAdi", YayinEvi.class); List<YayinEvi> grupListesi = grupSorgusu.getResultList(); for(YayinEvi li:grupListesi) logger.log(Level.INFO, "Grup çiktisi : {0}", li);
9) NEW tümcesi : JPQL sorgularında elde edilen bileşenler bir nesneye giydirilmek isteniyorsa NEW anahtar kelimesi kullanılabilir. Aşağıdaki örnekte olduğu gibi, Kitap entity’sinden döndürülen id, kitapAdi, fiyat, yayinevi bileşenleri “com.kodcu” paketi altındaki YeniKitap nesnesi içine giydirilmektedir. NEW ifadesiyle tam paket adının verilmesi, ve sözdizimine uyan constructor bulunması önemlidir. Bu sorgularla elde edilecek yeni nesne örnekleri yönetimli bir entity nesnesi değil, normal bir Java nesnesi olmaktadır.
Query newSorgu1 = em.createQuery("SELECT NEW "+ "com.kodcu.YeniKitap(k.id,k.kitapAdi,k.fiyat,k.yayinevi)"+ " FROM Kitap k "); List<Object> newSorgu1Listesi=newSorgu1.getResultList(); for(Object obj :newSorgu1Listesi) logger.log(Level.INFO, "YeniKitap sorgu1 : {0}", (YeniKitap)obj);
10) NEW tümcesi : Bu örnekte Kitap nesnesinin id niteliği 2 olan entity kaydı alınarak tek bir YeniKitap nesnesi elde edilmektedir.
Query newSorgu2 = em.createQuery("SELECT NEW " +"com.kodcu.YeniKitap(k.id,k.kitapAdi,k.fiyat,k.yayinevi)" +"FROM Kitap k WHERE k.id=2"); Object newSorgu1TekEntity=newSorgu2.getSingleResult();
11) DISTINCT tümcesi : Eğer döndürülecek kayıtların tekil olanları elde edilmek isteniyorsa DISTINCT tümcesi kullanılabilir.
TypedQuery<YeniKitap> newSorgusu = em.createQuery("select DISTINCT NEW " + "com.kodcu.YeniKitap(k.id,k.kitapAdi,k.fiyat,k.yayinevi)" + "from Kitap k GROUP BY k.kitapAdi",YeniKitap.class); List<YeniKitap> newListesi = newSorgusu.getResultList(); for(YeniKitap yeni:newListesi) logger.log(Level.INFO, "->{0}", yeni);
12) INNER JOIN veya JOIN tümcesi : Entity nesneleri arasındaki ilişkiye göre sorgulamalar oluşturmaya yarar. Örneğin burada Kitap entity nesnesi YayinEvi sınıfı türündeki yayinevi veri alanı ilişkisine göre birbirine katılmaktadır.
Query katilimSorgusu = em.createQuery("select k,y from Kitap k " +" INNER JOIN k.yayinevi y"); List<Object[]> katilimListesi = katilimSorgusu.getResultList(); for(Object[] oo:katilimListesi) logger.log(Level.INFO, "->{0}->{1}", new Object[]{oo[0], oo[1]});
13) LEFT OUTER JOIN veya LEFT JOIN : Bir yukardaki örneğe benzer sorgular oluşturur. Yukarıdaki sorguda, Kitap entity nesnesi içindeki yayinevi alanına bir YayinEvi entity nesne referansı bulunmazsa, bu kayıt sorguda yok sayılır, elde edilmez. LEFT JOIN‘ de ise ilişki karşılığı null olsa bile kayıtlar elde edilir, aradaki tek var budur.
Query katilimLeftSorgusu = em.createQuery("select k,y from Kitap k " +"LEFT OUTER JOIN k.yayinevi y"); List<Object[]> katilimLeftListesi = katilimLeftSorgusu.getResultList(); for(Object[] oo:katilimLeftListesi) logger.log(Level.INFO, "-> {0}-> {1}", new Object[]{oo[0], oo[1]});
Şimdi bu sorgu cümlelerini ister Hüseyin Akdoğan’ın tanıttığı JPQL test aracıyla veya buradaki örnek uygulamayla test edebilirsiniz.