14.JPQL

JPA Query Syntax

JPQL : (Java Persistence Query Language) JPA Query Language JPA apileri kullanılarak sorgu cümleleri oluşturmaya yarayan bir yapıdır. EntityManager arayüzünün createQuery ve createNamedQuery isimli 2 metodu ile sorgu cümlesi oluşturalabilir. JPQL syntax'ı klasik SQL syntax'ına benzemekle beraber özgün farklılıklar da içermektedir. Örneğin bir select cümlesinde from sözcüğünden sonra tablo ismi yerine JPQL içerisinde varlık(entity) ismi koyulmalıdır.Jpql sorguları JPA Api'lerinin kullanılarak implemente edilmiş tüm ortamlar için gerçerlidir.

İsimsiz sorgu işlemleri: İsimsiz sorgu işlemdieri jpql sorgularının doğrudan createQuery metoduna verilmesyle yazılır.Create query metodunun birçok versiyonu bulunur. String ve Class parametreli verisoyonu jpql sorgu cümlersi ve sorgulamaya ilşikin varlığın Class nesnesi almaktadır.Aşagıdaki kodda tüm ürünerlerin ede eldildiği bir jpql sorgusu elde edilmektedir.

CreateQuery metodu type query referansı döndürmektedir. getResultList metodu ile sorgunun sonucu elde edilebilir.

Örneğin aşağıdaki sorguda ilk n satır alınmaktadır yani aslında bu select top n sorgusunu temsil etmektedir.

public static List getAllProducts(EntityManager em, int n)

{

String query = "select p from Product p";

return em.createQuery(query, Product.class).setMaxResults(n).getResultList();

}

CreateQuery metodunun String parametreli versiyonu object türünden elemenlar tutan bir liste döndürmektedir.

public static List getAllProductsAsObject(EntityManager em)

{

return em.createQuery("select p from Product p").getResultList();

}

İsimli sorgu kullanımı: isimli sorgulama işlemlerinde sorgu işlemi yapılacak varlığa ilişkin sınıfınn bildiriminde NamedQuery Anotation sınıfı yardmıyla verilmektedir. Bu anotation sınıfının name ve query değişkenleri sırasıyla create named qury metodnua verilecek isim ve ilgili JPQL sorgu cümlesini içerecektir. Buna göre aşağıdaki gibi bir kod ile isimili sorgu cümleleri kullanılabiilir

Litaratürde isimli sorgu cümlelerine static query, isimsiz sorgu cümlerina dynamic query cümleleri denmektedir.

JPQL Sorgularında Parametrik Kullanım

Anımsanacağı gibi JPQL klasik SQL'e çok benzemektedir. Örneğin JPQL'de "where" operatoru bulunmaktadır. Örneğin "select p from product where p.m_id=3" gibi. where cümlelerinde çoğu zaman koşulun dinamik olarak belirlenmesi gerekmektedir. Bunun için klasik String birleştirmesi kullanılabilir. Ancak bu işlem çok fazla kod maliyeti getirdiğinden parametre ile kullanılması daha uygun olabilmektedir. JPQL de parametreler 2 nokta üst üste ile başlayan isimler şeklinde verilebilir. Parametrelere karşılık gelen değeler TypeQuery'nin setParameter() isimli metodu ile iki nokta üst üste olmadan parametre ismi ile verilmelidir.

Anahtar Notlar: Bazı sınıflar için birden fazla metodun sürekli olarak çağrılması durumunda kolay kullanım açısından zincir şeklinde çağrılabilmesi için çağıran sınıf referansının kendisiyle geri dönülmesi gerekmektedir.

Örneğin:

