Java JPA Nedir? Kurulumu ve Kullanımı

Paylaş

Java tabanlı ORM araçlarına standart oluşturmak için hazırlanan JPA nedir ile ilgili bilgiler, kurulumu, kullanımı örnekler ile birlikte yer alıyor.

JPA nedir?

JPA veya Jakarta/Java Persistance API verileri kalıcı olarak saklamak, almak, bulmak, silmek ve güncellemek için kullanılan Java ORM şartnamesidir.

ORM nedir?

ORM veya Object Relational Mapping veritabanı ve programlama dili arasında ilişki kurarak, her bir alanı programlama diline uygun veri yapısıyla eşleştirmek için kullanılan bir yapıdır.

OOP tabanlı programlama dili ile veritabanı işlemleri yapan bir uygulama geliştirirken ilk olarak verilerin veritabanından alınarak programlama diline ait veri yapısı ile ifade edilmesi/dönüştürülmesi gerekir.

Bu dönüşüm sırasında veritabanındaki sütun adlarının değişmesi, veri tipinin değişmesi, uygun veri türüne dönüşüm yapılamaması gibi çeşitli hatalar ile karşılaşılır.

ORM araçları genellikle veritabanında yer alan tabloları sınıflarla ifade edererek eşlemeyi yapar.

Neden JPA

Java programlama dili Apache, Eclipse, Redhat ve çeşitli geliştiriciler tarafından geliştirilen ORM araçlarına sahiptir.

Bu ORM araçlarına örnek olarak Hibernate, EclipseLink, MyBatis verilebilir.

Aynı işi farklı metot ismi-yöntemlerle yapan ORM araçları belirli bir standartın olmayışı kütüphane karmaşıklığına neden olmaktadır.

JCP Java topluluğu bu karmaşıklığı ortadan kaldırmak için Java ORM kütüphane standartlarını JPA olarak belirlemiştir.

Bu sayede kullanılan ORM aracı ister Hibernate ister EclipseLink olsun aynı metot adları aynı sonucu verecektir.

JPA kurulumu

Öncelikle JPA metot, sınıf ve arayüz tanımlarının yer aldığı jakarta.persistence-api-x.x.x.jar dosyası projeye eklenir.

JPA kurallarını uygulayan ORM aracına ait dosya projeye eklenir.

Son olarak kullanılacak olan JDBC sürücü dosyası projeye eklenir.

Java JDBC hakkında detaylı bilgi için Java JDBC yazıma bakmanda fayda var.

Gerekli olan dosyalar maven tabanlı proje ile aşağıdaki gibi kolayca eklenebilir.

JPA şartnamesi projeye eklenir.

<dependency>
    <groupId>jakarta.persistence</groupId>
    <artifactId>jakarta.persistence-api</artifactId>
    <version>3.1.0</version>
</dependency>

NOT: Bazı ORM araçları JPA şartnamesi ile birlikte gelir.

Herhangi bir ORM aracı projeye eklenir.

<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.4.4.Final</version>
</dependency>

Kullanılacak olan veritabanı sürücüsü projeye eklenir.

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.3.0</version>
</dependency>

Maven hakkında detaylı bilgi için Maven yazıma bakmanda fayda var.

Kullanılan ORM aracı ile JPA şartnamesi uyumluluğuna dikkat edilmelidir.

Bundan dolayı ORM paketi ile gelen JPA şartnamesini kullanmak faydalı olacaktır.

JPA kullanımı

JPA temel olarak Persistence, EntityManagerFactory, EntityManager ve Entity sınıfları üzerine kurulmuştur.

Persistence sınıfı EntityManagerFactory sınıfını elde etmek için kullanılır.

createEntityManagerFactory(String persistenceUnitName)
createEntityManagerFactory(String persistenceUnitName, Map properties)

Metot parametre olarak persistence.xml dosyasında yer alan persistence adını, isteğe göre ek ayarlar içeren Map türünden değer alır.

persistence.xml

JPA veritabanı ayarları, bağlantı ayarları ve ORM aracı ayarlarını tanımlamak için persistence.xml dosyasını kullanır.

Bu dosya proje yapısına göre META-INF veya src\main\resources\META-INF dizininde yer alır.

