4. 'in özeti
JPA (Java Persistence API) kavramını birkaç örnekle tanıtmayı amaçlıyoruz. JPA, şu kursta ele alınmaktadır:
- Uygulamalı Java 5 Kalıcılığı: [http://tahe.developpez.com/java/jpa] - JPA ile veri erişim katmanını oluşturmak için gerekli araçları sağlar
4.1. Katmanlı bir mimaride JPA'in yeri
Okuyucunun, katmanlı bir mimaride JPA katmanının rolünü açıklayan bu belgenin başını (2. paragraf) tekrar okumasını öneririz. JPA katmanı, veri erişim katmanları arasında yer alır:
![]() |
[DAO] katmanı, JPA spesifikasyonu ile etkileşim halindedir. Bunu uygulayan ürün ne olursa olsun, JPA katmanının [DAO] katmanına sunduğu arayüz aynı kalır. Aşağıda, kendi JPA katmanımızı oluşturmamıza olanak sağlayacak, [ref1]'ten alınmış birkaç örnek sunuyoruz.
4.2. JPA - örnekler
4.2.1. Örnek 1 - Tek bir tablonun nesne temsili
4.2.1.1. [personne] tablosu
Bireyler hakkında bazı bilgileri depolamakla görevli tek bir [personne] tablosu içeren bir veritabanını ele alalım:
![]() |
tablonun birincil anahtarı | |
tablodaki satırın sürümü. Kişi her değiştirildiğinde, sürüm numarası bir artırılır. | |
kişinin adı | |
ad | |
doğum tarihi | |
0 (bekar) veya 1 (evli) tamsayı | |
kişinin çocuk sayısı |
4.2.1.2. [Personne] varlığı
Aşağıdaki çalışma ortamındayız:
![]() |
JPA [5] katmanı, [7] veritabanının ilişkisel dünyası ile Java programları tarafından işlenen [4] nesne dünyası arasında bir köprü kurmalıdır. [3] tarafından işlenen nesne dünyası arasında bir köprü kurmalıdır. Bu köprü, yapılandırma yoluyla kurulur ve bunu yapmanın iki yolu vardır:
- XML dosyaları kullanılarak. JDK 1.5 sürümünün ortaya çıkmasına kadar bu, neredeyse tek yöntemdi
- JDK 1.5 sürümünden itibaren Java anotasyonları kullanılarak
Bu belgede, yalnızca ikinci yöntemi kullanacağız.
Daha önce sunulan [personne] tablosundaki [Personne] nesnesinin görüntüsü şu şekilde olabilir:
...
@SuppressWarnings("unused")
@Entity
@Table(name="Personne")
public class Personne implements Serializable{
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "VERSION", nullable = false)
@Version
private int version;
@Column(name = "NOM", length = 30, nullable = false, unique = true)
private String nom;
@Column(name = "PRENOM", length = 30, nullable = false)
private String prenom;
@Column(name = "DATENAISSANCE", nullable = false)
@Temporal(TemporalType.DATE)
private Date datenaissance;
@Column(name = "MARIE", nullable = false)
private boolean marie;
@Column(name = "NBENFANTS", nullable = false)
private int nbenfants;
// üreticiler
public Personne() {
}
public Personne(String nom, String prenom, Date datenaissance, boolean marie,
int nbenfants) {
setNom(nom);
setPrenom(prenom);
setDatenaissance(datenaissance);
setMarie(marie);
setNbenfants(nbenfants);
}
// toString
public String toString() {
...
}
// alıcı ve ayarlayıcılar
...
}
Yapılandırma, @Annotation Java anotasyonları kullanılarak yapılır. Java anotasyonları, derleme sırasında derleyici tarafından ya da yürütme sırasında özel araçlar tarafından kullanılır. Derleyiciye yönelik 3. satırdaki anotasyon dışında, buradaki tüm anotasyonlar kullanılan JPA uygulaması, Hibernate veya Toplink için tasarlanmıştır. Dolayısıyla bunlar yürütme sırasında kullanılacaktır. Bu anotasyonları yorumlayabilecek araçlar bulunmadığında, bu anotasyonlar göz ardı edilir. Böylece, yukarıdaki [Personne] sınıfı, JPA dışındaki bir bağlamda kullanılabilir.
T tablosuyla ilişkili bir C sınıfında JPA anotasyonlarının kullanımında iki durum ayırt edilmelidir:
- T tablosu zaten mevcutsa: JPA açıklamaları mevcut durumu yansıtmalıdır (sütunların adı ve tanımı, bütünlük kısıtlamaları, yabancı anahtarlar, birincil anahtarlar, ...)
- T tablosu mevcut değil ve C sınıfında bulunan anotasyonlara göre oluşturulacak.
- durum, yönetilmesi en kolay olanıdır. JPA anotasyonları yardımıyla, istediğimiz T tablosunun yapısını belirtiriz. 1. durum ise genellikle daha karmaşıktır. T tablosu, uzun zaman önce, herhangi bir JPA bağlamı dışında oluşturulmuş olabilir. Bu durumda, tablonun yapısı JPA'in ilişkisel/nesne köprüsüne uygun olmayabilir. Basitleştirmek için, C sınıfıyla ilişkili T tablosunun C sınıfının JPA anotasyonlarına göre oluşturulacağı 2. durumu ele alalım.
[Personne] sınıfındaki JPA anotasyonlarını inceleyelim:
- 4. satır: @Entity anotasyonu, ilk ve zorunlu anotasyondur. Sınıfı tanımlayan satırın önüne yerleştirilir ve söz konusu sınıfın JPA kalıcılık katmanı tarafından yönetilmesi gerektiğini belirtir. Bu anotasyon yoksa, diğer tüm JPA anotasyonları göz ardı edilir.
- 5. satır: @Table anotasyonu, sınıfın bir temsili olduğu veritabanı tablosunu belirtir. Ana argümanı, tablonun adını belirten name’dir. Bu argüman belirtilmezse, tablo sınıfın adını alır; bu örnekte [Personne]. Dolayısıyla, örneğimizde @Table anotasyonu gereksizdir.
- 8. satır: @Id anotasyonu, sınıftaki hangi alanın tablonun birincil anahtarını temsil ettiğini belirtmek için kullanılır. Bu anotasyon zorunludur. Burada, 11. satırdaki id alanının tablonun birincil anahtarını temsil ettiğini gösterir.
- 9. satır: @Column anotasyonu, sınıfın bir alanı ile bu alanın yansıması olan tablodaki sütun arasında bağlantı kurmak için kullanılır. name özniteliği, tablodaki sütunun adını belirtir. Bu özniteliğin bulunmaması durumunda, sütun alanla aynı adı taşır. Dolayısıyla, örneğimizde name argümanı zorunlu değildi. nullable=false argümanı, alana eşlenen sütunun NULL değerini alamayacağını ve dolayısıyla alanın mutlaka bir değere sahip olması gerektiğini belirtir.
- 10. satır: @GeneratedValue açıklaması, birincil anahtarın SGBD tarafından otomatik olarak oluşturulduğunda nasıl oluşturulduğunu belirtir. Tüm örneklerimizde durum böyle olacaktır. Bu zorunlu değildir. Dolayısıyla, kişimizin birincil anahtar görevi gören bir öğrenci numarası olabilir ve bu numara SGBD tarafından oluşturulmayıp uygulama tarafından belirlenebilir. Bu durumda, @GeneratedValue açıklaması bulunmayacaktır. strategy argümanı, birincil anahtarın SGBD tarafından oluşturulduğunda nasıl oluşturulduğunu belirtir. SGBD'lerin hepsi birincil anahtar değerlerini oluşturmak için aynı tekniği kullanmaz. Örneğin:
her ekleme işleminden önce çağrılan bir değer üreteci kullanır | |
birincil anahtar alanı, Identity türü olarak tanımlanmıştır. Firebird'ün değer üretecine benzer bir sonuç elde edilir, ancak anahtar değeri satırın eklenmesinden sonra bilinir. | |
SEQUENCE adlı bir nesne kullanılır; bu nesne de yine bir değer üreteci işlevi görür |
JPA katmanı, değer üretecini oluşturmak için her bir SGBD'e göre farklı SQL komutları üretmelidir. Yapılandırma yoluyla, bu katmana yönetmesi gereken SGBD nesnesinin türü belirtilir. Böylece, bu SGBD nesnesinin birincil anahtar değerlerini oluşturmak için kullanılan olağan stratejinin ne olduğunu bilebilir. strategy = GenerationType.AUTO argümanı, JPA katmanına bu standart stratejiyi kullanması gerektiğini belirtir. Bu teknik, bu belgedeki tüm örneklerde kullanılan yedi SGBD için işe yaramıştır.
- 14. satır: @Version açıklaması, tablonun aynı satırına eşzamanlı erişimleri yönetmek için kullanılan alanı belirtir.
[personne] tablosundaki aynı satıra eşzamanlı erişim sorununu anlamak için, bir web uygulamasının bir kişinin bilgilerinin güncellenmesine izin verdiğini varsayalım ve şu durumu inceleyelim:
T1 zamanında, U1 kullanıcı, P adlı bir kişinin düzenleme ekranına girer. Bu anda çocuk sayısı 0'dır. Bu sayıyı 1’e değiştirir ancak değişikliğini onaylamadan önce, U2 adlı bir kullanıcı aynı kişi P’nin düzenleme sayfasına girer. U1 henüz değişikliğini onaylamadığından, U2, ekranında çocuk sayısının 0 olduğunu görür. U2, kişi P'nin adını büyük harflerle yazar. Ardından U1 ve U2, bu sırayla değişikliklerini onaylar. U2’in yaptığı değişiklik geçerli olacaktır: veritabanında isim büyük harfe dönüştürülecek ve çocuk sayısı sıfır olarak kalacaktır; oysa U1, bu sayıyı 1 olarak değiştirdiğini sanmaktadır.
Kişi sürümü kavramı bu sorunu çözmemize yardımcı olur. Aynı kullanım örneğini ele alalım:
T1 zamanında, U1 adlı bir kullanıcı P adlı bir kişinin düzenleme ekranına girer. Bu anda çocuk sayısı 0’dır ve sürüm V1’tir. Kullanıcı çocuk sayısını 1’e değiştirir, ancak değişikliğini onaylamadan önce U2 adlı bir kullanıcı aynı kişi P’nin düzenleme sayfasına girer. U1 henüz değişikliğini onaylamadığından, U2, çocuk sayısını 0 ve sürümü V1 olarak görür. U2, P adlı kişinin adını büyük harflerle yazar. Ardından U1 ve U2, değişikliklerini bu sırayla onaylar. Bir değişikliği onaylamadan önce, P kişisini değiştiren kişinin, halihazırda kayıtlı olan P kişisiyle aynı sürüme sahip olup olmadığı kontrol edilir. Bu durum, U1 kullanıcısı için geçerlidir. Dolayısıyla, onun yaptığı değişiklik kabul edilir ve değiştirilen kişinin sürümü, kişide bir değişiklik yapıldığını belirtmek amacıyla V1'ten V2'e değiştirilir. U2'in yaptığı değişikliğin onaylanması sırasında, U2'in kişi P'nin V1 sürümüne sahip olduğu fark edilecektir; oysa şu anda bu kişinin sürümü V2'tir. Böylece U2 kullanıcıya, kendisinden önce birinin bu işlemi gerçekleştirdiği ve P kişisinin yeni sürümünden başlaması gerektiği söylenebilir. Kullanıcı bunu yapacak, artık bir çocuğu olan V2 sürümündeki P kişisini alacak, ismi büyük harflerle yazacak ve onaylayacaktır. Kayıtlı P kişisinin sürümü hâlâ V2 ise, yaptığı değişiklik kabul edilecektir. Sonuç olarak, U1 ve U2 tarafından yapılan değişiklikler dikkate alınacaktır; oysa sürüm kullanılmayan senaryoda, değişikliklerden biri kaybolurdu.
Müşteri uygulamasının [DAO] katmanı, [Personne] sınıfının sürümünü kendisi yönetebilir. Bir P nesnesinde her değişiklik yapıldığında, bu nesnenin sürümü tabloda 1 artırılır. @Version anotasyonu, bu yönetimi JPA katmanına devretmeyi sağlar. İlgili alanın, örnekte olduğu gibi version olarak adlandırılmasına gerek yoktur. Herhangi bir isim verilebilir.
@Id ve @Version anotasyonlarına karşılık gelen alanlar, kalıcılık nedeniyle mevcut olan alanlardır. [Personne] sınıfının kalıcı hale getirilmesine gerek olmasaydı, bu alanlara ihtiyaç duyulmazdı. Dolayısıyla, bir nesnenin kalıcı hale getirilmesi gerekip gerekmediğine bağlı olarak temsilinin farklı olduğu görülmektedir.
- 17. satır: Yine @Column anotasyonu, Personne sınıfının nom alanıyla ilişkili [personne] tablosundaki sütun hakkında bilgi vermek için kullanılmıştır. Burada iki yeni argüman bulunmaktadır:
- unique=true, bir kişinin adının benzersiz olması gerektiğini belirtir. Bu, veritabanında [personne] tablosundaki NOM sütununa bir benzersizlik kısıtlamasının eklenmesiyle sonuçlanacaktır.
- length=30, NOM sütununun karakter sayısını 30 olarak belirler. Bu, söz konusu sütunun türünün VARCHAR(30) olacağı anlamına gelir.
- 24. satır: @Temporal ek açıklaması, tarih/saat türündeki bir sütuna/alana hangi SQL türünün atanacağını belirtmek için kullanılır. TemporalType.DATE türü, saat bilgisi içermeyen tek bir tarihi ifade eder. Diğer olası türler arasında, saati kodlamak için TemporalType.TIME ve tarih ile saati birlikte kodlamak için TemporalType.TIMESTAMP bulunur.
Şimdi [Personne] sınıfının kodunun geri kalanını inceleyelim:
- 6. satır: Sınıf, Serializable arayüzünü uygular. Bir nesnenin sérialisation işlemi, onu bir bit dizisine dönüştürmektir. désérialisation ise bunun tersi işlemdir. Seri hale getirme / seri hale getirme işlemi, özellikle nesnelerin ağ üzerinden alışverişi yapılan istemci / sunucu uygulamalarında kullanılır. İstemci veya sunucu uygulamaları, JVM tarafından şeffaf bir şekilde gerçekleştirilen bu işlemden haberdar değildir. Ancak bunun mümkün olabilmesi için, alışverişi yapılan nesnelerin sınıflarının Serializable anahtar sözcüğüyle "etikelenmiş" olması gerekir.
- 37. satır: Sınıfın bir oluşturucusu. id ve version alanlarının parametreler arasında yer almadığına dikkat edilmelidir. Aslında bu iki alan, uygulama tarafından değil, JPA katmanı tarafından yönetilir.
- 51. satır ve sonrası: Sınıfın her bir alanının get ve set yöntemleri. JPA anotasyonlarının, alanların kendilerine değil, alanların get yöntemlerine yerleştirilebileceği unutulmamalıdır. Anotasyonların yerleştirildiği yer, JPA'in alanlara erişmek için kullanması gereken modu belirler:
- eğer anotasyonlar alan düzeyinde yerleştirilirse, JPA alanlara doğrudan erişerek bunları okur veya yazar
- eğer anotasyonlar get düzeyinde yer alıyorsa, JPA, alanlara okumak veya yazmak için get / set yöntemleri aracılığıyla erişecektir
Bir sınıftaki JPA anotasyonlarının konumunu belirleyen, @Id anotasyonunun konumudur. Alan düzeyinde yerleştirildiğinde alanlara doğrudan erişim, get düzeyinde yerleştirildiğinde ise get ve set aracılığıyla alanlara erişim belirtir. Diğer anotasyonlar da @Id anotasyonuyla aynı şekilde yerleştirilmelidir.
4.2.2. JPA katmanının yapılandırılması
JPA katmanındaki testler aşağıdaki mimari ile yapılabilir:
![]() |
- [7]'te: [Personne] varlığının anotasyonlarından ve [persistence.xml] adlı dosyada yapılan ek yapılandırmalardan oluşturulacak veritabanı
- [5, 6]: Hibernate tarafından uygulanan bir JPA katmanı
- [4]'te: [Personne] varlığı
- [3]: konsol türü bir test programı
JPA katmanının yapılandırması, [META-INF/persistence.xml] dosyası tarafından sağlanır:
![]() |
Çalıştırma sırasında, uygulamanın Classpath dosyasında [META-INF/persistence.xml] dosyası aranır.
Projemizdeki [persistence.xml] dosyasında yapılan JPA katmanının yapılandırmasını inceleyelim:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="jpa" transaction-type="RESOURCE_LOCAL">
<!-- sağlayıcı -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<!-- Kalıcı sınıflar -->
<property name="hibernate.archive.autodetection" value="class, hbm" />
<!-- günlükler SQL
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
-->
<!-- bağlantı JDBC -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/jpa" />
<property name="hibernate.connection.username" value="jpa" />
<property name="hibernate.connection.password" value="jpa" />
<!-- şemanın otomatik olarak oluşturulması -->
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-- Lehçe -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<!-- özellikler DataSource c3p0 -->
<property name="hibernate.c3p0.min_size" value="5" />
<property name="hibernate.c3p0.max_size" value="20" />
<property name="hibernate.c3p0.timeout" value="300" />
<property name="hibernate.c3p0.max_statements" value="50" />
<property name="hibernate.c3p0.idle_test_period" value="3000" />
</properties>
</persistence-unit>
</persistence>
Bu yapılandırmayı anlamak için, uygulamamızın veri erişim mimarisine geri dönmemiz gerekiyor:
![]() |
- [persistence.xml] dosyası, [4, 5, 6] katmanlarını yapılandıracaktır
- [4]: JPA'in Hibernate uygulaması
- [5]: Hibernate, bir bağlantı havuzu aracılığıyla veritabanına erişir. Bağlantı havuzu, SGBD ile açık olan bağlantıların bir rezervidir. Bir SGBD'e birden fazla kullanıcı erişir; ancak performans nedenleriyle, aynı anda açık olan bağlantı sayısı N sınırını aşamaz. İyi yazılmış bir kod, SGBD ile en kısa sürede bir bağlantı açar: SQL komutlarını gönderir ve bağlantıyı kapatır. Veritabanıyla çalışması gerektiği her seferinde bunu tekrar tekrar yapar. Bir bağlantıyı açma/kapama maliyeti ihmal edilemez ve işte burada bağlantı havuzu devreye girer. Uygulama başlatıldığında, bu havuz N1 ile SGBD arasında N1 bağlantı açar. Uygulama, ihtiyaç duyduğunda açık bir bağlantı için bu havuza başvuracaktır. Uygulamanın artık bağlantıya ihtiyacı kalmadığında, tercihen mümkün olan en kısa sürede, bağlantı havuza iade edilecektir. Bağlantı kapatılmaz ve bir sonraki kullanıcı için kullanılabilir durumda kalır. Dolayısıyla, bağlantı havuzu, açık bağlantıların paylaşıldığı bir sistemdir.
- [6]: SGBD tarafından kullanılan JDBC sürücüsü
Şimdi [persistence.xml] dosyasının yukarıdaki [4, 5, 6] katmanlarını nasıl yapılandırdığına bakalım:
- 2. satır: XML dosyasının kök etiketi <persistence>'dir.
- 3. satır: <persistence-unit>, bir kalıcılık birimini tanımlamak için kullanılır. Birden fazla kalıcılık birimi olabilir. Her birinin bir adı (name özniteliği) ve bir işlem türü (transaction-type özniteliği) vardır. Uygulama, kalıcılık birimine adını kullanarak erişecektir; burada bu ad jpa'dır. RESOURCE_LOCAL işlem türü, uygulamanın SGBD ile işlemleri kendi başına yönettiğini gösterir. Burada da durum böyledir. Uygulama bir EJB3 kapsayıcısında çalıştığında, bu kapsayıcının işlem hizmetini kullanabilir. Bu durumda, transaction-type=JTA (Java Transaction API) olarak ayarlanacaktır. transaction-type özniteliği belirtilmediğinde varsayılan değer JTA'tir.
- 5. satır: <provider> etiketi, uygulamanın kalıcılık katmanını başlatmasını sağlayan [javax.persistence.spi.PersistenceProvider] arayüzünü uygulayan bir sınıfı tanımlamak için kullanılır. JPA / Hibernate uygulaması kullanıldığından, burada kullanılan sınıf bir Hibernate sınıfıdır.
- 6. satır: <properties> etiketi, seçilen belirli provider'e özgü özellikleri tanımlar. Dolayısıyla Hibernate, Toplink, Kodo vb. seçildiğine göre farklı özellikler olacaktır. Aşağıdakiler Hibernate'e özgüdür.
- 8. satır: Hibernate’ten, projedeki classpath’i taramasını ve @Entity anotasyonuna sahip sınıfları bulup yönetmesini ister. @Entity sınıfları, <persistence-unit> etiketinin hemen altında <class>nom_de_la_classe</class> etiketleriyle de tanımlanabilir. provider / Toplink örneğinde bunu yapacağız.
- Burada yorum satırları olarak gösterilen 10-12. satırlar, Hibernate'in konsol günlüklerini yapılandırır:
- 10. satır: Hibernate tarafından SQL üzerinde verilen komutların görüntülenip görüntülenmeyeceğini belirler. Bu, öğrenme aşamasında çok yararlıdır. İlişkisel/nesne köprüsü nedeniyle, uygulama üzerinde [persist, merge, remove] türünde işlemler uyguladığı kalıcı nesneler üzerinde çalışır. Bu işlemler sırasında gerçekte hangi SQL komutlarının gönderildiğini bilmek oldukça ilginçtir. Bunları inceleyerek, kalıcı nesneler üzerinde belirli bir işlem yapıldığında Hibernate'in hangi SQL komutlarını oluşturacağını yavaş yavaş tahmin etmeye başlarız ve ilişkisel/nesne köprüsü zihnimizde netleşmeye başlar.
- 11. satır: Konsolda görüntülenen SQL komutları, okunmasını kolaylaştırmak için güzel bir şekilde biçimlendirilebilir
- 12. satır: Görüntülenen SQL komutlarına ayrıca açıklamalar eklenecektir
- 15-19. satırlar, JDBC katmanını (mimari içinde [6] katmanı) tanımlar:
- 15. satır: SGBD'in JDBC sürücü sınıfı, burada MySQL5
- 16. satır: Kullanılan veritabanının URL'si
- 17. ve 18. satırlar: Bağlantı kullanıcısı ve şifresi
- 22. satır: Hibernate, karşısındaki SGBD'i bilmek zorundadır. Nitekim, tüm SGBD'lerin kendine özgü SQL uzantıları vardır; bu, birincil anahtar değerlerinin otomatik olarak oluşturulmasını yönetmenin kendine özgü bir yoludur, ... bu nedenle Hibernate, çalıştığı SGBD'i bilmek zorundadır; böylece ona, onun anlayacağı SQL komutlarını gönderebilir. [MySQL5InnoDBDialect], işlemleri destekleyen InnoDB türündeki tablolara sahip SGBD MySQL5'i belirtir.
- 24-28. satırlar, c3p0 bağlantı havuzunu (mimari içinde [5] katmanı) yapılandırır:
- 24. ve 25. satırlar: havuzdaki minimum (varsayılan 3) ve maksimum bağlantı sayısı (varsayılan 15). Varsayılan başlangıç bağlantı sayısı 3'tür.
- 26. satır: İstemciden gelen bir bağlantı isteğinin bekleme süresinin milisaniye cinsinden maksimum süresi. Bu süre geçtikten sonra, c3p0 istemciye bir istisna döndürür.
- 27. satır: BD'e erişmek için Hibernate, c3p0'ın önbelleğe alabileceği önceden hazırlanmış SQL komutlarını (PreparedStatement) kullanır. Bu, uygulamanın önbellekte zaten bulunan hazırlanmış bir SQL komutunu ikinci kez talep etmesi durumunda, bu komutun yeniden hazırlanmasına gerek kalmayacağı (bir SQL komutunun hazırlanması bir maliyete sahiptir) ve önbellekteki komutun kullanılacağı anlamına gelir. Burada, tüm bağlantılar için önbelleğin içerebileceği hazırlanmış SQL emirlerinin maksimum sayısı belirtilir (hazırlanmış bir SQL emri bir bağlantıya aittir).
- 28. satır: Bağlantıların geçerliliğinin milisaniye cinsinden kontrol sıklığı. Havuzdaki bir bağlantı çeşitli nedenlerle geçersiz hale gelebilir (JDBC sürücüsü, bağlantının çok uzun sürmesi nedeniyle bağlantıyı geçersiz kılar, JDBC sürücüsünde "hatalar" vardır, ...).
- 20. satır: Burada, kalıcılık biriminin başlatılması sırasında @Entity nesnelerinin veritabanı şemasının oluşturulmasını talep ediyoruz. Hibernate artık veritabanı tablolarını oluşturmak için SQL komutlarını göndermek üzere gerekli tüm araçlara sahiptir:
- @Entity nesnelerinin yapılandırması, hangi tabloların oluşturulacağını belirlemesini sağlar
- 15-18 ve 24-28. satırlar, SGBD ile bağlantı kurmasını sağlar
- 22. satır, tabloları oluşturmak için hangi SQL lehçesinin kullanılacağını belirler
Böylece, burada kullanılan [persistence.xml] dosyası, uygulamanın her yeni çalıştırılışında yeni bir veritabanı oluşturur. Tablolar, varsa silindikten (drop table) sonra yeniden oluşturulur (create table). Elbette bunun, üretim ortamındaki bir veritabanında yapılmaması gerektiği unutulmamalıdır...
4.2.3. Örnek 2: Bir-çok ilişkisi
4.2.3.1. i veritabanı şeması
1 ![]() | 2 |
- [1] veritabanında ve [2]'te, DDL (MySQL5)
Bir ürün A(id, sürüm, ad) tam olarak bir kategoriye C(id, sürüm, ad) aittir. Bir kategori C, 0, 1 veya birden fazla ürün içerebilir. Burada bir-çok ilişki (Kategori -> Ürün) ve tersi çok-bir ilişki (Ürün -> Kategori) söz konusudur. Bu ilişki, [article] tablosunun [categorie] tablosuna sahip olduğu yabancı anahtarla somutlaştırılır (DDL tablosunun 24-28. satırları).
4.2.3.2. Veritabanını temsil eden @Entity nesneleri
Bir ürün, aşağıdaki @Entity [Article] ile temsil edilir:
package entites;
...
@Entity
@Table(name="jpa05_hb_article")
public class Article implements Serializable {
// alanlar
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@SuppressWarnings("unused")
@Version
private int version;
@Column(length = 30)
private String nom;
// ana ilişki: Ürün (çok) -> Kategori (tek)
// Makale'de bir yabancı anahtar (categorie_id) ile uygulanır
// 1 Makale mutlaka 1 Kategoriye sahiptir (nullable=false)
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "categorie_id", nullable = false)
private Categorie categorie;
// yapıcılar
public Article() {
}
// alıcı ve ayarlayıcılar
...
// toString
public String toString() {
return String.format("Article[%d,%d,%s,%d]", id, version, nom, categorie.getId());
}
}
- 9-11. satırlar: @Entity'nin birincil anahtarı
- 13-15. satırlar: ürünün sürüm numarası
- 17-18. satırlar: ürün adı
- 20-25. satırlar: @Entity Article ile @Entity Categorie arasında bir “birden çok-bir” ilişkisi kuran:
- 23. satır: ManyToOne anotasyonu. "Many" (Çok), içinde bulunduğumuz @Entity Article'e, "One" (Tek) ise @Entity Categorie'e (25. satır) karşılık gelir. Bir kategori (One), birden fazla ürün (Many) içerebilir.
- 24. satır: ManyToOne anotasyonu, [article] tablosundaki yabancı anahtar sütununu tanımlar. Bu sütunun adı (name) categorie_id olacak ve her satırda bu sütunda bir değer bulunmalıdır (nullable=false).
- 25. satır: Ürünün ait olduğu kategori. Bir ürün kalıcılık bağlamına alındığında, kategorisinin hemen eklenmemesi istenir (fetch=FetchType.LAZY, 23. satır). Bu talebin bir anlamı olup olmadığı bilinmemektedir. Göreceğiz.
Bir kategori, aşağıdaki @Entity [Categorie] ile temsil edilir:
package entites;
...
@Entity
@Table(name="jpa05_hb_categorie")
public class Categorie implements Serializable {
// alanlar
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@SuppressWarnings("unused")
@Version
private int version;
@Column(length = 30)
private String nom;
// Kategori (tek) -> Makale (çok) ters ilişkisi (Makale (çok) -> Kategori (tek) ilişkisinin tersi)
// Kategori -> Makaleler ekleme kaskadı
// Kategori -> Makaleler güncelleme zinciri
// Kategori -> Makaleler silme kaskadı
@OneToMany(mappedBy = "categorie", cascade = { CascadeType.ALL })
private Set<Article> articles = new HashSet<Article>();
// yapıcılar
public Categorie() {
}
// alıcı ve ayarlayıcılar
...
// toString
public String toString() {
return String.format("Categorie[%d,%d,%s]", id, version, nom);
}
// Kategori <--> Makale arasında çift yönlü ilişki
public void addArticle(Article article) {
// ürün, kategorinin ürün koleksiyonuna eklenir
articles.add(article);
// ürün kategorisi değiştirilir
article.setCategorie(this);
}
}
- satır 8-11: @Entity'nin birincil anahtarı
- 12-14. satırlar: sürümü
- 16-17. satırlar: kategorinin adı
- 19-24. satırlar: kategorideki makalelerin kümesi
- 23. satır: @OneToMany anotasyonu, bir-çok ilişkisini belirtir. "One", içinde bulunduğumuz @Entity [Categorie]'i, "Many" ise 24. satırdaki [Article] türünü ifade eder: bir (One) kategori, birden fazla (Many) makaleye sahiptir.
- 23. satır: Bu anotasyon, @Entity Article'in categorie alanına yerleştirilen ManyToOne anotasyonunun tersi (mappedBy) niteliğindedir: mappedBy=kategori. @Entity Article'in categorie alanına yerleştirilen ManyToOne ilişkisi ana ilişkidir. Bu ilişki zorunludur. Bu ilişki, @Entity Article ile @Entity Categorie'i birbirine bağlayan yabancı anahtar ilişkisini somutlaştırır. @Entity Categorie'in articles alanına yerleştirilen OneToMany ilişkisi ise ters ilişkidir. Bu ilişki zorunlu değildir. Bu, bir kategorideki öğeleri almak için bir kolaylık sağlar. Bu kolaylık olmasaydı, bu öğeler JPQL sorgusuyla alınırdı.
- 23. satır: cascadeType.ALL, bir @Entity Categorie üzerinde yapılan işlemlerin (persist, merge, remove) bu @Entity'nin makalelerine de yayılmasını sağlar.
- 24. satır: Bir kategorideki ürünler, Set<Article> türünde bir nesneye yerleştirilecektir. Set türü, yinelenen öğeleri kabul etmez. Dolayısıyla, aynı ürünü Set<Article> nesnesine iki kez ekleyemeyiz. "Aynı ürün" ne anlama gelir? a ürününün b ürünüyle aynı olduğunu belirtmek için Java, a.equals(b) ifadesini kullanır. Tüm sınıfların atası olan Object sınıfında, a==b ise a.equals(b) ifadesi doğrudur; c.a.d. a ve b nesneleri aynı bellek konumuna sahipse. a ve b öğelerinin aynı adı varsa, bunların aynı olduğunu söylemek isteyebiliriz. Bu durumda, geliştirici [Article] sınıfında iki yöntemi yeniden tanımlamalıdır:
- equals: İki öğenin adı aynıysa true değerini döndürmelidir
- hashCode: equals yöntemi tarafından eşit kabul edilen iki [Article] nesnesi için aynı tamsayı değerini döndürmelidir. Burada değer, öğenin adından oluşturulacaktır. hashCode tarafından döndürülen değer herhangi bir tamsayı olabilir. Bu değer, özellikle sözlükler (Hashtable) olmak üzere çeşitli nesne kapsayıcılarında kullanılır.
OneToMany ilişkisi, Many öğesini depolamak için Set dışında başka türler de kullanabilir; örneğin List nesneleri. Bu belgede bu durumlara değinmeyeceğiz. Okuyucu bunları [ref1]'te bulabilir.
- 38. satır: [addArticle] yöntemi, bir kategoriye bir makale eklememizi sağlar. Bu yöntem, [Categorie] ile [Article]'i birbirine bağlayan OneToMany ilişkisinin her iki ucunu da güncellemeyi sağlar.
4.3. JPA katmanındaki API
JPA istemcisinin çalışma ortamını açıklayalım:
![]() |
JPA katmanının, [2] katmanıyla birlikte bir nesne-ilişkisel köprü ([3] / [4]) oluşturduğunu biliyoruz. Bu nesne / ilişkisel köprü kapsamında JPA katmanı tarafından yönetilen nesnelerin tümüne "kalıcılık bağlamı" denir. Kalıcılık bağlamındaki verilere erişmek için, bir JPA [1] istemcisi, JPA [2] katmanını kullanmalıdır:
- bir nesne oluşturabilir ve JPA katmanından bu nesneyi kalıcı hale getirmesini isteyebilir. Böylece nesne, kalıcılık bağlamının bir parçası olur.
- [JPA] katmanından mevcut bir kalıcı nesneye ait bir referans isteyebilir.
- JPA katmanından alınan kalıcı bir nesneyi değiştirebilir.
- JPA katmanından, kalıcılık bağlamından bir nesnenin silinmesini isteyebilir.
JPA katmanı, istemciye [EntityManager] adlı bir arayüz sunar; bu arayüz, adından da anlaşılacağı gibi, kalıcılık bağlamındaki @Entity nesnelerini yönetmeye olanak tanır. Aşağıda, bu arayüzün başlıca yöntemlerini sunuyoruz:
entity'i kalıcılık bağlamına yerleştirir | |
entity'i kalıcılık bağlamından çıkarır | |
kalıcılık bağlamı tarafından yönetilmeyen istemciden gelen entity nesnesini aynı birincil anahtara sahip kalıcılık bağlamındaki entity nesnesiyle birleştirir. Sonuç olarak, kalıcılık bağlamındaki entity nesnesi elde edilir. | |
komutu, veritabanında birincil anahtarı aracılığıyla aranan bir nesneyi bir nesneyi kalıcı bağlam içine yerleştirir. Nesnenin T türü, JPA katmanının katmanının hangi tabloya sorgu yapması gerektiğini bilmesini sağlar. Bu şekilde oluşturulan kalıcı nesne, istemciye döndürülür. | |
, bir JPQL sorgusundan (Java Persistence Query Language). Bir JPQL sorgusu, yalnızca tek fark, tablolar yerine nesneler sorgulanmasıdır. | |
öncekine benzer bir yöntemdir; tek fark, queryText'in SQL komutudur, JPQL değil. | |
createQuery ile aynı yöntemdir; tek fark, JPQL ve queryText komutlarının bir yapılandırma dosyasına taşınmış ve bir adla ilişkilendirilmiştir. Yöntemin parametresi işte bu isimdir. |
Bir EntityManager nesnesinin yaşam döngüsü, uygulamanın yaşam döngüsüyle aynı olmak zorunda değildir. Bu nesnenin bir başlangıcı ve bir sonu vardır. Böylece bir JPA istemcisi, farklı EntityManager nesneleriyle art arda çalışabilir. Bir EntityManager nesnesine bağlı kalıcılık bağlamı, bu nesneyle aynı yaşam döngüsüne sahiptir. İkisi birbirinden ayrılamaz. Bir EntityManager nesnesi kapatıldığında, kalıcılık bağlamı gerekirse veritabanıyla senkronize edilir ve ardından ortadan kalkar. Kalıcılık bağlamına yeniden sahip olmak için yeni bir EntityManager oluşturulması gerekir.
JPA istemcisi, aşağıdaki komutla bir EntityManager nesnesi ve dolayısıyla bir kalıcılık bağlamı oluşturabilir:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("nom d'une unité de persistance");
- javax.persistence.Persistence, EntityManager nesnelerinin bir fabrikasını elde etmeyi sağlayan statik bir sınıftır. Bu fabrika, belirli bir kalıcılık birimiyle ilişkilidir. Hatırlanacağı üzere, [META-INF/persistence.xml] yapılandırma dosyası, kalıcılık birimlerinin tanımlanmasına olanak tanır ve bu birimlerin birer adı vardır:
<persistence-unit name="elections-dao-jpa-mysql-01PU" transaction-type="RESOURCE_LOCAL">
Yukarıda, kalıcılık biriminin adı elections-dao-jpa-mysql-01PU'tir. Bu birimle birlikte, kendisine özgü bir yapılandırma da gelir; özellikle de birlikte çalıştığı SGBD. [Persistence.createEntityManagerFactory("elections-dao-jpa-mysql-01PU")] komutu, elections-dao-jpa-mysql-01PU adlı kalıcılık birimiyle ilişkili kalıcılık bağlamlarını yönetmek üzere tasarlanmış EntityManager nesnelerini sağlayabilen bir nesne fabrikası oluşturur. Bir EntityManager nesnesinin ve dolayısıyla bir kalıcılık bağlamının elde edilmesi, EntityManagerFactory nesnesinden aşağıdaki şekilde gerçekleştirilir:
[EntityManager] arayüzündeki aşağıdaki yöntemler, kalıcılık bağlamının yaşam döngüsünü yönetmeye olanak tanır:
kalıcılık bağlamı kapatılır. Kalıcılık bağlamının veritabanıyla senkronizasyonunu zorlar:
| |
Kalıcılık bağlamındaki tüm nesneler silinir, ancak bağlam kapatılmaz. | |
kalıcılık bağlamı, close() için açıklanan şekilde veritabanıyla senkronize edilir |
JPA istemcisi, önceki [EntityManager].flush yöntemini kullanarak kalıcılık bağlamının veritabanıyla senkronizasyonunu zorlayabilir. Senkronizasyon açık veya örtük olabilir. İlk durumda, senkronizasyon yapmak istediğinde flush işlemlerini gerçekleştirmek istemli olarak istemciye aittir; aksi takdirde senkronizasyon, daha sonra belirteceğimiz belirli zamanlarda gerçekleştirilir. Senkronizasyon modu, [EntityManager] arayüzünün aşağıdaki yöntemleri tarafından yönetilir:
flushmode için iki olası değer vardır: FlushModeType.AUTO (varsayılan): senkronizasyon, veritabanına yapılan her SELECT isteğinden önce gerçekleşir. FlushModeType.COMMIT: senkronizasyon yalnızca veritabanındaki işlemlerin sonunda gerçekleşir. | |
mevcut senkronizasyon modunu gösterir |
Özetleyelim. Varsayılan mod olan FlushModeType.AUTO modunda, kalıcılık bağlamı aşağıdaki zamanlarda veritabanıyla senkronize edilecektir:
- veritabanındaki her SELECT işlemi öncesinde
- veritabanındaki bir işlemin sonunda
- kalıcılık bağlamında bir flush veya close işlemi sonrasında
FlushModeType.COMMIT modunda da durum aynıdır; ancak 1 numaralı işlem gerçekleşmez. JPA katmanıyla etkileşimin normal modu, işlem modudur. İstemci, bir işlem içinde kalıcılık bağlamı üzerinde çeşitli işlemler gerçekleştirir. Bu durumda, kalıcılık bağlamının veritabanıyla senkronize edildiği anlar, AUTO modunda yukarıdaki durum 1 ve 2’dir; COMMIT modunda ise yalnızca durum 2’dir.
Son olarak, Query arayüzündeki API'e değinelim; bu arayüz, kalıcılık bağlamına JPQL komutları göndermek veya verileri bulmak için doğrudan veritabanına SQL komutları göndermek için kullanılır. Query arayüzü şu şekildedir:
![]() |
- 1 - getResultList yöntemi, birden fazla nesne döndüren bir SELECT komutunu yürütür. Bu nesneler, bir List nesnesi içinde elde edilir. Bu nesne bir arayüzdür. Bu arayüz, L listesindeki öğeleri aşağıdaki biçimde taramaya olanak tanıyan bir Iterator nesnesi sunar:
Iterator iterator = L.iterator();
while (iterator.hasNext()) {
// Listenin geçerli öğesini temsil eden iterator.next() nesnesini kullan
...
}
L listesi, bir for nesnesiyle de kullanılabilir:
for (Object o : L) {
// o nesnesini kullan
}
- 2 - getSingleResult yöntemi, tek bir nesne döndüren JPQL / SQL / SELECT komutunu yürütür.
- 3 - executeUpdate yöntemi, SQL güncelleme veya silme emrini yürütür ve işlemin etkilediği satır sayısını döndürür.
- 4 - setParameter(String, Object) yöntemi, parametreleri belirlenmiş bir JPQL komutundaki adlandırılmış bir parametreye değer atamaya olanak tanır
- 5 - setParameter(int, Object) yöntemi, ancak parametre adıyla değil, JPQL komutundaki konumuyla belirtilir.
4.4. i JPQL
JPQL (Java Persistence Query Language), JPA katmanının sorgu dilidir. JPQL dili, veritabanlarının SQL diline benzerdir. SQL tablolarla çalışırken, JPQL bu tablolardaki görüntü nesneleriyle çalışır. Aşağıdaki mimari içinde bir örnek inceleyeceğiz:
![]() |
[dbrdvmedecins2] olarak adlandıracağımız veritabanı, dört tablo içeren bir MySQL5 veritabanıdır:
![]() |
Bu veritabanı, bir grup doktorun randevularını yönetmeye olanak tanıyan bilgileri bir araya getirir.
4.4.1. [MEDECINS] tablosu
Bu tablo, doktorlarla ilgili bilgileri içerir.
![]() | ![]() |
- ID: doktoru tanımlayan numara - tablonun birincil anahtarı
- VERSION: Tablodaki satırın sürümünü tanımlayan numara. Bu sayı, satırda her değişiklik yapıldığında 1 artırılır.
- NOM: doktorun soyadı
- PRENOM: doktorun adı
- TITRE: unvanı (Bayan, Bayan, Bay)
4.4.2. [CLIENTS] tablosu
Farklı doktorların hastaları [CLIENTS] tablosunda kaydedilir:
![]() | ![]() |
- ID: müşteriyi tanımlayan numara - tablonun birincil anahtarı
- VERSION: Tablodaki satırın sürümünü tanımlayan numara. Bu sayı, satırda her değişiklik yapıldığında 1 artırılır.
- NOM: müşterinin adı
- PRENOM: müşterinin adı
- TITRE: unvanı (Bayan, Bayan, Bay)
4.4.3. [CRENEAUX] tablosu
Bu tablo, RV'lerin mümkün olduğu zaman aralıklarını listeler:
![]() |
![]() |
- ID: zaman aralığını tanımlayan numara - tablonun birincil anahtarı (satır 8)
- VERSION: Tablodaki satırın sürümünü tanımlayan numara. Bu sayı, satırda her değişiklik yapıldığında 1 artırılır.
- ID_MEDECIN: Bu zaman diliminin ait olduğu doktoru tanımlayan numara – MEDECINS (ID) sütunundaki yabancı anahtar.
- HDEBUT: zaman aralığının başlangıç saati
- MDEBUT: zaman aralığının başlangıç dakikası
- HFIN: zaman aralığının bitiş saati
- MFIN: zaman aralığının bitiş dakikası
[CRENEAUX] tablosunun ikinci satırı (yukarıdaki [1]'e bakınız), örneğin, 2 numaralı zaman diliminin saat 08:20'de başlayıp 08:40'ta bittiğini ve 1 numaralı doktora ait olduğunu gösterir (Bayan Marie PELISSIER) ait olduğunu gösterir.
4.4.4. [RV] tablosu
Bu tablo, her doktor için ayrılan RV'leri listeler:
![]() |
- ID: RV'i benzersiz şekilde tanımlayan numara – birincil anahtar
- JOUR: RV'in günü
- ID_CRENEAU: RV'in zaman aralığı – [CRENEAUX] tablosundaki [ID] alanına yabancı anahtar – hem zaman aralığını hem de ilgili doktoru belirler.
- ID_CLIENT: Rezervasyonun yapıldığı müşterinin numarası – [CLIENTS] tablosundaki [ID] alanında yabancı anahtar
Bu tablo, birleştirilen sütunların değerleri (JOUR, ID_CRENEAU) üzerinde tekillik kısıtlaması içeren bir 'e sahiptir:
[RV] tablosundaki bir satırın değeri (JOUR1, ID_CRENEAU1) değerine sahipse, bu değer başka hiçbir yerde bulunamaz. Aksi takdirde, bu durum aynı hekim için aynı anda iki adet RV'in alınmış olduğu anlamına gelir. Java programlama açısından bakıldığında, veritabanındaki JDBC sürücüsü, bu durum meydana geldiğinde bir SQLException başlatır.
id satırındaki 3 değeri (yukarıdaki [1]'e bakınız), 23/08/2006 tarihinde 20 numaralı zaman dilimi ve 4 numaralı müşteri için bir RV'in rezerve edildiği anlamına gelir. [CRENEAUX] tablosundan, 20 numaralı randevu aralığının 16:20 - 16:40 saat aralığına karşılık geldiği ve 1 numaralı hekime (Bayan Marie PELISSIER) ait olduğu anlaşılmaktadır. [CLIENTS] tablosundan, 4 numaralı müşterinin Bayan Brigitte BISTROU olduğu anlaşılmaktadır.
4.4.5. Veritabanının Oluşturulması
Tabloları oluşturmak ve doldurmak için [dbrdvmedecins2.sql] komut dosyasını kullanabiliriz. [WampServer] ile şu şekilde ilerleyebiliriz:
![]() |
- [1]'te, [WampServer] simgesine tıklayıp [PhpMyAdmin] [2] seçeneğini seçin,
- [3]'te, açılan pencerede [Bases de données] bağlantısını seçin,
![]() |
- [2]'e geçin, [4] adını ve [5] kodlamasını verdiğiniz bir veritabanı oluşturun,
- [7]'te veritabanı oluşturulmuştur. Bağlantısına tıklanır,
![]() |
- [8]'te, SQL adlı bir dosyayı içe aktarıyoruz,
- dosya sisteminde [9] düğmesiyle seçip,
![]() |
- [11]'te, SQL komut dosyasını seçip [12]'te çalıştırdığımızda,
- [13]'te, veritabanındaki dört tablo oluşturulmuştur. Bağlantılardan birini takip ediyoruz,
![]() |
- 'de [14]'te tablonun içeriği görüntülenir.
Bundan sonra bu veritabanına bir daha dönmeyeceğiz. Ancak okuyucunun, özellikle de programlar çalışmadığında, programlar boyunca veritabanındaki gelişmeleri takip etmesi önerilir.
4.4.6. [JPA] katmanı
Örneğin mimarisine geri dönelim:
![]() |
Şimdi [JPA] katmanının Maven projesini oluşturuyoruz.
4.4.7. NetBeans projesi
Şu şekildedir:
![]() |
- [1]'te, [Java Application] [2] türünde bir Maven projesi oluşturuyoruz,
- [3]'te projeye bir isim verilir,
![]() |
- [4]'te, oluşturulan proje.
4.4.8. [JPA] katmanının oluşturulması
Şimdi inşa etmemiz gereken mimariye geri dönelim:
![]() |
NetBeans ile [JPA] katmanını otomatik olarak oluşturmak mümkündür. Oluşturulan kod, JPA varlıklarının nasıl yazılacağına dair değerli ipuçları verdiği için bu otomatik oluşturma yöntemlerini bilmek önemlidir.
4.4.9. NetBeans ile veritabanına bağlantı oluşturma
- SGBD MySQL 5'i çalıştırarak BD'in kullanılabilir olmasını sağlayın,
- [dbrdvmedecins2] veritabanına bir NetBeans bağlantısı oluşturun,
![]() |
- sekmesinde [Services] [1], [Databases] [2] dalında, JDBC MySQL [3] sürücüsünü seçin,
- ardından [4] "Connect Using" seçeneğini seçin; bu, MySQL veritabanıyla bağlantı kurmanızı sağlar,
- [5]'te istenen bilgileri girin. [6]'te veritabanının adını, [7]'te veritabanı kullanıcısını ve şifresini,
- [8]'te girdiğiniz bilgileri test edebilirsiniz,
- [9]'te, bilgiler doğruysa beklenen mesaj,
![]() |
- [10]'te bağlantı kurulur. Burada, bağlanılan veritabanındaki dört tablo görülür.
4.4.10. Kalıcılık biriminin oluşturulması
Şu anda oluşturulmakta olan mimariye geri dönelim:
![]() |
Şu anda [JPA] katmanını oluşturuyoruz. Bu katmanın yapılandırması, kalıcılık birimlerinin tanımlandığı [persistence.xml] dosyasında yapılır. Her bir kalıcılık birimi için aşağıdaki bilgiler gereklidir:
- veritabanına erişim özellikleri (JDBC; URL, kullanıcı adı, şifre),
- veritabanı tablolarının görüntülerini oluşturacak sınıflar,
- kullanılacak JPA uygulaması. Aslında, JPA, çeşitli ürünler tarafından uygulanan bir spesifikasyondur. Burada Hibernate'i kullanacağız.
NetBeans, bir sihirbaz yardımıyla bu kalıcılık dosyasını oluşturabilir.
![]() |
- projeye sağ tıklayın ve [1] kalıcılık birimi oluşturmayı seçin,
- [2]'te bir kalıcılık birimi oluşturun,
![]() |
- [3]'te, oluşturulan kalıcılık birimine bir ad verin,
- [4]'te, JPA Hibernate (JPA 2.0) uygulamasını seçin,
- [5]'te, BD'teki tabloların zaten oluşturulduğunu ve bu nedenle bunların oluşturulmayacağını belirtin. Sihirbazı onaylayın,
- [6]'te yeni proje,
- [7]'te, [persistence.xml] dosyası [META-INF] klasöründe oluşturuldu,
- [8]'te, Maven projesine yeni bağımlılıklar eklendi.
Oluşturulan [META-INF/persistence.xml] dosyası şöyledir:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mv-rdvmedecins-jpql-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbrdvmedecins2"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
</properties>
</persistence-unit>
</persistence>
Bu dosya, sihirbazda girilen bilgileri içerir:
- 3. satır: kalıcılık biriminin adı,
- 3. satır: veritabanı işlemlerinin türü. Burada, RESOURCE_LOCAL, uygulamanın işlemlerini kendi başına yöneteceğini belirtir,
- 6-9. satırlar: veri kaynağının JDBC özellikleri.
[Design] sekmesinde, [persistence.xml] dosyasının genel bir görünümünü elde edebilirsiniz:
![]() |
Hibernate günlüklerini elde etmek için, [persistence.xml] dosyasını şu şekilde tamamlıyoruz:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mv-rdvmedecins-jpql-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbrdvmedecins2"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
- 11. satır: Hibernate tarafından oluşturulan SQL emirlerini görüntülemek isteniyor,
- 12. satır: Bu özellik, bu emirlerin biçimlendirilmiş bir şekilde görüntülenmesini sağlar.
Projeye bağımlılıklar eklendi. [pom.xml] dosyası şu şekildedir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-rdvmedecins-jpql-hibernate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-rdvmedecins-jpql-hibernate</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.1.0.GA</version>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.transaction</groupId>
<artifactId>jboss-transaction-api_1.1_spec</artifactId>
<version>1.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.15.0-GA</version>
</dependency>
<dependency>
<groupId>org.hibernate.common</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>4.0.1.Final</version>
</dependency>
</dependencies>
</project>
Eklenen bağımlılıkların tümü Hibernate ile ilgilidir. MySQL'ten JDBC sürücüsünün bağımlılığını ekleyeceğiz:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
4.4.11. JPA varlıklarının oluşturulması
JPA varlıkları, NetBeans'teki bir sihirbaz aracılığıyla oluşturulabilir:
![]() |
- [1]'te, bir veritabanından JPA varlıklar oluşturulur,
![]() |
- [2]'e dönüştürürken, daha önce oluşturulan [dbrdvmedecins2] bağlantısı seçilir,
- [3]'te, ilgili veritabanındaki tüm tabloları seçiyoruz,
![]() |
- [4]'te, dört tabloya ilişkin Java sınıflarına bir ad verilir,
- ve bir paket adı verilir ([5]),
- [6]'te, JPA, BD'teki tablo satırlarını koleksiyonlarda toplar. Koleksiyon olarak listeyi seçiyoruz,
![]() |
- [7]'te, sihirbaz tarafından oluşturulan Java sınıfları.
4.4.12. Oluşturulan JPA varlıklar
[Medecin] varlığı, [medecins] tablosunun yansımasıdır. Java sınıfı, ilk bakışta kodu okunması zor hale getiren çok sayıda açıklama ile doludur. Varlığın rolünü anlamak için gerekli olan unsurları elimizde tutarsak, şu kodu elde ederiz:
package rdvmedecins.jpa;
...
@Entity
@Table(name = "medecins")
public class Medecin implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "TITRE")
private String titre;
@Column(name = "NOM")
private String nom;
@Column(name = "VERSION")
private int version;
@Column(name = "PRENOM")
private String prenom;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "idMedecin")
private List<Creneau> creneauList;
// yapıcılar
....
// alıcı ve ayarlayıcılar
....
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
- 4. satırda, @Entity anotasyonu, [Medecin] sınıfını bir varlık haline getirir: JPA, c.a.d. BD tablosuna, API ve JPA aracılığıyla bağlı bir sınıf,
- 5. satırda, JPA varlığıyla ilişkili BD tablosunun adı yer almaktadır. Tablodaki her alan, Java sınıfındaki bir alana karşılık gelir,
- 6. satırda, sınıf Serializable arayüzünü uygular. Bu, varlıkların istemci ile sunucu arasında serileştirildiği istemci/sunucu uygulamalarında gereklidir.
- 10-11. satırlar: [Medecin] sınıfındaki id alanı, [medecins] tablosundaki [ID] alanına (10. satır) karşılık gelir,
- 13-14. satırlar: [Medecin] sınıfındaki titre alanı, [medecins] tablosundaki [TITRE] alanına (13. satır) karşılık gelir,
- satır 16-17: [Medecin] sınıfındaki "ad" alanı, [medecins] tablosundaki [NOM] alanına (satır 16) karşılık gelir,
- 19-20. satırlar: [Medecin] sınıfındaki "version" alanı, [medecins] tablosundaki [VERSION] alanına (19. satır) karşılık gelir. Burada sihirbaz, sütunun aslında ait olduğu satır her değiştirildiğinde artırılması gereken bir sürüm sütunu olduğunu tanımıyor. Ona bu rolü vermek için @Version ek açıklaması eklenmelidir. Bunu bir sonraki adımda yapacağız,
- 22-23. satırlar: [Medecin] sınıfındaki prenom alanı, [medecins] tablosundaki [PRENOM] alanına karşılık gelir,
- 10-11. satırlar: id alanı, tablonun [ID] birincil anahtarına karşılık gelir. 8-9. satırlardaki anotasyonlar bu noktayı netleştirir,
- 8. satır: @Id açıklaması, açıklanan alanın tablonun birincil anahtarıyla ilişkili olduğunu belirtir,
- 9. satır: [JPA] katmanı, [Medecins] tablosuna ekleyeceği satırların birincil anahtarını oluşturacaktır. Birkaç farklı strateji mümkündür. Burada GenerationType.IDENTITY stratejisi, JPA katmanının MySQL tablosunun auto_increment modunu kullanacağını belirtir,
- 25-26. satırlar: [creneaux] tablosu, [medecins] tablosuna bir yabancı anahtara sahiptir. Bir randevu aralığı bir doktora aittir. Tersine, bir doktorun kendisine atanan birden fazla randevu aralığı vardır. Dolayısıyla, bir (doktor) ile birden fazla (randevu aralığı) arasındaki bir ilişki söz konusudur; bu ilişki, @OneToMany ile JPA arasındaki notla tanımlanmıştır (25. satır). 26. satırdaki alan, doktorun tüm randevu zaman aralıklarını içerecektir. Bu, herhangi bir programlama gerektirmez. 25. satırı tam olarak anlamak için [Creneau] sınıfını tanıtmamız gerekir.
Bu sınıf şu şekildedir:
package rdvmedecins.jpa;
import java.io.Serializable;
import java.util.List;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "creneaux")
public class Creneau implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "MDEBUT")
private int mdebut;
@Column(name = "HFIN")
private int hfin;
@Column(name = "HDEBUT")
private int hdebut;
@Column(name = "MFIN")
private int mfin;
@Column(name = "VERSION")
private int version;
@JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Medecin idMedecin;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "idCreneau")
private List<Rv> rvList;
// yapıcılar
...
// getter ve setter'lar
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
Sadece yeni açıklamaları yorumlayacağız:
- [creneaux] tablosunun, [medecins] tablosuna bir yabancı anahtara sahip olduğunu belirtmiştik: bir randevu aralığı bir doktora atanır. Aynı doktora birden fazla randevu aralığı atanabilir. [creneaux] tablosundan [medecins] tablosuna giden bir ilişki vardır ve bu ilişki "birden çok (zaman aralığı)" - "bir (doktor)" şeklinde tanımlanmıştır. Yabancı anahtarı tanımlamak için 32. satırdaki @ManyToOne notu kullanılır,
- 31. satırda yer alan @JoinColumn açıklaması, yabancı anahtar ilişkisini belirtir: [ID_MEDECIN] tablosunun [creneaux] sütunu, [medecins] tablosunun [ID] sütununa yabancı anahtardır,
- 33. satır: zaman diliminin sahibi olan hekime bir referans. Bu da yine programlama gerektirmeden elde edilir.
Dolayısıyla, [Creneau] varlığı ile [Medecin] varlığı arasındaki yabancı anahtar ilişkisi iki notla belirtilmiştir:
- [Creneau] varlığında:
@JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Medecin idMedecin;
- [Medecin] varlığında:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "idMedecin")
private List<Creneau> creneauList;
Bu iki anotasyon aynı ilişkiyi yansıtmaktadır: [creneaux] tablosundan [medecins] tablosuna giden yabancı anahtar ilişkisi. Bunların birbirinin tersi olduğu söylenir. Yalnızca @ManyToOne ilişkisi zorunludur. Bu ilişki, yabancı anahtar ilişkisini kesin bir şekilde tanımlar. @OneToMany ilişkisi ise isteğe bağlıdır. Varsa, yalnızca ilişkili olduğu @ManyToOne ilişkisine referans verir. Bu, [Medecin] varlığının 1. satırındaki mappedBy özniteliğinin anlamıdır. Bu özniteliğin değeri, yabancı anahtarı belirten @ManyToOne ek açıklaması bulunan [Creneau] varlığının alan adıdır. Yine [Medecin] varlığının aynı 1. satırında, cascade=CascadeType.ALL özniteliği, [Medecin] varlığının [Creneau] varlığına karşı davranışını belirler:
- eğer veritabanına yeni bir [Medecin] varlığı eklenirse, o zaman 2. satırdaki alandaki [Creneau] varlıkları da eklenmelidir,
- veritabanındaki bir [Medecin] nesnesi değiştirilirse, 2. satırdaki alandaki [Creneau] nesneleri de değiştirilmelidir,
- veritabanındaki [Medecin] varlığı silinirse, 2. satırdaki alandaki [Creneau] varlıkları da silinmelidir.
Diğer iki varlığın kodlarını, yeni bir notasyon getirmedikleri için özel bir açıklama yapmadan veriyoruz.
[Client] varlığı
package rdvmedecins.jpa;
...
@Entity
@Table(name = "clients")
public class Client implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "TITRE")
private String titre;
@Column(name = "NOM")
private String nom;
@Column(name = "VERSION")
private int version;
@Column(name = "PRENOM")
private String prenom;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "idClient")
private List<Rv> rvList;
// yapıcılar
...
// getter ve setter'lar
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
- 24-25. satırlar, [rv] tablosu ile [clients] tablosu arasındaki yabancı anahtar ilişkisini yansıtmaktadır.
[Rv] varlığı:
package rdvmedecins.jpa;
...
@Entity
@Table(name = "rv")
public class Rv implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "JOUR")
@Temporal(TemporalType.DATE)
private Date jour;
@JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Creneau idCreneau;
@JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Client idClient;
// yapıcılar
...
// getter ve setter'lar
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
- 13. satır, Date tablosundaki "gün" alanının Java türü olduğunu belirtir. Burada, [rv] tablosunda, [JOUR] sütununun (12. satır) tarih (saatsiz) türü olduğu belirtilir,
- 16-18. satırlar: [rv] tablosunun [creneaux] tablosuna olan yabancı anahtar ilişkisini tanımlar,
- satır 20-22: [rv] tablosunun [clients] tablosuna olan yabancı anahtar ilişkisini tanımlar.
JPA varlıklarının otomatik olarak oluşturulması, bize bir çalışma temeli sağlar. Bazen bu yeterlidir, bazen değildir. Bu durumda da öyle:
- varlıkların çeşitli sürüm alanlarına @Version ek açıklaması eklenmesi gerekir,
- oluşturulan yöntemlerden daha açıklayıcı toString yöntemleri yazmak gerekiyor,
- [Medecin] ve [Client] varlıklar birbirine benziyor. Bunları [Personne] sınıfından türeteceğiz,
- @OneToMany ilişkilerinin @ManyToOne ilişkilerine ters ilişkilerini kaldıracağız. Bunlar zorunlu değildir ve programlamayı karmaşıklaştırır,
- birincil anahtarlar üzerindeki @NotNull doğrulamasını kaldırıyoruz. Bir JPA varlığını MySQL ile kalıcı hale getirdiğimizde, başlangıçtaki varlığın birincil anahtarı null'tir. Veritabanına kaydedildikten sonra, kaydedilen öğenin birincil anahtarı bir değere sahip olur.
Bu özelliklere göre, sınıflar şu şekilde şekillenir:
Personne sınıfı, doktorları ve müşterileri temsil etmek için kullanılır:
package rdvmedecins.jpa;
import java.io.Serializable;
import javax.persistence.*;
@MappedSuperclass
public class Personne implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Basic(optional = false)
@Column(name = "TITRE")
private String titre;
@Basic(optional = false)
@Column(name = "NOM")
private String nom;
@Basic(optional = false)
@Column(name = "VERSION")
@Version
private int version;
@Basic(optional = false)
@Column(name = "PRENOM")
private String prenom;
// yapıcılar
...
// getter ve setter'lar
...
@Override
public String toString() {
return String.format("[%s,%s,%s,%s,%s]", id, version, titre, prenom, nom);
}
}
- 6. satır: [Personne] sınıfının kendisinin bir varlık (@Entity) olmadığına dikkat edilmelidir. Bu sınıf, varlıkların üst sınıfı olacaktır. @MappedSuperClass anotasyonu bu durumu belirtir.
[Client] varlığı, [clients] tablosundaki satırları kapsüller. Bu varlık, önceki [Personne] sınıfından türetilmiştir:
package rdvmedecins.jpa;
import java.io.Serializable;
import javax.persistence.*;
@Entity
@Table(name = "clients")
public class Client extends Personne implements Serializable {
private static final long serialVersionUID = 1L;
// yapıcılar
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
return String.format("Client[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(), getNom());
}
}
- 6. satır: [Client] sınıfı bir JPA varlığıdır,
- 7. satır: [clients] tablosuyla ilişkilidir,
- 8. satır: [Personne] sınıfından türetilmiştir.
[medecins] tablosunun satırlarını kapsayan [Medecin] varlığı da aynı modeli izler:
package rdvmedecins.jpa;
import java.io.Serializable;
import javax.persistence.*;
@Entity
@Table(name = "medecins")
public class Medecin extends Personne implements Serializable {
private static final long serialVersionUID = 1L;
// yapıcılar
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
return String.format("Médecin[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(), getNom());
}
}
[Creneau] varlığı, [creneaux] tablosunun satırlarını kapsar:
package rdvmedecins.jpa;
import java.io.Serializable;
import java.util.List;
import javax.persistence.*;
@Entity
@Table(name = "creneaux")
public class Creneau implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "ID")
private Long id;
@Basic(optional = false)
@Column(name = "MDEBUT")
private int mdebut;
@Basic(optional = false)
@Column(name = "HFIN")
private int hfin;
@Basic(optional = false)
@NotNull
@Column(name = "HDEBUT")
private int hdebut;
@Basic(optional = false)
@Column(name = "MFIN")
private int mfin;
@Basic(optional = false)
@Column(name = "VERSION")
@Version
private int version;
@JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Medecin medecin;
// yapıcılar
...
// getter ve setter'lar
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
// TODO: Uyarı - id alanları ayarlanmamışsa bu yöntem çalışmayacaktır
...
}
@Override
public String toString() {
return String.format("Creneau [%s, %s, %s:%s, %s:%s,%s]", id, version, hdebut, mdebut, hfin, mfin, medecin);
}
}
- 40-42. satırlar, veritabanındaki [creneaux] tablosu ile [medecins] tablosu arasında bulunan "birden çok-bir" ilişkisini modellemektedir: bir doktorun birden çok randevu aralığı vardır, bir randevu aralığı ise yalnızca bir doktora aittir.
[Rv] varlığı, [rv] tablosundaki satırları kapsar:
package rdvmedecins.jpa;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
@Entity
@Table(name = "rv")
public class Rv implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "ID")
private Long id;
@Basic(optional = false)
@Column(name = "JOUR")
@Temporal(TemporalType.DATE)
private Date jour;
@JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Creneau creneau;
@JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Client client;
// yapıcılar
...
// getter ve setter'lar
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
return String.format("Rv[%s, %s, %s]", id, creneau, client);
}
}
- 27-29. satırlar, veritabanındaki [rv] tablosu ile [clients] tablosu arasında bulunan "birden çok-bir" ilişkisini modellemektedir (bir müşteri birden fazla Rv'de yer alabilir) ve 23-25. satırlar, [rv] tablosu ile [creneaux] tablosu arasında var olan "birden çok-bir" ilişkisini modellemektedir (bir zaman aralığı birden fazla Rv'de yer alabilir).
4.4.13. Verilere erişim kodu
Şimdi projeye, JPA katmanı üzerinden verilere erişim kodunu ekleyeceğiz:
![]() |
![]() |
[MainJpql] sınıfı şu şekildedir:
package rdvmedecins.console;
import java.util.Scanner;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class MainJpql {
public static void main(String[] args) {
// EntityManagerFactory
EntityManagerFactory emf = Persistence.createEntityManagerFactory("mv-rdvmedecins-jpql-hibernatePU");
// entityManager
EntityManager em = emf.createEntityManager();
// klavye tarayıcısı
Scanner clavier = new Scanner(System.in);
// sorgu giriş döngüsü JPQL
System.out.println("Requete JPQL sur la base dbrdvmedecins2 (* pour arrêter) :");
String requete = clavier.nextLine();
while (!requete.trim().equals("*")) {
try {
// sorgu sonucu görüntüleme
for (Object o : em.createQuery(requete).getResultList()) {
System.out.println(o);
}
} catch (Exception e) {
System.out.println("L'exception suivante s'est produite : " + e);
}
// kalıcı verilerin içeriği boşaltılır
em.clear();
// yeni sorgu
System.out.println("---------------------------------------------");
System.out.println("Requete JPQL sur la base dbrdvmedecins2 (* pour arrêter) :");
requete = clavier.nextLine();
}
// kaynakların kapatılması
em.close();
emf.close();
}
}
- 12. satır: Daha önce oluşturduğumuz kalıcılık birimiyle ilişkili EntityManagerFactory sınıfının oluşturulması. createEntityManagerFactory yönteminin parametresi, bu kalıcılık biriminin adıdır:
<persistence-unit name="mv-rdvmedecins-jpql-hibernatePU" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>
- 14. satır: Kalıcılık katmanını yöneten EntityManager'in oluşturulması,
- 19. satır: JPQL ve select sorgularının girilmesi,
- 23-28. satırlar: sorgu sonucunun görüntülenmesi,
- 20. satır: Kullanıcı * tuşuna bastığında giriş işlemi sona erer.
Soru: Aşağıdaki bilgileri elde etmeyi sağlayan JPQL sorgularını belirtiniz:
- adlarına göre azalan sırada doktorların listesi
- unvanı 'Mr' olan doktorların listesi
- Bayan Pelissier'in randevu saatlerinin listesi
- günlere göre artan sırada alınan randevuların listesi
- 24/08/2006 tarihinde Bayan PELISSIER ile RV randevusu alan müşterilerin (soyadı) listesi
- 24/08/2006 tarihinde Bayan PELISSIER'in müşteri sayısı
- Randevu almamış müşteriler
- Randevusu olmayan doktorlar
[ref1]'in 2.7. paragrafındaki örnekten yararlanılacaktır. İşte bir uygulama örneği:
- 2. satır: JPQL sorgusu,
- 3-11. satırlar: ilgili SQL sorgusu,
- 12-15. satırlar: JPQL sorgusunun sonucu.
4.5. Kalıcılık bağlamı ile SGBD arasındaki bağlantılar
4.5.1. Kişi sınıfı
4.5.2. Test programı
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
4.5.3. Hibernate yapılandırması
4.5.4. log4j.properties yapılandırması
4.5.5. Sonuçlar
Soru: Java koduyla görüntülenen sonuçlar arasındaki bağlantıyı kurun.





