/*----------------------------------------------------------------------------------------------------------------------
    Zincir çağırması (fluent kalıbı)
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

public class App {    
    public static void main(String[] args) 
    {    
        Sample s = new Sample();

        s.foo().bar().tar();        
    }    
}

class Sample {
    public Sample foo()
    {
        System.out.println("foo");

        return this;
    }
    public Sample bar()
    {
        System.out.println("bar");

        return this;
    }

    public Sample tar()
    {
        System.out.println("tar");

        return this;
    }

}

Aşağıdaki kodda JPQL parametreleri kullanılarak isme göre ürünlerin listesi elde edilmektedir.

    public static List<Product> getProductsByName(EntityManager em, String name)
    {
        String query = "select p from Product p where p.m_name like :product_name";

        return em.createQuery(query, Product.class).setParameter("product_name", name).getResultList();
    }   

    public static List<Product> getProductsByNameAndPrice(EntityManager em, String name, double minPrice)
    {
        String query = "select p from Product p where p.m_name like :product_name and p.m_unitPrice >= :unit_price";

        return em.createQuery(query, Product.class).setParameter("product_name", name).setParameter("unit_price", minPrice).getResultList();
    }

Sınıf Çalışması: Ürünlerden oluşan veritabanını kullanan bir web uygulaması ile ürün ekleyen isme yada fiyata göre ürün sorgulaması yapan programı yazınız.

015-ProductInformationWebApp

Bu uygulamada validasyon ve tasarıma dikkat edilmemiştir.

Sı: 015- uygulamasında arama işlemini isme ve ürün birim fiyatına göre arama yapabileceği şekle getiriniz.

JPQL ile Numaralı Parametre Kullanımı

JPQL ile parametreler istenirse numaralandırılabilir. Bunun için parametre ismi yerine soru işareti ve parametre numarası kullanılmalıdır. Örn:

    public static List<Product> getProductsByName(EntityManager em, String name)
    {
        String query = "select p from Product p where p.m_name like ?1";

        return em.createQuery(query, Product.class).setParameter(1, name).getResultList();
    }   

    public static List<Product> getProductsByNameAndPrice(EntityManager em, String name, double minPrice)
    {
        String query = "select p from Product p where p.m_name like ?1 and p.m_unitPrice >= ?2";

        return em.createQuery(query, Product.class).setParameter(1, name).setParameter(2, minPrice).getResultList();
    }

Bu yöntem parametrelere isim vermek kadar okunabilir olmasa da, kolay kullanım açısından çok parametreli sorgularda tercih edilebilir.

JPQL ile bir varlığa ilişkin tüm elemanlar yerine belirli elemanlarda elde edilebilir. Örneğin belirli bir ürünün yalnızca stoktaki miktarını elde etmek istiyorsak, şu şekilde bir sorgu cümlesi kullanabiliriz. "select p.m_stock Product p where p.m_id=9"

Örneğin aşağıdaki kodda bu işlem yapılmaktadır.

 public static List<Integer> getProductsStockByName(EntityManager em, String name)
    {
        String query = "select p.m_stock from Product p where p.m_name=?1";

        return em.createQuery(query, Integer.class).setParameter(1, name).getResultList();
    }

İlişkili varlıklar için de sorgulama in operatörü ile yapılabilir. Örneğin Student ve Lecture manyToMany ilişkisinde bir öğrencinin numarasına göre aldığı dersler şu şekilde yazılabilir;

011-SampleMTMRelationship

public static List getStudentsLectureNames(EntityManager em, String id)

{
String query = "select l.m_name from Student s, in(s.m_students) where s.m_citizenId=?1";

return em.createQuery(query, String.class).setParameter(1, id).getResultList();
}

Aynı işlem join operatörü ile de aşağıdaki gibi yapılabilir;

public static List<String> getStudentsLectureNames(EntityManager em, String id)

{

String query = "select lec.m_name from Student s join s.m_students lec where s.m_citizenId=?1";

return em.createQuery(query, String.class).setParameter(1, id).getResultList();

}

Aşağıdaki sorguda derse kayıt olmuş tüm öğrenciler listelenmektedir.

public static List<String> getStudentsLectureNames(EntityManager em, String id)

{

String query = "select s.m_name from Student s where s.m_students is not empty";

return em.createQuery(query, String.class).getResultList();

}

Benzer şekilde aşağıda da tüm kayıt olmamaış öğreciler listenebilir.

public static List<String> getStudentsLectureNames(EntityManager em, String id)

{

String query = "select s.m_name from Student s where s.m_students is empty";

return em.createQuery(query, String.class).getResultList();

}

Sınıf Çalışması;

Product ve Customer varlıklarına ilişkin bir web uygulaması geliştiriniz

Açıklamalar :

  • Porduct ve Customer arasında çok a çokluk (ManyToMany) ilişkisi olacaktır.

  • Product içersinde ürün kodu, adı, stock ve birim fiyatı bilgileri tutulacaktır.

  • Customer içerisinde citizenId, adı ve adres bilgileri tutulacaktır.

  • Müşteri ekleyen, ürün ekleyen ve sipariş veren sayfaları geliştiriniz.

017-ProductCustomerOrder

LIKE İfadesi

Klasik SQL'de olduğu gibi LIKE ifadesi de JPQL'de kullanılabilir, örneğin "select p fron Products where p.m_name like 'iphone%'" NULL ifadesi

Sorgu sonucunda çıkan değerin NULL olup olmadığını kontrol etmek için is ile birlikte kullanılır.

örneğin "select c from Customer where c.m_id

BETWEEN İfadesi

Klasik SQL'de olduğu gibi BETWEEN anahtar sözcüğü de JPQL'de kullanılabilir. Between anahtar sözcüğünden sonra END operatörü de kullanılmalıdır.

"select s from Student where s.m_id BETWEEN 20 and 25"

BULK Update ve Delete İşlemleri

JPQL ile klasik SQL'de olduğu gibi Update ve Delete işlemleri birden fazla veri üzerinde yapılabilmektedir. Buna göre örneğin "update Player p SET p.status = 'inactive' where p.year between 200 and 250"

Anahtar Notlar: Join işlemleri de oldukça yetenekli olarak kullanılabilmektedir. Klasik SQL'e benzer şekilde LEFT, RIGHT ve INNER gibi join işlemleri JPQL'de desteklemektedir.

JPA Criteria API

Criteria JPA içerisinde önceden tanımlanmış bir API'dir. JPQL'e alternatif olarak kullanılmaktadır. JPQL'e göre tür güvenli, taşınabilir ve kolay modifiye edilebilir bir API sistemidir. Bu sistemde de JPQL'de olduğu gibi abstract şema ile işlem yapılmaktadır. Ancak criteria apilerinin en önemli özelliği hataların bir çoğunun derleme aşamasında anlaşılabilmesidir. Örneğin JPQL'de sorgu cümlesi içerisinde yapılacak küçük bir imla hatası bile çalışma zamanı sırasında anlaşılabilecekken(runtime) Criteria APIleri ile bu hatalar derleme zamanında anlaşılabilecektir. C. Apileri JPA'ye 2.0 versiyonu ile eklenmiştir ve 2.1 versiyonu ile Bulk Delete ve Bulk Update gibi önemli işlemlerde dahil edilmiştir.

Criteria Sorgu Yapısı Criteria APIleri genel olarak javax.persistance.criteria paketi içerisinde bildirilmiştir. Criteria Api kullanımı temel olarak şu adımlardan oluşturulabilir;

  • EntityManager kullanılarak bir CriteriaBuilder nesnesi elde edilir.
  • Bir CriteriaBuilder sınıfından bir CriteriaQuery nesnesi elde edilir. Bu nesne sorgu yaratmak için kullanılan temel bir sınıf nesnesidir.
  • CriteriaQuery sınıfının from() metodu ile sorguya ilişkin bir kök elde edilir. Kökü temsil eden Generic sınıf Root isimli sınıftır.
  • CriteriaQuery sınıfının select() metodu ile sonucun türü set edilir.
  • TypedQuery isimli Generic sınıf yine EntityManager sınıfının CreateQuery metodu çağrılarak elde edilir. Anımsanacağı gibi bu sınıf bir sorguya ilişkin sonucu temsil eden sınıftır.
  • TypedQuery sınıfının getResultList() veya metodları ile ilgili sonuçlar elde edilir.

Aşağıdaki örnekte basit bir Criteria APi kullanımı örneği verilmektedir.

public static List<Product> getAllProducts(EntityManager em)
    {
        CriteriaBuilder cb = em.getCriteriaBuilder();           
        CriteriaQuery<Product> cq = cb.createQuery(Product.class);

        Root<Product> from = cq.from(Product.class);          
        CriteriaQuery<Product> select = cq.select(from);

        TypedQuery<Product> tq = em.createQuery(select);

        return tq.getResultList();
    }

Criteria API'nin detayları dökümanlardan incelenebilir. Bir kısmı ileride ele alınacaktır.

Sınıf Çalışması Bir öğrenciye ilişkin aşağıdaki bilgiler tutulsun,

  1. öğrenci no
  2. isim
  3. adress
  4. email

ayrıca derslere ilişkin aşağıdaki bilgiler tutulsun

  1. ders kodu
  2. ders adı
  3. kredisi

eğitmene ilişkin aşağıdaki bilgiler tutulsun

  1. citizen id
  2. isim
  3. branş

Buna göre öğrenci ekleyen, ders ekleyen, eğitmen ekleyen, öğrenciye ders atayan, eğitmene ders atayan uygulamayı geliştiriniz.

019-SimpleSchoolAuto

Not: Uygulamada genel veri tabanı işlemleri hazırlanmış ve test edilmiştir. Fakat web'e ilişkin işlemler öğrencilere bırakılmıştır.

Anahtar Notlar: Class sınıfının getName() metodu, o türe ilişkin ismi tam nitelikli (fully-qualified) olarak vermektedir. Bununla birlikte JPQL entity ismi olarak tam nitelikli isim ile de çalışabilmektedir. Buna göre aşağıdaki gibi genel bir metod yazılabilir.

    public <T> List<T> getEntity(Class<T> c)
    {
        String name = c.getName();      
        String query = String.format("select t from %s t", name);

        return m_manager.createQuery(query, c).getResultList();     
    }

Anahtar Not: Bilindiği gibi insert, delete, update gibi işlemler için, transaction içerisinde olunması gerekmektedir. Ancak bir nesne için her zaman bu durum mümkün olmayabilir, bu durumda EntityManager sınıfının merge() isimli metoduna nesne referansı verilerek ve transaction arasında çağrılarak o nesne transaction'a dahil edilebilir.

results matching ""

    No results matching ""