Aşağıda örnek persistence.xml dosyası yer almaktadır.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.1" xmlns="https://jakarta.ee/xml/ns/persistence" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence 
             https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
    <persistence-unit name="yusufsezerPU" transaction-type="RESOURCE_LOCAL">
        <description>JPA ayarları açıklaması</description>
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <mapping-file>META-INF/map.xml</mapping-file>
        <jar-file>extEntity.jar</jar-file>
        <class>com.yusufsezer.entity.Person</class>
        <class>com.yusufsezer.entity.Product</class>
        <class>com.yusufsezer.entity.Order</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test"/>
            <property name="jakarta.persistence.jdbc.user" value="root"/>
            <property name="jakarta.persistence.jdbc.password" value=""/>
            <property name="jakarta.persistence.lock.timeout" value="100"/>
            <property name="jakarta.persistence.query.timeout" value="100"/>
            <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
            <property name="jakarta.persistence.schema-generation.create-source" value="script" />
            <property name="jakarta.persistence.schema-generation.create-script-source" value="create-db.sql"/>
            <property name="jakarta.persistence.schema-generation.drop-source" value="script"/>
            <property name="jakarta.persistence.schema-generation.drop-script-source" value="drop-db.sql"/>
            <property name="jakarta.persistence.schema-generation.scripts.action" value="drop-and-create"/>
            <property name="jakarta.persistence.schema-generation.scripts.create-target" value="create.sql"/>
            <property name="jakarta.persistence.schema-generation.scripts.drop-target" value="drop.sql"/>
            <property name="jakarta.persistence.sql-load-script-source" value="data.sql"/>
        </properties>
    </persistence-unit>
</persistence>
  • persistance – JPA ayarlarını ifade eder.
    • version – JPA sürümünü ifade eder.
  • persistance-unit – Persistance örneğini ifade eder.
    • name – Persistance adını ifade der.
    • transaction-type – Veritabanı işlemlerinin (Transaction) kimin yöneteceğini ifade eder. (JTA, RESOURCE_LOCAL)
  • description – Persistance açıklamasını ifade der.
  • provider – Hangi JPA sağlayıcısının (ORM aracının) kullanılacağını ifade eder.
  • mapping-file – Eşleştirme dosyasını ifade eder.
  • jar-file – Derlenmiş Entity sınıflarının bulunduğu jar dosyasını ifade eder.
  • class – Entity sınıfını ifade eder.
  • exclude-unlisted-classes – Belirtilmeyen Entity sınıflarının dahil edilip edilmeyeceğini ifade eder.
  • properties – JPA ve ORM ayarları ifade eder.
    • property – JPA ve ORM ayarını ifade eder.
  • jakarta.persistence.jdbc.driver – Veritabanı bağdaştırıcısını (sürücüsünü) ifade eder.
  • jakarta.persistence.jdbc.url – Veritabanı bağlantı yolunu ifade eder.
  • jakarta.persistence.jdbc.user – Veritabanı kullanıcı adını ifade eder.
  • jakarta.persistence.jdbc.password – Veritabanı kullanıcı şifresini ifade eder.
  • jakarta.persistence.lock.timeout – Kilit süresini ifade eder. (pessimistic)
  • jakarta.persistence.query.timeout – Kilit süresini ifade eder. (query)
  • jakarta.persistence.schema-generation.database.action – Veritabanı tablo ve şemalarının nasıl oluşturulacağını, kaldırılacağını ifade eder. (create, drop, drop-and-create, none)
  • jakarta.persistence.schema-generation.create-source – Oluşturma işleminin hangi değere  göre yapılacağını ifade eder. (metadata, metadata-then-script, script, script-then-metadata)
  • jakarta.persistence.schema-generation.create-script-source – Oluşturma işlemi için kullanılacak dosyayı ifade eder.
  • jakarta.persistence.schema-generation.drop-source – Kaldırma işleminin hangi değere göre yapılacağını ifade eder. (metadata, metadata-then-script, script, script-then-metadata)
  • jakarta.persistence.schema-generation.drop-script-source – Kaldırma işlemi için kullanılacak dosyayı ifade eder.
  • jakarta.persistence.schema-generation.scripts.action – Hangi komutlarının oluşturulacağını ifade eder. (create, drop, drop-and-create)
  • jakarta.persistence.schema-generation.scripts.create-target – Veritabanı oluşturma komutlarınının saklanacağı dosyayı ifade eder.
  • jakarta.persistence.schema-generation.scripts.drop-target – Veritabanı kaldırma komutlarınının saklanacağı dosyayı ifade eder.
  • jakarta.persistence.sql-load-script-source – Çalıştırılacak komut dosyasını ifade eder.

Ayrıca önbellek (cache), JEE Application Server veri kaynağı, doğrulama (validation) ve ORM aracına ait çeşitli ayarlarda yer alır.

Hibernate ORM aracını ile sık kullanılan ayarlar şunlardır.

hibernate.dialect – Veritabanı işlemlerinde kullanılacak SQL komutlarını ifade eder.

hibernate.show_sql – Çalıştırılan SQL komutlarının ekrana çıktı olarak yazılıp yazılmayacağı ifade eder.

EclipeLink ORM aracını ile sık kullanılan ayarlar şunlardır.

eclipselink.logging.level – Çalıştırılan SQL komutlarının ekrana çıktı olarak yazılma seviyesini ifade eder.

eclipselink.logging.level.sql – Çalıştırılan SQL komutlarının ekrana çıktı olarak yazılma seviyesini ifade eder.

eclipselink.logging.parameters – SQL komutlarına değerlerin-parametrelerin ekleneip eklenmeyeceğini ifade eder.

JPA şartnamesinin görevi ORM aracına bağlılığı azaltmak olduğundan olabildiğince az ORM özelliğinin kullanımı faydalı olacaktır.

Bazı JPA ayarları ORM araçları tarafından desteklenmediğinden olabildiğine az ayarın kullanımı faydalı olacaktır.

NOT: JPA ayarları ayrıca createEntityManagerFactory metoduna Map veri yapısı olarakta verilebilir.

EntityManagerFactory

EntityManagerFactory sınıfı EntityManager sınıfı, CriteriaBuilder sınıfı ve JPA ayarlarını elde etmek için kullanılır.

Aşağıdaki komutlar JPA ayarlarını ve projede yer alan Entity sınıfları ile özelliklerini listelemektedir.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("yusufsezerPU");
System.out.println("JPA ayarları");
emf.getProperties().forEach((k, v) -> {
    System.out.println(k + " : " + v);
});

System.out.println("Entity listesi");
emf.getMetamodel().getEntities().forEach((t) -> {
    System.out.println(t.getName());
    t.getAttributes().forEach((t1) -> {
        System.out.println(t1);
    });
});
emf.close();

Aşağıdaki örnekte EntityManagerFactory üzerinden EntityManager sınıfı elde edilmiştir.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("yusufsezerPU");
EntityManager em = emf.createEntityManager();
// veritabanı işlemleri
em.close();
emf.close();

EntityManager

EntityManager sınıfı EntityTransaction, Query, TypedQuery, StoredProcedureQuery sınıflarını elde etmek kayıt ekleme, silme, güncelleme, arama gibi veritabanı işlemleri, JPQL ve SQL komutları çalıştırma gibi işlemler için kullanılır.

Aşağıdaki örnekte JPQL ve SQL komutlarını çalıştırma ile ilgili örnek yer almaktadır.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("yusufsezerPU");
EntityManager em = emf.createEntityManager();

System.out.println("JPQL kullanımı");
TypedQuery<String> jpqlQuery = em.createQuery("SELECT CONCAT(P.firstName, ' ', P.lastName) FROM Person P", String.class);
System.out.println("Sonuç : " + jpqlQuery.getSingleResult());

System.out.println("SQL kullanımı");
Query sqlQuery = em.createNativeQuery("SELECT count(P.id) FROM Person P");
System.out.println("Kayıt sayısı : " + sqlQuery.getResultList());

em.close();
emf.close();

EntityTransaction

JPA ile veri ekleme, silme ve güncelleme gibi işlemler için Transaction yapısını kullanır.

Veritabanı işlemi yapılmadan begin metodu ile Transaction başlatılır.

İşlem sırasında hata oluştuğunda rollback metodu ile işlem geri alınabilir.

İşlem sonucunu veritabanına yansıtmak için commit metodu ile kullanılır.

Transaction hakkında detaylı bilgi için A.C.I.D yazıma bakmalısın.

EntityManagerFactory emf = Persistence.createEntityManagerFactory("yusufsezerPU");
EntityManager em = emf.createEntityManager();

EntityTransaction et = em.getTransaction();
et.begin();
System.out.println("Durum: " + et.isActive());
// veritabanı işlemleri
// et.rollback(); // işlemi geri almak için kullanılır.
et.commit(); // işlemi tamamla
System.out.println("Durum: " + et.isActive());

em.close();
emf.close();

JPA bu sayede veri güvenliği ve bütünlüğünü sağlar.

Entity

JPA veritabanı işlemleri sırasında tabloları entity-varlık olarak adlandırılan sıradan Java sınıflarıyla (POJO) ifade edilir.

Örnek JPA entity sınıfı aşağıda yer almaktadır.

@Entity
public class Person implements Serializable {

    public enum Gender {
        MALE, FEMALE
    };

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;
    private LocalDate dateOfBirth;
    @Column(precision = 19, scale = 2)
    private BigDecimal salary;
    @Enumerated
    private Gender gender;

    // getter-setter metotlar

}

Bir sınıf @Entity ve @Id ile sınıfın entity olduğu belirlenir.

İsteğe bağlı çeşitli annotations ile verilerin veritabanı yönetim sistemlerinde saklanış özelliği düzenlenebilir.

Annotations hakkında detaylı bilgi için Java Annotations yazıma bakmanda fayda var.

NOT: Farklı veritabanı yönetim sistemleri farklı sonuçlar vereceğinden olabildiğince az annotations kullanmak faydalı olacaktır.

Sık kullanılan bazı annotations değerleri ve özellikleri;

@Entity – Sınıfın entity sınıfı olduğunu ifade eder. name değeri ile entity-tablo adı belirenebilir. NOT: Kullanımı zorunludur.

@MappedSuperclass – Entity sınıflarında temel-ortak tablo elemanlarını (id, soft-delete, updatedate, insertdate vs.) belirlemek için kullanılır.

@NamedQuery – Entity için adlandırılmış JPQL sorgusu tanımlamak için kullanılır.

Tanımlama;

@NamedQuery(
        name = "findAll",
        query = "SELECT p FROM Person p"
)

Kullanımı;

persons = em.createNamedQuery("findAll").getResultList()

@NamedQueries – Birden fazla @NamedQuery tanımlamak için kullanılır.

@Id – Entity için birincil anahtar belirlemek için kullanılır. NOT: Kullanımı zorunludur.

@Transient – Veritabanında saklanmayacak alanlar için kullanılır

@Enumerated – Sıralayıcı/Numaralandırıcı türünden değerler için kullanılır.

@Temporal – Date, Calendar veri türünün saklanış biçimini ifade etmek için TemporalType numaralandırıcısı ile kullanılır.

@GeneratedValue – Birincil anahtar için değer oluşturma yöntemini GenerationType ile ifade etmek için kullanılır.

@Column – Entity alanlarını özelleştirmek için kullanılır.

@Lob – Resim, dosya gibi büyük boyutlu veri türlerini ifade etmek için kullanılır.

@Table – Entity için tablo adı ve şema belirlemek için kullanılır.

@ManyToMany – Çoka çok ilişki için kullanılır.

@ManyToOne – Çoka tek ilişki için kullanılır.

@OneToMany – Teke çok ilişki için kullanılır.

@OneToOne – Bire bir ilişki için kullanılır.

@JoinColumn – İlişki sütunlarını ifade etmek için kullanılır.

JPA ayrıca Jakarta/Java EE gibi uygulamaya özel @PersistenceContext, @PersistenceUnit özelliklere de sahiptir.

Entity yaşam döngüsü

JPA çeşitli Java yapılarında (Servlet, JSF, EJB, Spring vb.) olduğu gibi Lifecycle Events olarak adlandırılan yaşam döngüsü özelliğine sahiptir.

Bu özellik sayesinde Entity işlemlerinde komut çalıştırma, kayıt (log) tutma gibi işlemler yapılabilir.

@PrePersist – Entity kayıt edilmeden önce çalıştırılacak metodu ifade eder.

@PostPersist – Entity kayıt edildikten sonra çalıştırılacak metodu ifade eder.

@PreRemove – Entity silinmeden önce çalıştırılacak metodu ifade eder.

@PostRemove – Entity silindikten sonra çalıştırılacak metodu ifade eder.

@PreUpdate – Entity güncellenmeden önce çalıştırılacak metodu ifade eder.

@PostUpdate – Entity güncellendikten sona çalıştırılacak metodu ifade eder.

@PostLoad – Entity veritabanından alındıktan sonra çalıştırılacak metodu ifade eder.

Yaşam döngüsü metotları Entity sınıfının içinde yazılabileceği gibi Entity sınıfına eklenecek @EntityListeners ile belirlenebilir.

JPQL

JPQL veya Jakarta Persistence Query Language SQL benzeri JPA Entity nesneleri üzerinden sorgulama yapmak için kullanılan bir sorgulama dilidir.

Örnek JPQL sorgusu kullanımı aşağıdaki gibidir.

Query query = em.createQuery("SELECT p FROM Person p");
List results = query.getResultList();

for (Object result : results) {
    System.out.println(result);
}

Örnek parametreli JPQL sorgusu kullanımı aşağıdaki gibidir.

Query query = em.createQuery("SELECT p FROM Person p WHERE p.firstName = :name");
query.setParameter("name", "Yusuf");
List results = query.getResultList();

Örnek JPQL sorgusu kullanımı aşağıdaki gibidir.

TypedQuery<Person> query = em.createQuery("SELECT p FROM Person p", Person.class);
List<Person> results = query.getResultList();

results.forEach((person) -> {
    System.out.println(person.getFirstName());
});

Örnek parametreli JPQL sorgusu kullanımı aşağıdaki gibidir.

TypedQuery<Person> query = em.createQuery("SELECT p FROM Person p WHERE p.firstName = :name", Person.class);
query.setParameter("name", "Yusuf");
List<Person> results = query.getResultList();

results.forEach((person) -> {
    System.out.println(person.getFirstName());
});

JPQL kullanımı amacı tablolar yerine sınıfları kullanmak ve Veritabanı yönetim sistemleri arasındaki SQL söz dizimi farklılıklarını en aza indirmektedir.

JPA Criteria API

JPA Criteria API, JPQL gibi SQL benzeri sorgulama yapmak yerine Java komutları kullanarak sorgulama yapmayı sağlar.

Örneğin; Aşağıdaki JPQL sorgusunu JPA Criteria API yazalım.

SELECT p FROM Person p
TypedQuery<Person> query = em.createQuery("SELECT p FROM Person p", Person.class);
List<Person> results = query.getResultList();

results.forEach((person) -> {
    System.out.println(person.getFirstName());
});

Sorgunun JPA Criteria API ile yazılmış hali aşağıdaki gibidir.

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> from = cq.from(Person.class);
cq.select(from);

TypedQuery<Person> query = em.createQuery(cq);
List<Person> results = query.getResultList();

results.forEach((person) -> {
    System.out.println(person.getFirstName());
});

Sorgulama Java komutları ile yapıldığından dolayı çalışma zamanında ortaya çıkan hatalı SQL, JPQL sorgularının önüne geçilmiş olur.

JPA Criteria API ile karmaşık sorgular hazırlamak SQL ve JPQL’e göre zaman alabilir.

Son

Java tabanlı ORM kütüphaneleri için standart olarak geliştirilen JPA halen güncellenen bir şartnamedir.

JPA içerisinde bazı işlemler(soft-delete gibi) diğer ORM araçlarına göre daha zor yapılabilir.

Bir çok özelliğini Hibernate ORM aracından aldığından dolayı bazı özellikler sadece Hibernate ile çalışmaktadır.

JPA ve JSF örneğine buradan ulaşabilirsiniz.

JPA ve JavaFX örneğine buradan ulaşabilirsiniz.

Java Derslerine buradan ulaşabilirsiniz.

Hayırlı günler dilerim.


Bunlarda ilgini çekebilir