5. Sürüm 1: Spring Mimarisi / JPA
Bir belediyenin "Erken Çocukluk Merkezi"nde çalışan çocuk bakıcılarının maaş bordrolarını hazırlamaya yarayan bir konsol uygulaması ve bir grafik kullanıcı arayüzü uygulaması yazmayı planlıyoruz. Bu uygulama aşağıdaki mimariye sahip olacaktır:
![]() |
5.1. BD Veritabanı
Maaş bordrosunu oluşturmak için gerekli statik veriler, bundan sonra dbpam olarak adlandıracağımız bir veritabanına yerleştirilecektir. Bu veritabanı aşağıdaki tabloları içerebilir:
Yapısı:
birincil anahtar | |
sürüm numarası – satır her değiştirildiğinde artar | |
çalışanın sosyal güvenlik numarası – benzersiz | |
çalışanın adı | |
ad | |
adresi | |
şehri | |
posta kodu | |
[ID] alanındaki yabancı anahtar, [INDEMNITES] tablosunda |
İçeriği şu şekilde olabilir:

Yapı:
birincil anahtar | |
sürüm numarası – satır her değiştirildiğinde artar | |
yüzde: genel sosyal katkı payı + sosyal borç geri ödemesine katkı payı | |
yüzde: indirilebilir genel sosyal katkı payı | |
yüzde: sosyal güvenlik, dul ve yaşlılık | |
yüzde: ek emeklilik + işsizlik sigortası |
İçeriği şu şekilde olabilir:
![]()
Sosyal güvenlik prim oranları çalışandan bağımsızdır. Yukarıdaki tablo sadece bir satırdan oluşur.
birincil anahtar | ||
sürüm numarası – satır her değiştirildiğinde artar | ||
işlem numarası – benzersiz | ||
bir saatlik nöbetin net fiyatı (Euro) | ||
bakım günü başına euro cinsinden bakım ücreti | ||
bakım günü başına euro cinsinden yemek ödeneği | ||
Ücretli izin ödeneği. Bu, temel maaşa uygulanacak bir yüzde oranıdır. | ||
İçeriği şu şekilde olabilir:

Ödeneklerin çocuk bakıcısından bakıcıya farklılık gösterebileceği unutulmamalıdır. Bu ödenekler, çocuk bakıcısının maaş endeksi aracılığıyla o bakıcıya özel olarak belirlenir. Örneğin, maaş endeksi 2 olan Bayan Marie Jouveinal’ın (EMPLOYES tablosu) saatlik maaşı 2,1 avrodur (INDEMNITES tablosu).
5.2. Bir çocuk bakıcısının maaşının hesaplanma yöntemi
Şimdi bir çocuk bakıcısının aylık maaşının hesaplanma yöntemini sunuyoruz. Bu, gerçekte kullanılan yöntem olduğunu iddia etmemektedir. Örnek olarak, ödeme yapılacak ay boyunca 20 gün boyunca 150 saat çalışmış olan Bayan Marie Jouveinal’ın maaşını ele alıyoruz.
Aşağıdaki unsurlar dikkate alınmaktadır: | | |
Çocuk bakıcısının temel maaşı aşağıdaki formülle hesaplanır: | ||
Bir miktar sosyal güvenlik primi bu temel maaştan : | | |
Toplam sosyal güvenlik primleri: | ||
Ayrıca, çocuk bakıcısı, çalıştığı her gün için geçim ödeneği ve yemek ödeneği almaya hak kazanır. Bu kapsamda aşağıdaki ödenekleri alır: | | |
Sonuç olarak, çocuk bakıcısına ödenecek net maaş şu şekildedir: |
5.3. Konsol uygulamasının işleyişi
İşte konsol uygulamasının bir DOS penceresinde çalıştırılmasına ilişkin bir örnek:
Aşağıdaki bilgileri alacak bir program yazacağız:
- çocuk bakıcısının sosyal güvenlik numarası (örnekte 254104940426058 - 1. satır)
- toplam çalışma saati sayısı (örnekte 150 - 1. satır)
- toplam çalışma günü sayısı (örnekte 20 - 1. satır)
Görüldüğü gibi:
- 9-14. satırlar: Sosyal güvenlik numarası verilen çalışana ait bilgileri gösterir
- 17-20. satırlar: çeşitli prim oranlarını gösterir
- 23-26. satırlar: çalışanın maaş endeksine (burada endeks 2) bağlı ödenekleri gösterir
- 29-33. satırlar: ödenecek maaşın bileşenlerini gösterir
Uygulama, olası hataları bildirir:
Parametre olmadan çağrıldığında:
dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar
Syntaxe : pg num_securite_sociale nb_heures_travaillées nb_jours_travaillés
Hatalı verilerle çağrı:
dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar 254104940426058 150x 20x
Le nombre d'heures travaillées [150x] est erroné
Le nombre de jours travaillés [20x] est erroné
Hatalı sosyal güvenlik numarasıyla yapılan çağrı:
dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar xx 150 20
L'erreur suivante s'est produite : L'employé de n°[xx] est introuvable
5.4. Grafik uygulamanın işleyişi
Grafik uygulama, bir Swing formu aracılığıyla çocuk bakıcılarının maaşlarının hesaplanmasına olanak tanır:
![]() |
- Konsol programına parametre olarak aktarılan bilgiler artık [1, 2, 3] giriş alanları aracılığıyla girilmektedir.
- [4] düğmesi maaş hesaplamasını başlatır
- Form, ödenecek net maaşa kadar maaşın çeşitli bileşenlerini görüntüler: [5]
[1, 6] açılır listesi, çalışanların SS numaralarını değil, ad ve soyadlarını gösterir. Burada, aynı ad ve soyada sahip iki çalışan olmadığı varsayılmaktadır.
5.5. Veritabanının oluşturulması
WampServer komutunu çalıştırıyoruz ve PhpMyAdmin ile [1] araçlarını kullanıyoruz:
![]() |
- [2]'te [Bases de données] seçeneğini seçiyoruz,
![]() |
- [3]'e, bir veritabanı oluşturulur [dbpam_hibernate],
- [4]'te, oluşturulan veritabanı. Onu seçiyoruz,
![]() |
- [5]'te, SQL komut dosyasını içe aktarmak istiyoruz,
- [6]'e, dosyayı seçmek için [Parcourir] düğmesini kullanıyoruz,
![]() |
- [7,8]'te, SQL komut dosyasını seçiyoruz,
- [9]'te, komut dosyası çalıştırılır,
![]() |
- [10]'e dönüştürüldüğünde, tablolar oluşturulmuştur. İçerikleri şöyledir:
EMPLOYES tablosu

INDEMNITES tablosu

COTISATIONS tablosu
![]()
5.6. Uygulama JPA
5.6.1. JPA Katmanı / Hibernate
Aşağıdaki ortamda JPA katmanını yapılandıracağız:
![]() |
Bir konsol programı veritabanıyla çalışacaktır. Bunun için şunlar gereklidir:
- bir veritabanına sahip olmak,
- SGBD sürücüsünün JDBC sürücüsüne sahip olmak (burada MySQL),
- Hibernate ile JPA katmanını uygulamak,
- konsol programını yazmak.
[mv-pam-jpa-hibernate] [1] adlı Maven projesini oluşturuyoruz:
![]() |
Uygulamamızın mimarisinde şu öğelere ihtiyacımız var:
- veritabanı,
- JDBC sürücüsü (SGBD ve MySQL),
- JPA / Hibernate katmanı (varlıklar ve yapılandırma),
- test konsol programı.
5.6.1.1. Veritabanı
Öncelikle boş veritabanını oluşturalım. WampServer'i başlatıp PhpMyAdmin ve [1] araçlarını kullanıyoruz:
![]() |
- [2]'te [Bases de données] seçeneğini seçin,
![]() |
- [3]'e dönüştürürken, [dbpam_hibernate] adlı bir veritabanı oluşturulur,
- [4]'te, veritabanı oluşturuldu.
5.6.1.2. JPA katmanının yapılandırılması
JDBC katmanı ile veritabanı arasındaki bağlantı, JPA katmanını yapılandıran [persistence.xml] dosyası aracılığıyla sağlanır. Bu dosya NetBeans ile oluşturulabilir:
![]() |
- [services] [1] sekmesinde, JDBC sürücüsü kullanılarak MySQL [2] veritabanına bağlanılır,
- [3] sekmesinde, bağlanmak istediğiniz veritabanının adı.
- [4]'te, veritabanının URL JDBC'i,
- [5]'te, root olarak şifre girmeden bağlanılır,
- [6]'te bağlantıyı test edebiliriz,
- [7]'te bağlantı başarılı oldu.
![]() |
- bağlantı [8] ve [9] olarak görünür,
- [10]'te projeye yeni bir öğe ekleniyor,
![]() |
- [11]'te [Persistence] kategorisi seçilir ve [12]'te [Persistence Unit] öğesi seçilir,
- [13]'te bu kalıcılık birimine bir ad verilir,
- [14]'te bir Hibernate uygulaması seçilir,
- [15]'te, az önce oluşturduğumuz MySQL veritabanına bağlantıyı belirleriz,
- [16] adımında, JPA katmanının örneklendirilmesi sırasında, bu katmanın projedeki JPA varlıklarına karşılık gelen tabloları oluşturması (create) gerektiğini belirtiriz.
Sihirbazın tamamlanmasıyla [persistence.xml] dosyası oluşturulur:
![]() |
- dosya, projenin yeni bir dalında, [META-INF] [1] klasöründe görünür;
- bu klasör, [2,3] projesindeki [src/main/resources] klasörüne karşılık gelir.
İçeriği şu şekildedir:
<?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-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<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.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
- 3. satır: kalıcılık biriminin adı ve işlem türü. RESOURCE_LOCAL, projenin işlemleri kendi başına yönettiğini gösterir. Burada bunu yapması gereken konsol programıdır,
- 4. satır: Kullanılan JPA uygulaması Hibernate'tir,
- satır 6-9: veritabanı bağlantısının özellikleri JDBC,
- 11. satır: JPA varlıklarına karşılık gelen tabloların oluşturulmasını ister. Aslında, NetBeans burada hatalı bir yapılandırma oluşturur. Yapılandırma şu şekilde olmalıdır:
<property name="hibernate.hbm2ddl.auto" value="create"/>
"create" seçeneği ile Hibernate, JPA katmanının oluşturulması sırasında, JPA varlıklarına karşılık gelen tabloları siler ve ardından yeniden oluşturur. "create-drop" seçeneği de aynı işlevi görür, ancak JPA katmanının ömrü sona erdiğinde tüm tabloları siler. Başka bir seçenek daha vardır:
<property name="hibernate.hbm2ddl.auto" value="update"/>
Bu seçenek, tablolar mevcut değilse oluşturur, ancak zaten mevcutsa silmez.
Hibernate yapılandırmasına üç özellik daha ekleyeceğiz:
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
Bu özellikler, Hibernate'ten veritabanına gönderdiği SQL komutlarını görüntülemesini ister. Dolayısıyla tam dosya şu şekildedir:
<?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-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>jpa.Cotisation</class>
<class>jpa.Employe</class>
<class>jpa.Indemnite</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<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.hbm2ddl.auto" value="create"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>
5.6.1.3. Bağımlılıklar
Proje mimarisine geri dönelim:
![]() |
JPA katmanını [persistence.xml] dosyası aracılığıyla yapılandırdık. Seçilen uygulama Hibernate oldu. Bu, projeye bazı bağımlılıklar getirdi:
![]() |
Bu bağımlılıklar, Hibernate'in projeye dahil edilmesinden kaynaklanmaktadır. Mimariye ait JDBC katmanını uygulayan MySQL'in JDBC sürücüsüne ait bir başka bağımlılığı daha eklememiz gerekiyor. [pom.xml] dosyasını şu şekilde güncelliyoruz:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
</dependency>
...
<dependency>
<groupId>org.hibernate.common</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>4.0.1.Final</version>
</dependency>
</dependencies>
8-12. satırlar, MySQL dosyasına JDBC sürücüsünün bağımlılığını ekler.
5.6.1.4. JPA
![]() |
Soru: 4.4. paragrafındaki örnekteki adımları izleyerek, [Cotisation, Indemnite, Employe] varlıklarını oluşturun.
Notlar:
- varlıklar, [jpa] adlı bir paketin parçası olacaktır,
- her varlık bir sürüm numarasına sahip olacaktır,
- iki varlık bir ilişkiyle birbirine bağlıysa, yalnızca ana ilişki @ManyToOne oluşturulacaktır. Ters ilişki @OneToMany oluşturulmayacaktır.
5.6.1.5. Ana sınıfın kodu
Projeye, daha önce geliştirilen JPA ve [1] varlıklarını dahil ediyoruz:
![]() |
ardından [2]'i ve aşağıdaki [main.Main] sınıfını ekliyoruz:
package main;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class Main {
public static void main(String[] args) {
// Entity Manager'ı oluşturmak, JPA katmanını oluşturmak için yeterlidir
EntityManagerFactory emf = Persistence.createEntityManagerFactory("mv-pam-jpa-hibernatePU");
EntityManager em=emf.createEntityManager();
// kaynakların serbest bırakılması
em.close();
emf.close();
}
}
- 10. satır: [mv-pam-jpa-hibernatePU] adlı kalıcılık biriminin EntityManagerFactory'ini oluşturuyoruz. Bu ad, [persistence.xml] dosyasından gelmektedir:
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>
- 12. satır: EntityManager dosyası oluşturulur. Bu işlem, JPA katmanını oluşturur. [persistence.xml] dosyası işlenecek ve dolayısıyla veritabanı tabloları oluşturulacaktır,
- 14-15. satırlar: Kaynaklar serbest bırakılır.
5.6.1.6. Tests
Projemizin mimarisine geri dönelim:
![]() |
Tüm katmanlar uygulandı. [2] projesini çalıştırıyoruz.
![]() |
Konsol çıktıları şu şekildedir:
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 | |
Çalıştırılan program, JPA katmanını örneklendirmek dışında hiçbir işlem yapmadığından, konsolda yalnızca Hibernate günlükleri bulunur. Aşağıdaki noktalara dikkat edilmelidir:
- 43. satır: Hibernate, [EMPLOYES] tablosundaki yabancı anahtarı silmeye çalışıyor,
- 51-55. satırlar: üç tablonun silinmesi,
- 57. satır: [COTISATIONS] tablosunun oluşturulması,
- 67. satır: [EMPLOYES] tablosunun oluşturulması,
- satır 80: [INDEMNITES] tablosunun oluşturulması,
- 91. satır: [EMPLOYES] tablosunun yabancı anahtarının oluşturulması.
NetBeans'te, daha önce oluşturulan bağlantıda tablolar görülebilir:
![]() |
Oluşturulan tablolar, hem kullanılan JPA katmanının hem de kullanılan SGBD'in uygulamasına bağlıdır. Dolayısıyla, aynı veritabanını kullanan bir JPA / EclipseLink uygulaması, farklı tablolar oluşturabilir. Şimdi bunu inceleyeceğiz.
5.6.2. JPA / EclipseLink Katmanı
Aşağıdaki ortamda yeni bir Maven projesi oluşturacağız:
![]() |
Bir önceki paragrafta anlatılan adımları takip edeceğiz:
- MySQL ve [dbpam_eclipselink] veritabanlarını oluşturacağız. Bunları oluşturmak için [dbpam_eclipselink.sql] komut dosyasını kullanacağız,
- projenin [persistence.xml] dosyasını oluşturacağız. JPA 2.0 EclipseLink uygulamasını alacağız,
- oluşturulan bağımlılıklara MySQL'in JDBC sürücüsü bağımlılığını ekleyin,
- JPA varlıklarını ve konsol programını ekleyin,
- testleri yapın.
[persistence.xml] dosyası şu şekilde olacaktır:
<?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="pam-jpa-eclipselinkPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>jpa.Cotisation</class>
<class>jpa.Employe</class>
<class>jpa.Indemnite</class>
<properties>
<property name="eclipselink.target-database" value="MySQL"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/>
<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="eclipselink.logging.level" value="FINE"/>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
- 9-13 numaralı özellikler NetBeans sihirbazı tarafından oluşturulmuştur,
- 14. satır: Bu özellik, EclipseLink'in günlük düzeyini ayarlamamızı sağlar. FINE seviyesi, EclipseLink'in veritabanına göndereceği SQL komutlarını bilmemizi sağlar,
- 15. satır: JPA / EclipseLink katmanının örneklendirilmesi sırasında, JPA varlık tabloları silinecek ve ardından yeniden oluşturulacaktır.
Konsolda elde edilen sonuçlar şunlardır:
- 26-30. satırlar: MySQL veritabanına bağlantı,
- satır 31-34: bağlantının başarılı olduğu teyit edildi,
- 36. satır: [EMPLOYES] tablosundaki yabancı anahtarın silinmesi,
- 37. satır: [COTISATIONS] tablosunun silinmesi,
- satır 38: [COTISATIONS] tablosunun oluşturulması. İlginç bir şekilde, ID birincil anahtarının MySQL ve auto_increment özniteliklerine sahip olmadığı görülmektedir. Bu, birincil anahtarın değerlerini oluşturanın MySQL olmadığı anlamına gelir,
- 39. satır: [EMPLOYES] tablosunun silinmesi,
- 40. satır: [EMPLOYES] tablosu oluşturuldu. Bu tablonun birincil anahtarı ID, MySQL ve auto_increment özniteliklerine sahip değildir,
- 41. satır: [INDEMNITES] tablosunun silinmesi,
- 42. satır: [INDEMNITES] tablosu oluşturuldu. Bu tablonun birincil anahtarı ID, MySQL ve auto_increment özniteliklerine sahip değildir,
- 43. satır: [EMPLOYES] tablosundan [INDEMNITES] tablosuna yabancı anahtar oluşturuldu,
- satır 44: [SEQUENCE] tablosu oluşturuldu. Bu tablo, önceki üç tablonun birincil anahtarlarını oluşturmak için kullanılacak,
- satır 47: Bu tablo zaten mevcut olduğu için bir istisna oluştu,
- 51-53. satırlar: [SEQUENCE] tablosunun başlatılması.
Oluşturulan tabloların varlığı NetBeans'te [1]'te kontrol edilebilir:
![]() |
Dolayısıyla, aynı JPA varlıklarından yola çıkıldığında, JPA, Hibernate ve EclipseLink uygulamaları aynı tabloları oluşturmaz. Belgenin devamında, kullanılan JPA uygulaması:
- Hibernate ise, [dbpam_hibernate] veritabanı kullanılacak,
- EclipseLink ise [dbpam_eclipselink] veritabanı kullanılacaktır.
5.6.3. Yapılması gereken iş
Daha önce uygulanan adımları takip ederek,
- bir [mv-pam-jpa-hibernate-oracle] projesi oluşturun ve test edin; bu projede JPA Hibernate uygulaması ve SGBD Oracle uygulaması kullanılmalıdır,
- Hibernate JPA uygulaması ve SGBD SQL sunucusunu kullanan bir [mv-pam-jpa-hibernate-mssql] projesi oluşturun ve test edin,
- JPA ve EclipseLink uygulamalarını ve bir SGBD Oracle sunucusunu kullanarak bir [mv-pam-jpa-eclipselink-oracle] projesi oluşturmak ve test etmek,
- JPA ve EclipseLink uygulamalarını ve bir SGBD ile SQL sunucusunu kullanarak bir [mv-pam-jpa-eclipselink-mssql] projesi oluşturmak ve test etmek,
5.6.4. Tembel mi, Hevesli mi?
[Employe] varlığının olası bir tanımına geri dönelim:
package jpa;
...
@Entity
@Table(name="EMPLOYES")
public class Employe implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
@Column(name="VERSION",nullable=false)
private int version;
@Column(name="SS", nullable=false, unique=true, length=15)
private String SS;
@Column(name="NOM", nullable=false, length=30)
private String nom;
@Column(name="PRENOM", nullable=false, length=20)
private String prenom;
@Column(name="ADRESSE", nullable=false, length=50)
private String adresse;
@Column(name="VILLE", nullable=false, length=30)
private String ville;
@Column(name="CP", nullable=false, length=5)
private String codePostal;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="INDEMNITE_ID",nullable=false)
private Indemnite indemnite;
...
}
27-29. satırlar, [EMPLOYES] tablosundan [INDEMNITES] tablosuna giden yabancı anahtarı tanımlar. 27. satırdaki fetch özniteliği, 29. satırdaki indemnite alanının arama stratejisini tanımlar. İki mod vardır:
- FetchType.LAZY: Bir çalışan arandığında, ona ait tazminat getirilir. Bu tazminat, [Employe].indemnite alanı ilk kez referans alındığında getirilir.
- FetchType.EAGER: Bir çalışan arandığında, ona ait tazminat getirilir. Bu, herhangi bir mod belirtilmediğinde varsayılan moddur.
FetchType.LAZY seçeneğinin yararını anlamak için aşağıdaki örneği ele alabiliriz. [Details] bağlantısı içeren bir web sayfasında, tazminatları içermeyen bir çalışan listesi gösterilir. Bu bağlantıya tıklandığında, seçilen çalışanın tazminatları görüntülenir. Şu durum görülür:
- İlk sayfayı görüntülemek için çalışanların ek ödemeleriyle birlikte verilmesine gerek yoktur. Bu durumda FetchType.LAZY modu uygundur;
- ayrıntıları içeren ikinci sayfayı görüntülemek için, seçilen çalışanın ek ödemelerini almak üzere veritabanına ek bir sorgu gönderilmesi gerekir.
FetchType.LAZY modu, uygulamanın hemen ihtiyaç duymadığı fazla verinin getirilmesini önler. Bir örneğe bakalım.
[mv-pam-jpa-hibernate] projesi kopyalanır:
![]() |
- [1]'e kopyalanır;
- [2]'te kopyanın bulunduğu klasör belirtilir ve [3]'te adı belirtilir,
- [4]'te yeni proje eskisiyle aynı adı taşıyor. Bunu değiştiriyoruz:
![]() |
- [1] olarak değiştirip projeyi yeniden adlandırıyoruz,
- [2] olarak projenin adını değiştiriyoruz ve artifactId,
- yeni projeyi [3] olarak yeniden adlandırıyoruz.
[Main.java] programını şu şekilde değiştiriyoruz:
package main;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import jpa.Employe;
public class Main {
// Aşağıdaki JPQL sorgusu bir çalışanı döndürüyor
// [Employe].indemnite yabancı anahtarı FetchType.LAZY'tedir
public static void main(String[] args) {
// Entity Manager'ı oluşturmak, JPA katmanını oluşturmak için yeterlidir
EntityManagerFactory emf = Persistence.createEntityManagerFactory("pam-jpa-hibernatePU");
// ilk deneme
EntityManager em = emf.createEntityManager();
Employe employe = (Employe) em.createQuery("select e from Employe e where e.nom=:nom").setParameter("nom", "Jouveinal").getSingleResult();
em.close();
// çalışanı görüntülüyoruz
try {
System.out.println(employe);
} catch (Exception ex) {
System.out.println(ex);
}
// ikinci deneme
em = emf.createEntityManager();
employe = (Employe) em.createQuery("select e from Employe e left join fetch e.indemnite where e.nom=:nom").setParameter("nom", "Jouveinal").getSingleResult();
// kaynakları serbest bırak
em.close();
// çalışanı görüntüleme
try {
System.out.println(employe);
} catch (Exception ex) {
System.out.println(ex);
}
// kaynakların serbest bırakılması
emf.close();
}
}
- 15. satır: JPA katmanından EntityManagerFactory'i oluşturuyoruz,
- 17. satır: JPA katmanıyla iletişim kurmamızı sağlayan EntityManager'i elde ediyoruz,
- 18. satır: Jouveinal adlı çalışanı talep ediyoruz,
- 19. satır: EntityManager'i kapatıyoruz. Bu işlem, kalıcılık bağlamını kapatır.
- 22. satır: Alınan çalışanı görüntülüyoruz.
[Employe] sınıfı şu şekildedir:
package jpa;
...
@Entity
@Table(name="EMPLOYES")
public class Employe implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
@Column(name="VERSION",nullable=false)
private int version;
@Column(name="SS", nullable=false, unique=true, length=15)
private String SS;
@Column(name="NOM", nullable=false, length=30)
private String nom;
@Column(name="PRENOM", nullable=false, length=20)
private String prenom;
@Column(name="ADRESSE", nullable=false, length=50)
private String adresse;
@Column(name="VILLE", nullable=false, length=30)
private String ville;
@Column(name="CP", nullable=false, length=5)
private String codePostal;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="INDEMNITE_ID",nullable=false)
private Indemnite indemnite;
/**
* Returns a string representation of the object. This implementation constructs
* that representation based on the id fields.
* @return a string representation of the object.
*/
@Override
public String toString() {
return "jpa.Employe[id=" + getId()
+ ",version="+getVersion()
+",SS="+getSS()
+ ",nom="+getNom()
+ ",prenom="+getPrenom()
+ ",adresse="+getAdresse()
+",ville="+getVille()
+",code postal="+getCodePostal()
+",indice="+getIndemnite().getIndice()
+"]";
}
...
}
- 27. satır: indemnite alanı, LAZY moduna geri döndürülür,
- satır 47: indemnite alanı kullanılır. indemnite alanı henüz geri döndürülmemişken toString yöntemi çağrılırsa, o anda geri döndürülür. Örnekte olduğu gibi kalıcılık bağlamı kapatılmamışsa tabii.
[Main] koduna geri dönelim:
- 21-25. satırlar: burada bir istisna oluşması gerekir. Nitekim, toString yöntemi çağrılacaktır. Bu yöntem, indemnite alanını kullanacaktır. Bu alan aranacaktır. Kalıcılık bağlamı kapatıldığı için, geri getirilen [Employe] varlığı artık mevcut değildir; bu da istisnaya neden olur.
- 27. satır: yeni bir EntityManager oluşturulur,
- 28. satır: Jouveinal çalışanını, JPQL sorgusunda ilgili tazminatı açıkça belirterek talep ediyoruz. Bu açık talep gereklidir çünkü bu tazminatın arama modu LAZY'tir,
- 30. satır: EntityManager kapatılır,
- 32-36. satırlar: Çalışan yeniden görüntülenir. Herhangi bir istisna olmamalıdır.
Projeyi çalıştırmak için doldurulmuş bir veritabanına ihtiyaç vardır. Bu veritabanı, 5.5. paragrafındaki adımlar izlenerek oluşturulacaktır. Ayrıca, [persistence.xml] dosyası değiştirilmelidir:
<?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-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>jpa.Cotisation</class>
<class>jpa.Employe</class>
<class>jpa.Indemnite</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<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>
- Tabloları oluşturan seçenek kaldırılmıştır. Buradaki veritabanı zaten mevcuttur ve verilerle doldurulmuştur,
- Hibernate'in veritabanına gönderdiği SQL komutlarını günlüğe kaydeden seçenekler kaldırılmıştır.
Projenin çalıştırılması sonucunda konsolda aşağıdaki iki çıktı görüntülenir:
- 1. satır: oturum kapalıyken eksik olan tazminatın aranması gerektiğinde ortaya çıkan istisna. LAZY modu nedeniyle tazminatın getirilmediğini görüyoruz,
- 2. satır: LAZY modunu atlayan bir sorgu ile elde edilen tazminat bilgisine sahip çalışan.
5.6.5. Yapılması gereken iş
Az önce izlenen yaklaşıma benzer bir şekilde, [mv-pam-pa-eclipselink-lazy] adlı bir proje oluşturun; bu proje, EclipseLink'in LAZY moduna karşı nasıl davrandığını göstermelidir.
Aşağıdaki sonuçlar elde edilir:
LAZY modunda, her iki sorgu da tazminatı çalışanla birlikte geri getirmiştir. İnternette bu tutarsızlık hakkında araştırma yapıldığında, [FetchType.LAZY] notunun (1. satır):
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name="INDEMNITE_ID",nullable=false)
private Indemnite indemnite;
bir emir değil, bir istek olduğu ortaya çıkıyor. JPA uygulayıcısı bunu uygulamak zorunda değildir. Dolayısıyla, kodun bazen kullanılan JPA uygulamasına bağlı hale geldiği görülmektedir. EclipseLink'e, LAZY modu için beklenen davranışı yapılandırma yoluyla kazandırmak mümkündür.
5.6.6. Devamı için
Oluşturulacak uygulamanın mimarisi şu şekildedir:
![]() |
Belgenin devamında, [mv-pam-jpa-hibernate] adlı Maven projesini [mv-pam-spring-hibernate] ve [1, 2, 3] projelerine kopyalayacağız:
![]() |
- ardından yeni projenin adını [4, 5, 6] olarak değiştireceğiz.
Yeni projenin bağımlılıklarını değiştireceğiz. [pom.xml] dosyası şu şekilde olur:
<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-pam-spring-hibernate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-pam-spring-hibernate</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>swing-layout</id>
<layout>default</layout>
<name>Repository for library Library[swing-layout]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
- 25-31. satırlar: JUnit testleri için bağımlılık,
- 32-41. satırlar: Apache bağlantı havuzu DBCP'e ilişkin bağımlılıklar,
- 42-65. satırlar: Spring çerçevesine ilişkin bağımlılıklar,
- satır 67-71: JPA / Hibernate uygulaması için bağımlılıklar,
- 72-76. satırlar: MySQL için JDBC sürücüsüne ait bağımlılık,
- 77-81. satırlar: Swing arayüzü için bağımlılık. Bu bağımlılık, projeye bir Swing arayüzü eklendiğinde NetBeans tarafından otomatik olarak eklenir.
Ayrıca, MySQL adlı iki veritabanı oluşturulacaktır:
- [dbpam_hibernate], [dbpam_hibernate.sql] komut dosyasından,
- [dbpam_eclipselink], [dbpam_eclipselink.sql] komut dosyasından,
5.7. , [metier] ve [DAO] katmanlarının arayüzleridir
Uygulamanın mimarisine geri dönelim:
![]() |
Yukarıdaki mimaride, [DAO] katmanı [metier] katmanına hangi arayüzü sunmalıdır ve [metier] katmanı [ui] katmanına hangi arayüzü sunmalıdır? Farklı katmanların arayüzlerini tanımlamak için ilk yaklaşım, uygulamanın farklı kullanım senaryolarını (use cases) incelemektir. Burada, seçilen kullanıcı arayüzüne göre iki kullanım senaryosu vardır: konsol veya grafik form.
Konsol uygulamasının kullanım şeklini inceleyelim:
Uygulama, kullanıcıdan üç bilgi alır (yukarıdaki 1. satıra bakın)
- çocuk bakıcısının sosyal güvenlik numarası
- ay içinde çalışılan saat sayısı
- ay içinde çalışılan gün sayısı
Bu bilgiler ve yapılandırma dosyalarında kayıtlı diğer bilgilerden yola çıkarak, uygulama aşağıdaki bilgileri görüntüler:
- 4-6. satırlar: girilen değerler
- satır 8-10: sosyal güvenlik numarası girilen çalışanla ilgili bilgiler
- 12-14. satırlar: çeşitli sosyal güvenlik prim oranları
- 16-17. satırlar: çocuk bakıcısına ödenen çeşitli ödenekler
- satır 19-24: çocuk bakıcısının maaş bordrosundaki kalemler
[metier] katmanı tarafından [ui] katmanına bir dizi bilgi sağlanmalıdır:
- Sosyal güvenlik numarasıyla tanımlanan bir çocuk bakıcısına ilişkin bilgiler. Bu bilgiler [EMPLOYES] tablosunda bulunur. Bu sayede 6-8. satırlar görüntülenebilir.
- brüt maaştan kesilecek çeşitli sosyal güvenlik prim oranlarının tutarları. Bu bilgiler [COTISATIONS] tablosunda bulunur. Bu sayede 10-12. satırlar görüntülenebilir.
- çocuk bakıcılığı göreviyle ilgili çeşitli ödeneklerin tutarları. Bu bilgiler [INDEMNITES] tablosunda bulunur. Bu sayede 14-15. satırlar görüntülenebilir.
- Maaşın bileşenleri, 18-22. satırlarda görüntülenir.
Buna göre, [metier] katmanı tarafından [ui] katmanına sunulan [IMetier] arayüzüne ilişkin ilk kayıt şu şekilde belirlenebilir:
- 1. satır: [metier] katmanındaki öğeler, [metier] paketine aktarılır
- 5. satır: [ calculerFeuilleSalaire ] yöntemi, [ui] katmanı tarafından elde edilen üç bilgiyi parametre olarak alır ve [ui] katmanının konsolda görüntüleyeceği bilgileri içeren [FeuilleSalaire] türünde bir nesne döndürür. [FeuilleSalaire] sınıfı şu şekilde olabilir:
- 9. satır: maaş bordrosu ile ilgili çalışan - [ui] katmanı tarafından görüntülenen 1 numaralı bilgi
- 10. satır: çeşitli katkı payı oranları - [ui] katmanı tarafından görüntülenen 2 numaralı bilgi
- 11. satır: Çalışanın endeksine bağlı çeşitli ödenekler – [ui] katmanı tarafından görüntülenen 3 numaralı bilgi
- satır 12: maaşını oluşturan unsurlar - [ui] katmanı tarafından görüntülenen bilgi no. 4
[métier] katmanının ikinci bir kullanım örneği, grafik arayüzde ortaya çıkar:
![]() |
Yukarıda görüldüğü gibi, [1, 2] açılır listesi tüm çalışanları göstermektedir. Bu liste, [métier] katmanından talep edilmelidir. arayüzü bu durumda şu şekilde değişir:
- [10] satırı: [ui] katmanının, [métier] katmanından tüm çalışanların listesini talep etmesini sağlayacak yöntem.
[metier] katmanı, yukarıdaki [FeuilleSalaire] nesnesinin [Employe, Cotisation, Indemnite] alanlarını ancak [DAO] katmanını sorgulayarak başlatabilir; çünkü bu bilgiler veritabanı tablolarında yer almaktadır. Aynı durum, tüm çalışanların listesini almak için de geçerlidir. Üç [Employe, Cotisation, Indemnite] varlığına erişimi yöneten tek bir [DAO] arayüzü oluşturulabilir. Ancak burada, her varlık için ayrı bir [DAO] arayüzü oluşturmaya karar verdik.
[COTISATIONS] tablosundaki [Cotisation] varlıklarına erişim için [DAO] arayüzü şu şekilde olacaktır:
- 6. satırda, [ICotisationDao] arayüzü, [Cotisation] varlığına ve dolayısıyla veritabanındaki [COTISATIONS] tablosuna erişimi yönetir. Uygulamamızın ihtiyacı olan tek şey, 16. satırdaki [findAll] yöntemidir; bu yöntem, [COTISATIONS] tablosunun tüm içeriğini almayı sağlar. Burada, varlık üzerinde tüm CRUD işlemleri (Oluşturma, Okuma, Güncelleme, Silme) gerçekleştirilen daha genel bir durumu ele almak istedik.
- 8. satır: [create] yöntemi, yeni bir [Cotisation] varlığı oluşturur
- 10. satır: [edit] yöntemi, mevcut bir [Cotisation] varlığını değiştirir
- 12. satır: [destroy] yöntemi, mevcut bir [Cotisation] varlığını siler
- satır 14: [find] yöntemi, id tanımlayıcısı aracılığıyla mevcut bir [Cotisation] varlığını bulmaya olanak tanır
- 16. satır: [findAll] yöntemi, mevcut tüm [Cotisation] varlıklarını bir liste halinde döndürür
[create] yönteminin imzasına biraz daha yakından bakalım:
create yöntemi, Cotisation türünde bir cotisation parametresine sahiptir. cotisation parametresi kalıcı hale getirilmelidir; c.a.d burada [COTISATIONS] tablosuna kaydedilir. Bu kalıcı hale getirilme işleminden önce, cotisation parametresinin, değeri olmayan id kimliği vardır. Kalıcı hale getirilme işleminden sonra, id alanı, [COTISATIONS] tablosuna eklenen kaydın birincil anahtarı olan bir değere sahiptir. Dolayısıyla, cotisation parametresi, create yönteminin bir giriş/çıkış parametresidir. create yönteminin, cotisation parametresini de sonuç olarak döndürmesi gerekli görünmemektedir. Çağıran yöntem, [Cotisation cotisation] nesnesine bir referansa sahip olduğundan, bu nesne değiştirilirse, üzerinde bir referansı olduğu için değiştirilmiş nesneye erişebilir. Dolayısıyla, create yönteminin [Cotisation cotisation] nesnesinin id alanına atadığı değeri öğrenebilir. Bu nedenle yöntemin imzası daha basit bir şekilde şöyle olabilir:
Bir arayüz yazarken, bunun iki farklı bağlamda kullanılabileceğini hatırlamak önemlidir: local ve distant. local bağlamında, çağıran yöntem ve çağrılan yöntem aynı JVM içinde yürütülür:
![]() |
[metier] katmanı, [DAO] katmanındaki create yöntemini çağırıyorsa, yönteme aktardığı [Cotisation cotisation] parametresine bir referans içerir.
distant bağlamında, çağıran yöntem ve çağrılan yöntem farklı JVM'lerde yürütülür:
![]() |
Yukarıda, [metier] katmanı JVM 1'de, [DAO] katmanı ise JVM 2'de, iki farklı makinede yürütülür. Bu iki katman birbiriyle doğrudan iletişim kurmaz. Aralarında, [1] iletişim katmanı olarak adlandıracağımız bir katman yer alır. Bu katman, bir [2] gönderim katmanı ve bir [3] alım katmanından oluşur. Geliştiricinin genellikle bu iletişim katmanlarını yazması gerekmez. Bunlar yazılım araçları tarafından otomatik olarak oluşturulur. [metier] katmanı, sanki [DAO] katmanıyla aynı JVM içinde çalışıyormuş gibi yazılır. Dolayısıyla kodda herhangi bir değişiklik yapılmaz.
[metier] katmanı ile [DAO] katmanı arasındaki iletişim mekanizması şöyledir:
- [metier] katmanı, [DAO] katmanının create yöntemini çağırır ve ona [Cotisation cotisation1] parametresini aktarır
- bu parametre aslında [2] gönderim katmanına aktarılır. Bu katman, referansını değil, cotisation1 parametresinin değerini ağ üzerinden iletecektir. Bu değerin tam biçimi, kullanılan iletişim protokolüne bağlıdır.
- [3] alım katmanı bu değeri alacak ve bu değerden yola çıkarak, [metier] katmanı tarafından gönderilen başlangıç parametresinin bir kopyası olan [Cotisation cotisation2] nesnesini yeniden oluşturacaktır. Artık iki farklı JVM'te (içerik açısından) aynı iki nesneye sahibiz: cotisation1 ve cotisation2.
- Alıcı katman, cotisation2 nesnesini, [DAO] katmanındaki create yöntemine aktaracak ve bu yöntem nesneyi veritabanında kalıcı hale getirecektir. Bu işlemden sonra, cotisation2 nesnesinin id alanı, [COTISATIONS] tablosuna eklenen kaydın birincil anahtarıyla başlatılmıştır. Ancak, [metier] katmanının bir referansını barındıran cotisation1 nesnesi için durum böyle değildir. [metier] katmanının cotisation2 nesnesine bir referans vermesini istiyorsak, bu nesneyi ona göndermemiz gerekir. Bu nedenle, [DAO] katmanındaki create yönteminin imzasını şu şekilde değiştirmemiz gerekir:
- Bu yeni imzayla, create yöntemi, kalıcı nesne cotisation2'i sonuç olarak döndürecektir. Bu sonuç, [DAO] katmanını çağıran alıcı katman [3]'e iletilir. Bu katman, cotisation2 nesnesinin değerini (referansını değil) [2] gönderim katmanına döndürecektir.
- Gönderim katmanı [2] bu değeri alacak ve bu değerden yola çıkarak, [DAO] katmanındaki create yönteminin döndürdüğü sonucun bir kopyası olan [Cotisation cotisation3] nesnesini yeniden oluşturacaktır.
- [Cotisation cotisation3] nesnesi, tüm bu mekanizmayı [DAO] katmanındaki create yöntemine yapılan çağrı ile başlatan [metier] katmanındaki yönteme geri verilir. Böylece [metier] katmanı, kalıcılığını talep ettiği [Cotisation cotisation1] nesnesine verilen birincil anahtar değerini öğrenebilir: bu, cotisation3 nesnesinin id alanının değeridir.
Yukarıdaki mimari en yaygın olanı değildir. Aynı JVM içinde [metier] ve [DAO] katmanlarına daha sık rastlanır:
![]() |
Bu mimaride, sonuçları döndürmesi gerekenler [metier] katmanındaki yöntemlerdir, [DAO] katmanındakiler değil. Bununla birlikte, [DAO] katmanındaki create yönteminin imzası şöyledir:
sayesinde, fiilen kurulan mimari hakkında herhangi bir varsayımda bulunmamıza gerek kalmaz. Seçilen mimari ne olursa olsun (yerel veya uzak) çalışacak imzalar kullanmak, çağrılan bir yöntemin bazı parametrelerini değiştirmesi durumunda:
- bu parametrelerin de çağrılan yöntemin sonucunun bir parçası olması gerekir
- çağıran yöntem, çağrılan yönteme ilettiği değiştirilmiş parametre referanslarını değil, çağrılan yöntemin sonucunu kullanmalıdır.
Böylelikle, kodda herhangi bir değişiklik yapmadan locale mimarisinden distante mimarisine geçiş yapma imkânı elde ederiz. Bu bağlamda, [ICotisationDao] arayüzünü yeniden inceleyelim:
- 8. satır: create yönteminin durumu işlendi
- 10. satır: edit yöntemi, [Cotisation cotisation1] parametresini kullanarak, cotisation nesnesiyle aynı birincil anahtara sahip olan [COTISATIONS] tablosundaki kaydı günceller. Sonuç olarak, değiştirilen kaydın görüntüsü olan cotisation2 nesnesini döndürür. cotisation1 parametresi ise değiştirilmez. Yöntem, ister distante ister locale mimarisi kapsamında olsun, sonuç olarak cotisation2'i döndürmelidir.
- 12. satır: destroy yöntemi, parametre olarak geçirilen cotisation nesnesiyle aynı birincil anahtara sahip olan [COTISATIONS] tablosundaki kaydı siler. Bu kayıt değiştirilmez. Dolayısıyla, döndürülmesi gerekmez.
- 14. satır: find yönteminin id parametresi, yöntem tarafından değiştirilmez. Sonucun bir parçası olması gerekmez.
- 16. satır: findAll yönteminin parametresi yoktur. Dolayısıyla bu yöntem incelenmesine gerek yoktur.
Sonuç olarak, create yönteminin imzası, distante mimarisi kapsamında kullanılabilir hale getirilmek üzere uyarlanmalıdır. Yukarıdaki açıklamalar, diğer [DAO] arayüzleri için de geçerlidir. Bunları tekrar etmeyeceğiz ve hem distante hem de locale mimarisi kapsamında kullanılabilen imzaları doğrudan kullanacağız.
[INDEMNITES] tablosundaki [Indemnite] varlıklarına erişim için [DAO] arayüzü şu şekilde olacaktır:
- 6. satırda, [IIndemniteDao] arayüzü, [Indemnite] varlığına ve dolayısıyla veritabanındaki [INDEMNITES] tablosuna erişimi yönetir. Uygulamamızın, [INDEMNITES] tablosunun tüm içeriğini almayı sağlayan 16. satırdaki [findAll] yöntemine ihtiyacı vardır. Burada, varlık üzerinde tüm CRUD işlemleri (Oluşturma, Okuma, Güncelleme, Silme) gerçekleştirilen daha genel bir durumu ele almak istedik.
- 8. satır: [create] yöntemi, yeni bir [Indemnite] varlığı oluşturur
- 10. satır: [edit] yöntemi, mevcut bir [Indemnite] varlığını değiştirir
- 12. satır: [destroy] yöntemi, mevcut bir [Indemnite] varlığını siler
- satır 14: [find] yöntemi, id tanımlayıcısı aracılığıyla mevcut bir [Indemnite] varlığını bulmaya olanak tanır
- 16. satır: [findAll] yöntemi, mevcut tüm [Indemnite] varlıklarını bir liste halinde döndürür
[EMPLOYES] tablosundaki [Employe] varlıklarına erişim için [DAO] arayüzü şu şekilde olacaktır:
- 6. satırda, [IEmployeDao] arayüzü, [Employe] varlığına ve dolayısıyla veritabanındaki [EMPLOYES] tablosuna erişimi yönetir. Uygulamamızın ihtiyacı olan tek şey, 16. satırdaki [findAll] yöntemidir; bu yöntem, [EMPLOYES] tablosunun tüm içeriğini almayı sağlar. Burada, varlık üzerinde tüm CRUD işlemleri (Oluşturma, Okuma, Güncelleme, Silme) gerçekleştirilen daha genel bir durumu ele almak istedik.
- 8. satır: [create] yöntemi, yeni bir [Employe] varlığı oluşturur
- 10. satır: [edit] yöntemi, mevcut bir [Employe] varlığını değiştirir
- 12. satır: [destroy] yöntemi, mevcut bir [Employe] varlığını siler
- 14. satır: [find] yöntemi, id kimliği aracılığıyla mevcut bir [Employe] varlığını bulmaya olanak tanır
- 16. satır: [find(String SS)] yöntemi, [Employe] numaralı mevcut bir varlığı, SS numarası aracılığıyla bulmaya olanak tanır. Bu yöntemin konsol uygulaması için gerekli olduğunu görmüştük.
- 18. satır: [findAll] yöntemi, mevcut tüm [Employe] varlıklarını bir liste halinde döndürür. Bu yöntemin grafik uygulaması için gerekli olduğunu görmüştük.
5.8. [PamException] sınıfı
[DAO] katmanı, Java'daki API ve JDBC ile birlikte çalışacaktır. Bu API, [SQLException] türünde kontrollü istisnalar tetikler; bu istisnaların iki dezavantajı vardır:
- bu istisnaları zorunlu olarak try/catch ile yönetmek zorunda olan kodu ağırlaştırırlar.
- [IDao] arayüzünün yöntem imzalarında "throws SQLException" ifadesiyle bildirilmeleri gerekir. Bu durum, [SQLException] türünden farklı bir kontrollü istisna oluşturan sınıfların bu arayüzü uygulamasına engel olmaktadır.
Bu sorunu gidermek için, [DAO] katmanı yalnızca [PamException] türündeki kontrolsüz istisnaları "yukarıya iletecektir".
![]() |
- [JDBC] katmanı, [SQLException] türünde istisnalar atar
- [JPA] katmanı, kullanılan JPA uygulamasına özgü istisnalar oluşturur
- [DAO] katmanı, kontrol edilemeyen [PamException] türünde istisnalar oluşturur
Bunun iki sonucu vardır:
- [metier] katmanı, [DAO] katmanındaki istisnaları try/catch ile yönetmek zorunda kalmayacaktır. Bunları basitçe [ui] katmanına kadar iletebilir.
- [IDao] arayüzünün yöntemleri, imza alanlarına [PamException] istisnasının türünü eklemek zorunda değildir; bu da, bu arayüzü, başka bir tür kontrol edilemeyen istisna oluşturan sınıflarla uygulayabilme imkânı sağlar.
[PamException] sınıfı, NetBeans projesinin [exception] paketine yerleştirilecektir:
![]() |
Kodu şu şekildedir:
- 4. satır: [PamException], [RuntimeException]'ten türetilmiştir. Dolayısıyla bu, derleyicinin bizden try/catch ile yönetmemizi veya yöntem imzalarına eklememizi zorunlu kılmadığı bir istisna türüdür. Bu nedenle, [PamException], [IDao] arayüzünün yöntem imzalarında yer almamaktadır. Bu, söz konusu arayüzün, başka bir istisna türü atan bir sınıf tarafından uygulanmasına olanak tanır; tabii ki bu sınıfın da [RuntimeException]'ten türetilmiş olması şartıyla.
- Oluşabilecek hataları ayırt etmek için 7. satırdaki hata kodu kullanılır. 14., 19. ve 24. satırlardaki üç yapıcı, üst sınıf [RuntimeException]’e ait olanlardır ve bunlara bir parametre eklenmiştir: istisnaya atanacak hata kodu.
Uygulamanın istisnalar açısından işleyişi şu şekilde olacaktır:
- [DAO] katmanı, karşılaşılan tüm istisnaları [PamException] türünde bir istisna içinde kapsülleyecek ve bunu [métier] katmanına yeniden atacaktır.
- [métier] katmanı, [DAO] katmanı tarafından atılan istisnaların yukarıya iletilmesine izin verecektir. [métier] katmanı, [DAO] katmanında meydana gelen tüm istisnaları [PamException] türünde bir istisna olarak kapsayacak ve bunu [ui] katmanına yeniden iletecektir.
- [ui] katmanı, [métier] ve [DAO] katmanlarından gelen tüm istisnaları yakalar. Bu katman, istisnayı konsolda veya grafik kullanıcı arayüzünde görüntülemekle yetinir.
Şimdi sırasıyla [DAO] ve [metier] katmanlarının uygulamasını inceleyelim.
5.9. [PAM] uygulamasının [DAO] katmanı
Aşağıdaki mimari yapısı çerçevesinde ele alacağız:
![]() |
5.9.1. Uygulama
Önerilen okumalar: [ref1]'in 3.1.3 paragrafı
Soru: Spring / JPA entegrasyonunu kullanarak, [ICotisationDao, IIndemniteDao, IEmployeDao] arayüzlerini uygulayan [CotisationDao, IndemniteDao, EmployeDao] sınıflarını yazınız. Her sınıf yöntemi, olası bir istisnayı yakalayacak ve bunu, yakalanan istisnaya özgü bir hata kodu içeren [PamException] türünde bir istisna içine kapsülleyecektir.
Uygulama sınıfları, [dao] paketinin bir parçası olacaktır:
![]() |
5.9.2. Yapılandırma
Önerilen okuma: [ref1]'in 3.1.5 paragrafı
DAO / JPA entegrasyonu, Spring dosyası [spring-config-dao.xml] ve JPA [persistence.xml] dosyaları ile yapılandırılır:
![]() |
Soru: Bu iki dosyanın içeriğini yazınız. Kullanılan veritabanının, SQL [dbpam_hibernate.sql] komut dosyası tarafından oluşturulan MySQL5 [dbpam_hibernate] veritabanı olduğu varsayılacaktır. Spring dosyası, aşağıdaki üç bean'i tanımlayacaktır: employeDao (türü EmployeDao), indemniteDao (türü IndemniteDao), CotisationDao türündeki cotisationDao. Ayrıca, kullanılacak JPA uygulaması Hibernate olacaktır.
5.9.3. Testler
Önerilen okumalar: [ref1] belgesinin 3.1.6 ve 3.1.7 paragrafları
[DAO] katmanı yazıldı ve yapılandırıldı; artık bunu test edebiliriz. Test mimarisi şu şekilde olacaktır:
![]() |
5.9.4. InitDB
[DAO] katmanı için iki test programı oluşturacağız. Bu programlar, NetBeans projesinin [Test Packages] [1] dalındaki [dao] [2] paketine yerleştirilecektir. Bu dal, [Build project] seçeneği ile oluşturulan projeye dahil edilmez; bu sayede, buraya yerleştirdiğimiz test programlarının projenin nihai .jar dosyasına dahil edilmeyeceğinden emin oluruz.
![]() |
[Test Packages] dalına yerleştirilen sınıflar, [Source Packages] dalında bulunan sınıfların yanı sıra projenin sınıf kütüphanelerini de tanır. Testlerin projeye ait olanlar dışındaki kütüphanelere ihtiyacı varsa, bunlar [Test Libraries] ve [2] dallarında tanımlanmalıdır.
Test sınıfları, JUnit birim test aracını kullanır:
- [JUnitInitDB] herhangi bir test yapmaz. Veritabanını birkaç kayıtla doldurur ve ardından bunları konsolda görüntüler.
- [JUnitDao], bir dizi test gerçekleştirir ve sonuçlarını doğrular.
[JUnitInitDB] sınıfının iskeleti şu şekildedir:
- [init] yöntemi, test serisi başlamadan önce yürütülür (@BeforeClass anotasyonu). Bu yöntem, [DAO] katmanını örneklendirir.
- [clean] yöntemi her testten önce çalıştırılır (@Before etiketi). Bu yöntem veritabanını boşaltır.
- [initDB] yöntemi bir testtir (@Test anotasyonu). Tek test budur. Bir test, Assert.assertCondition onay komutlarını içermelidir. Burada ise hiçbiri olmayacaktır. Dolayısıyla bu yöntem sahte bir testtir. Amacı, veritabanını birkaç satırla doldurmak ve ardından veritabanının içeriğini konsolda görüntülemektir. Burada, [DAO] katmanına ait create ve findAll yöntemleri kullanılmaktadır.
Soru: [JUnitInitDB] sınıfının kodunu tamamlayınız. [ref1]'in 3.1.6 numaralı paragrafındaki örnekten yararlanınız. Kod, 5.1 numaralı paragrafta sunulan içeriği oluşturacaktır.
5.9.5. Uygulam : Testler
Artık [InitDB]'i çalıştırmaya hazırız. SGBD ve MySQL5 örneklerini kullanarak prosedürü açıklayacağız:
![]() |
- [1] sınıfları, [2] yapılandırma dosyaları ve [DAO] ile [3] katman test sınıfları kuruldu,
![]() |
- proje derlenir [4]
- [JUnitInitDB] sınıfı çalıştırılır ([5]). SGBD ve MySQL5, mevcut bir [dbpam_hibernate] veritabanıyla başlatılır,
- [Test Results] ve [6] pencereleri, testlerin başarılı olduğunu bildiriyor. Bu mesaj burada anlamlı değildir, çünkü [JUnitInitDB] programı, testin başarısız olmasına neden olabilecek herhangi bir Assert.assertCondition doğrulama talimatı içermez. Bununla birlikte, bu durum testin yürütülmesi sırasında herhangi bir istisna oluşmadığını gösterir.
[Output] penceresi, yürütme günlüklerini, Spring günlüklerini ve testin kendisinin günlüklerini içerir. [JUnitInitDB] sınıfı tarafından yapılan çıktılar şunlardır:
[EMPLOYES, INDEMNITES, COTISATIONS] tabloları doldurulmuştur. Bunu, NetBeans ile [dbpam_hibernate] veritabanına bağlanarak doğrulayabilirsiniz.
![]() |
- [1]'te, [services] sekmesinde, [dbpam_hibernate] [2] bağlantısının [employes] tablosundaki veriler görüntülenir,
- [3]'te ise sonuç görüntülenir.
5.9.6. JUnitDao
Şimdi ikinci bir test sınıfı olan [JUnitDao] ile ilgileniyoruz:
![]() |
Sınıfın iskeleti şu şekilde olacaktır:
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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
Önceki test sınıfında, her testten önce veritabanı boşaltılıyordu.
Soru: Aşağıdaki yöntemleri yazınız:
1 - test02: test01'den esinlenilecektir
2 - test03: Bir çalışanın Indemnite türünde bir alanı vardır. Bu nedenle, bir Indemnite varlığı ve bir Employe varlığı oluşturulmalıdır
3 - test04.
[JUnitInitDB] test sınıfında olduğu gibi aynı şekilde ilerlendiğinde, aşağıdaki sonuçlar elde edilir:
![]() |
- [1]'te, test sınıfı
- [2]'te, test sonuçları [Test Results] penceresinde görüntülenir
Bir hata oluşturarak bunun sonuç sayfasında nasıl bildirildiğini görelim:
- satırda, Csgrds değeri 3,49 olduğu için (8. satır) doğrulama işlemi bir hata verecektir. Test sınıfının çalıştırılması şu sonuçları verir:
![]() |
- [1] sonuç sayfası artık başarısız testler olduğunu gösteriyor.
- [2]'te, testin başarısız olmasına neden olan istisnanın bir özeti yer almaktadır. Burada, istisnanın meydana geldiği Java kod satırının numarası bulunur.
5.10. [PAM] uygulamasının [metier] katmanı
[DAO] katmanı yazıldığına göre, şimdi [2] iş katmanını incelemeye geçiyoruz:
![]() |
5.10.1. [IMetier] Java arayüzü
Bu arayüz 5.7. paragrafında açıklanmıştı. Aşağıda özetini veriyoruz:
[metier] katmanının uygulaması, [metier] paketinde gerçekleştirilecektir:
![]() |
[metier] paketi, [IMetier] arayüzü ve bunun [Metier] uygulaması dışında, [FeuilleSalaire] ve [ElementsSalaire] adlı iki sınıfı da içerecektir. [FeuilleSalaire] sınıfı, 5.7. paragrafta kısaca tanıtılmıştı. Şimdi bu konuya tekrar dönelim.
5.10.2. [FeuilleSalaire] sınıfı
[IMetier] arayüzündeki [calculerFeuilleSalaire] yöntemi, bir maaş bordrosundaki çeşitli öğeleri temsil eden [FeuilleSalaire] türünde bir nesne döndürür. Tanımı şöyledir:
- 7. satır: Sınıf, örnekleri ağ üzerinden aktarılabileceğinden Serializable arayüzünü uygular.
- 9. satır: maaş bordrosunda adı geçen çalışan
- 10. satır: çeşitli prim oranları
- 11. satır: Çalışanın endeksine bağlı çeşitli ödenekler
- 12. satır: maaşını oluşturan unsurlar
- satır 14-22: sınıfın iki oluşturucusu
- satır 25-27: belirli bir [FeuilleSalaire] nesnesini tanımlayan [toString] yöntemi
- 29. satır ve sonrası: Sınıfın özel alanlarına erişim sağlayan kamu erişimcileri
Yukarıdaki [FeuilleSalaire] sınıfının 11. satırında referans verilen [ElementsSalaire] sınıfı, bir maaş bordrosunu oluşturan unsurları bir araya getirir. Tanımı şöyledir:
- 3. satır: Sınıf, serileştirilebilir olması gereken FeuilleSalaire sınıfının bir bileşeni olduğu için Serializable arayüzünü uygular.
- 6. satır: temel maaş
- 7. satır: bu temel maaş üzerinden ödenen sosyal güvenlik primleri
- 8. satır: Çocuk bakım günlük ödenekleri
- 9. satır: Çocuğun günlük yemek yardımı
- 10. satır: çocuk bakıcısına ödenecek net maaş
- satır 12-24: sınıf oluşturucular
- satır 27-31: belirli bir [ElementsSalaire] nesnesini tanımlayan [toString] yöntemi
- satır 34 ve sonrası: sınıfın özel alanlarına erişim sağlayan genel erişim yöntemleri
5.10.3. [metier] katmanının [Metier] uygulama sınıfı
[metier] katmanının [Metier] uygulama sınıfı şu şekilde olabilir:
- 5. satır: Spring @Transactional anotasyonu, sınıfın her yönteminin bir işlem içinde gerçekleştirilmesini sağlar.
- 9-10. satırlar: [Cotisation, Employe, Indemnite] varlıklarının [DAO] katmanlarına yapılan referanslar
- 14-17. satırlar: [calculerFeuilleSalaire] yöntemi
- 20-22. satırlar: [findAllEmployes] yöntemi
- 24. satır ve sonrası: Sınıfın özel alanlarına ait genel erişim yöntemleri
Soru: [findAllEmployes] yönteminin kodunu yazınız.
Soru: [calculerFeuilleSalaire] yönteminin kodunu yazınız.
Aşağıdaki noktalara dikkat edilmelidir:
- Maaş hesaplama yöntemi 5.2. paragrafında açıklanmıştır.
- [SS] parametresi hiçbir çalışana karşılık gelmiyorsa ([DAO] katmanı bir null işaretçisi döndürdüyse), yöntem, uygun bir hata koduyla birlikte [PamException] türünde bir istisna oluşturacaktır.
5.10.4. [metier] katmanının testleri
İki test programı oluşturuyoruz:
![]() |
[3] test sınıfları, projenin [Test Packages] [1] dalındaki [metier] [2] paketinde oluşturulur.
[JUnitMetier_1] sınıfı şu şekilde olabilir:
Sınıfta Assert.assertCondition adlı bir onaylama yoktur. Amacımız, maaşları hesaplayıp daha sonra manuel olarak doğrulamaktır. Önceki sınıfın çalıştırılmasıyla elde edilen ekran görüntüsü şöyledir:
- 4. satır: Justine Laverti'nin maaş bordrosu
- 5. satır: Marie Jouveinal'ın maaş bordrosu
- 6. satır: SS numaralı 'xx' çalışanı bulunmadığı için oluşan istisna.
Soru: [JUnitMetier_1]'in 17. satırı, metier adlı Spring bean'ini kullanmaktadır. Bu bean'in [spring-config-metier-dao.xml] dosyasındaki tanımını veriniz.
[JUnitMetier_2] sınıfı şu şekilde olabilir:
[JUnitMetier_2] sınıfı, [JUnitMetier_1] sınıfının bir kopyasıdır; ancak bu sefer test01 yöntemine doğrulama ifadeleri eklenmiştir.
Soru: test01 yöntemini yazınız.
[JUnitMetier_2] sınıfı çalıştırıldığında, her şey yolunda giderse aşağıdaki sonuçlar elde edilir:

5.11. [PAM] uygulamasının [ui] katmanı – sürüm konsol
[metier] katmanı yazıldığına göre, geriye [ui] ve [1] katmanlarını yazmak kalıyor:
![]() |
[ui] katmanının iki farklı uygulamasını oluşturacağız: bir console sürümü ve bir grafik sürümü olan swing:
![]() |
5.11.1. [ui.console.Main] sınıfı
Öncelikle, yukarıdaki [ui.console.Main] sınıfı tarafından uygulanan konsol uygulamasına odaklanacağız. Bu uygulamanın işleyişi 5.3. paragrafında açıklanmıştır. [Main] sınıfının iskeleti şu şekilde olabilir:
Soru: Yukarıdaki kodu tamamlayınız.
5.11.2. Çalıştırma
[ui.console.Main] sınıfını çalıştırmak için aşağıdaki adımlar izlenmelidir:
![]() |
- [1]'te proje özelliklerini seçin,
- [2]'te, projenin [Run] özelliğini seçin,
- [3] düğmesini kullanarak çalıştırılacak sınıfı (ana sınıf olarak adlandırılır) belirleyin,
- [4] sınıfını seçin,
- sınıf [5]'te görünür. Bu sınıfın çalışması için üç argümana ihtiyaç vardır (SS numarası, çalışılan saat sayısı, çalışılan gün sayısı). Bu argümanlar [6]'e yerleştirilir,
- bu işlem tamamlandıktan sonra [7] projesini çalıştırabiliriz. Önceki yapılandırma nedeniyle [ui.console.Main] sınıfı çalıştırılacaktır.
Çalıştırma sonuçları [output] penceresinde görüntülenir:
![]() | ![]() |
5.12. [PAM] uygulamasının [ui] katmanı – grafik sürümü
Şimdi [ui] katmanını bir grafik arayüzle uyguluyoruz:
![]() |
![]() |
- [1]'te, grafik arayüzün [PamJFrame] sınıfı
- [2]: grafik kullanıcı arayüzü
5.12.1. Hızlı bir eğitim
Grafik kullanıcı arayüzünü oluşturmak için şu adımları izleyebilirsiniz:
![]() |
- [1]: [1] düğmesiyle yeni bir dosya oluşturulur [New File...]
- [2]: [Swing GUI Forms], c.a.d dosya kategorisini seçin. Grafik formlar
- [3]: Tür seçilir [JFrame Form], boş bir form türü
![]() |
- [5]: Forma, aynı zamanda bir sınıf olacak şekilde bir ad verilir
- [6]: Form bir pakete yerleştirilir
- [8]: Form, proje ağacına eklenir
- [9]: Forma iki farklı bakış açısı üzerinden erişilebilir: [Design] ve [9] (formun çeşitli bileşenlerini tasarlamaya olanak sağlar), [Source] ve [10 ci-dessous] (formun Java koduna erişim sağlar). Sonuç olarak, bir form da diğerleri gibi bir Java sınıfıdır. [Design] görünümü, formu tasarlamayı kolaylaştırır. [Design] modunda her bileşen eklendiğinde, bunu hesaba katmak için [Source] görünümüne Java kodu eklenir.
![]() |
- [11]: Bir form için kullanılabilir Swing bileşenlerinin listesi, [Palette] penceresinde bulunur.
- [12]: [Inspector] penceresi, form bileşenlerinin ağaç yapısını gösterir. Görsel bir temsili olan bileşenler [JFrame] dalında, diğerleri ise [Other Components] dalında bulunur.
![]() |
- [13]'te, tek bir tıklamayla [JLabel] bileşenini seçiyoruz
- [14]'te, onu [Design] modunda forma bırakıyoruz
- [15]'te, JLabel'in özelliklerini (metin, yazı tipi) tanımlıyoruz.
![]() |
- [16]'te elde edilen sonuç.
- [17]'te, formun önizlemesi isteniyor
- [18]'te, sonuç
- [19]'te, [JLabel1] etiketi, [Inspector] penceresindeki bileşen ağacına eklendi
![]() |
- [20] ve [21]: Formun [Source] görünümüne, eklenen JLabel'i yönetmek için Java kodu eklendi.
NetBeans ile form oluşturmaya ilişkin bir eğitim kılavuzu [http://www.netbeans.org/kb/trails/matisse.html] adresinde mevcuttur.
5.12.2. [PamJFrame] grafik kullanıcı arayüzü
Aşağıdaki grafik kullanıcı arayüzünü oluşturacağız:
![]() |
- [1]'te, grafik arayüz
- [2]'te, bileşenlerinin ağaç yapısı: bir JLabel ve altı JPanel konteyneri
JLabel1
![]() |
JPanel1
![]() | ![]() |
JPanel2
![]() | ![]() |
JPanel3
![]() | ![]() |
JPanel4
![]() | ![]() |
JPanel5
![]() | ![]() |
Uygulama ödevi: [http://www.netbeans.org/kb/trails/matisse.html] öğreticisini kullanarak önceki grafik kullanıcı arayüzünü oluşturun.
5.12.3. Grafik kullanıcı arayüzündeki olaylar
Önerilen okumalar: [ref2] kitabının [Interfaces graphiques] bölümü.
[jButtonSalaire] düğmesine yapılan tıklamayı işleyeceğiz. Bu olayı işleyen yöntemi oluşturmak için şu adımları izleyebiliriz:
![]() |
[JButtonSalaire] düğmesine tıklama işleyicisi oluşturulur:
Önceki yöntemi [JButtonSalaire] düğmesine tıklamaya bağlayan Java kodu da oluşturulur:
2-5. satırlar, [jButtonSalaire] düğmesine (2. satır) yapılan tıklamanın (muhtemelen ActionPerformed türünde) [jButtonSalaireActionPerformed] yöntemi (4. satır) tarafından işlenmesi gerektiğini belirtir.
Ayrıca, [jTextFieldHT] giriş alanında gerçekleşen [caretUpdate] olayını (giriş imlecinin yer değiştirmesi) da işleyeceğiz. Bu olayın işleyicisini oluşturmak için, daha önce yaptığımız gibi ilerleyeceğiz:
![]() |
[jTextFieldHT] giriş alanında [caretUpdate] olayının işleyicisi oluşturulur:
[jTextFieldHT] giriş alanındaki [caretUpdate] olayını önceki yönteme bağlayan Java kodu da oluşturulmuştur:
1-4. satırlar, [jTextFieldHT] düğmesi (1. satır) üzerindeki [caretUpdate] olayı (2. satır) [ jTextFieldHTCaretUpdate] yöntemi (3. satır) tarafından işlenmelidir.
5.12.4. Grafik kullanıcı arayüzünün başlatılması
Uygulamamızın mimarisine geri dönelim:
![]() |
[ui] katmanı, [metier] katmanına bir referansa ihtiyaç duyar. Bu referansın console uygulamasında nasıl elde edildiğini hatırlayalım:
Grafik uygulamasında da yöntem aynıdır. Uygulama başlatıldığında, yukarıdaki 3. satırdaki [IMetier metier] referansının da başlatılması gerekir. Grafik arayüz için şu anda oluşturulan kod şöyledir:
- 29-35. satırlar: Uygulamayı başlatan statik [main] yöntemi
- 32. satır: [PamJFrame] grafik arayüzünün bir örneği oluşturulur ve görünür hale getirilir.
- 7-9. satırlar: Grafik kullanıcı arayüzünün oluşturucusu.
- 8. satır: 17. satırda tanımlanan [initComponents] yöntemine çağrı yapılır. Bu yöntem, [Design] modunda yapılan çalışmadan otomatik olarak oluşturulmuştur. Bu yönteme dokunulmamalıdır.
- 21. satır: [jTextFieldHT] alanında giriş imlecinin hareketini yönetecek yöntem
- 25. satır: [jButtonSalaire] düğmesine tıklamayı yönetecek yöntem
Önceki koda kendi başlatma kodlarımızı eklemek için şu şekilde ilerleyebiliriz:
- 4. satır: Kendi başlangıç ayarlamalarımızı yapmak için özel bir yöntem çağrılır. Bu ayarlamalar 10-42. satırlardaki kodda tanımlanmıştır
Soru: Yorumlardan yararlanarak, [doMyInit] prosedürünün kodunu tamamlayınız.
5.12.5. Olay Yöneticileri
Soru: [jTextFieldHTCaretUpdate] yöntemini yazınız. Bu yöntem, [jTextFieldHT] alanında bulunan verinin >=0 olan bir gerçek sayı olmaması durumunda, [jButtonSalaire] düğmesinin devre dışı kalmasını sağlamalıdır.
Soru: [jComboBoxEmployes]'te seçilen çalışanın maaş bordrosunu görüntüleyen [jButtonSalaireActionPerformed] yöntemini yazınız.
5.12.6. Grafik kullanıcı arayüzünün çalıştırılması
Grafik kullanıcı arayüzünü çalıştırmak için, projenin [Run] yapılandırmasını değiştireceğiz:
![]() |
- [1] olarak değiştirin ve grafik arayüz sınıfını ekleyin
Proje, yapılandırma dosyaları (persistence.xml, spring-config-metier-dao.xml) ve grafik kullanıcı arayüzünün sınıfı ile birlikte eksiksiz olmalıdır. Projeyi çalıştırmadan önce SGBD hedefini başlatın.
5.13. JPA katmanının EclipseLink ile uygulanması
JPA katmanının artık EclipseLink tarafından uygulandığı aşağıdaki mimariyle ilgileniyoruz:
![]() |
5.13.1. NetBeans projesi
Yeni NetBeans projesi, önceki projenin kopyalanmasıyla elde edilir:
![]() |
- [1]'e: Hibernate projesine sağ tıklayın ve Copy
- [2] düğmesini kullanarak yeni projenin üst klasörünü seçin. Klasörün adı [3] olarak görünür.
- [4]'te, yeni projeye bir ad verin
- [5]'te, proje klasörünün adını
![]() |
- [1]'te yeni proje oluşturulmuştur. Proje, orijinal projeyle aynı adı taşımaktadır,
- [2] ve [3]'te, projenin adı [mv-pam-spring-eclipselink] olarak değiştirilir.
Proje, yeni katman JPA / EclipseLink'e uyarlanabilmesi için iki noktada değiştirilmelidir:
- [4]'te, Spring yapılandırma dosyaları değiştirilmelidir. Bu dosyalarda aslında JPA katmanının yapılandırması bulunmaktadır.
- [5]'te, projenin kütüphaneleri değiştirilmelidir: Hibernate kütüphaneleri, EclipseLink'teki kütüphanelerle değiştirilmelidir.
Sonuncusuyla başlayalım. Yeni proje için [pom.xml] dosyası şu şekilde olacaktır:
<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-pam-spring-eclipselink</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-pam-spring-eclipselink</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>swing-layout</id>
<layout>default</layout>
<name>Repository for library Library[swing-layout]</name>
</repository>
<repository>
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library Library[eclipselink]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
- 73-82. satırlar: JPA ve EclipseLink uygulamaları için bağımlılıklar,
- 19-24. satırlar: EclipseLink için Maven deposu.
Spring yapılandırma dosyaları, JPA uygulamasının değiştiğini belirtmek üzere güncellenmelidir. Her iki dosyada da yalnızca JPA katmanını yapılandıran bölüm değişir. Örneğin, [spring-config-metier-dao.xml] dosyasında şunlar yer alır:
19-36. satırlar, JPA katmanını yapılandırmaktadır. Kullanılan JPA uygulaması Hibernate'dir (22. satır). Ayrıca, hedef veritabanı [dbpam_hibernate]'tir (41. satır).
JPA / EclipseLink uygulamasına geçmek için, yukarıdaki 19-35. satırlar aşağıdaki satırlarla değiştirilir:
- 5. satır: Kullanılan JPA uygulaması, EclipseLink'tir
- 9. satır: databasePlatform özelliği, hedef SGBD'i belirler; burada MySQL
- 11. satır: JPA katmanı örneklendiğinde veritabanı tablolarını oluşturmak için. Burada özellik yorum satırında yer almaktadır.
- 7. satır: JPA katmanı tarafından gönderilen SQL komutlarını konsolda görüntülemek için. Burada, özellik yorum olarak işaretlenmiştir.
Ayrıca, hedef veritabanı [dbpam_eclipselink] olur (aşağıdaki 4. satır):
5.13.2. Testlerin Uygulanması
Uygulamanın tamamını test etmeden önce, JUnit testlerinin yeni JPA uygulamasıyla başarılı olup olmadığını kontrol etmek iyi bir fikirdir. Testlere başlamadan önce, veritabanındaki tabloları sileceğiz. Bunun için NetBeans'in [Runtime] sekmesinde, gerekirse dbpam_eclipselink / MySQL5 veritabanına bir bağlantı oluşturacağız. dbpam_eclipselink / MySQL5 veritabanına bağlandıktan sonra, aşağıda gösterildiği gibi tabloları silebiliriz:
- [1]: silme işleminden önce
- [2]: silme işleminden sonra
![]() |
Bu işlem tamamlandıktan sonra, veritabanını dolduran [DAO]: InitDB katmanında ilk testi gerçekleştirebiliriz. Daha önce silinen tabloların uygulama tarafından yeniden oluşturulabilmesi için, Spring’in JPA / EclipseLink yapılandırmasında şu satırın bulunduğundan emin olunmalıdır:
satırının mevcut olduğundan ve yorum satırına alınmadığından emin olunmalıdır.
Projeyi derliyoruz (Build) ve ardından [JUnitInitDB] testini çalıştırıyoruz:
![]() |
- [1]'te, InitDB testi çalıştırılır.
- [2]'te ise test başarısız olur. İstisna, başarısız olan bir test tarafından değil, Spring tarafından tetiklenir.
Nedeni: org.springframework.beans.factory.BeanCreationException: Sınıf yolu kaynağında tanımlanan 'entityManagerFactory' adlı bean oluşturulurken hata oluştu [spring-config-DAO.xml]: init yönteminin çağrılması başarısız oldu; iç içe geçmiş istisna java.lang.IllegalStateException: InstrumentationLoadTimeWeaver'i kullanmak için Java ajanı ile başlatılmalıdır. Spring belgelerine bakınız.
Spring, bir yapılandırma sorunu olduğunu belirtiyor. Mesaj net değil. İstisnanın nedeni, [ref1]'in 3.1.9 numaralı paragrafında açıklanmıştır. Spring / EclipseLink yapılandırmasının çalışması için, uygulamayı çalıştıran JVM'in belirli bir parametre, yani bir Java ajanı ile başlatılması gerekir. Bu parametrenin biçimi şöyledir:
[spring-agent.jar], JVM'in Spring / EclipseLink yapılandırmasını yönetmek için ihtiyaç duyduğu Java ajanıdır.
Bir proje çalıştırıldığında, JVM'e argümanlar aktarılabilir:
![]() |
- [1]'te, projenin özelliklerine erişilir
- [2] ile Run'in özelliklerine erişilir
- [3]'te, -javaagent parametresini JVM'e aktarıyoruz
5.13.3. InitDB
Şimdi, [InitDB]'i yeniden test etmeye hazırız. Bu sefer elde edilen sonuçlar şunlardır:
![]() |
- [1]'te test başarılı oldu
- [2]'te, [Services] sekmesinde, NetBeans'in veritabanıyla olan bağlantısı yenileniyor [dbpam_eclipselink]
- [3]'te dört tablo oluşturuldu
![]() |
- [5]'te, [employes] tablosunun içeriği görüntülenir
- [6]'te sonuç görüntülenir.
5.13.4. JUnitDao
[JUnitDao] test sınıfının çalıştırılması, JPA / Hibernate uygulamasıyla başarılı olmuş olsa bile başarısız olabilir. Nedenini anlamak için bir örneği inceleyelim.
Test edilen yöntem, aşağıdaki IndemniteDao.create yöntemidir:
- 15-22. satırlar: test edilen yöntem
Test yöntemi şöyledir:
package dao;
...
public class JUnitDao {
// katmanlar DAO
static private IEmployeDao employeDao;
static private IIndemniteDao indemniteDao;
static private ICotisationDao cotisationDao;
@BeforeClass
public static void init() {
// günlük
log("init");
// uygulama yapılandırması
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-DAO.xml");
// katmanlar DAO
employeDao = (IEmployeDao) ctx.getBean("employeDao");
indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao");
cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao");
}
@Before()
public void clean() {
// veritabanını boşaltma
for (Employe employe : employeDao.findAll()) {
employeDao.destroy(employe);
}
for (Cotisation cotisation : cotisationDao.findAll()) {
cotisationDao.destroy(cotisation);
}
for (Indemnite indemnite : indemniteDao.findAll()) {
indemniteDao.destroy(indemnite);
}
}
// günlükler
private static void log(String message) {
System.out.println("----------- " + message);
}
// testler
….
@Test
public void test05() {
log("test05");
// aynı endeksle iki tazminat oluşturuluyor
// endeksin teklik kuralını ihlal ediyor
boolean erreur = true;
Indemnite indemnite1 = null;
Indemnite indemnite2 = null;
Throwable th = null;
try {
indemnite1 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12));
indemnite2 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12));
erreur = false;
} catch (PamException ex) {
th = ex;
// denetimler
Assert.assertEquals(31, ex.getCode());
} catch (Throwable th1) {
th = th1;
}
// kontroller
Assert.assertTrue(erreur);
// istisna dizisi
System.out.println("Chaîne des exceptions --------------------------------------");
System.out.println(th.getClass().getName());
while (th.getCause() != null) {
th = th.getCause();
System.out.println(th.getClass().getName());
}
// 1. tazminatın kalıcı hale getirilmesi gerekiyordu
Indemnite indemnite = indemniteDao.find(indemnite1.getId());
// doğrulama
Assert.assertNotNull(indemnite);
Assert.assertEquals(1, indemnite.getIndice());
Assert.assertEquals(1.93, indemnite.getBaseHeure(), 1e-6);
Assert.assertEquals(2, indemnite.getEntretienJour(), 1e-6);
Assert.assertEquals(3, indemnite.getRepasJour(), 1e-6);
Assert.assertEquals(12, indemnite.getIndemnitesCP(), 1e-6);
// ikinci tazminat kalıcı hale getirilmemeliydi
List<Indemnite> indemnites = indemniteDao.findAll();
int nbIndemnites = indemnites.size();
Assert.assertEquals(nbIndemnites, 1);
}
...
}
Soru: test05 testinin ne yaptığını açıklayın ve beklenen sonuçları belirtin.
JPA / Hibernate katmanıyla elde edilen sonuçlar şunlardır:
Test başarılı oldu, c.a.d. Doğrulamalar kontrol edildi ve test yönteminden herhangi bir istisna çıkmadı.
Soru: Neler olduğunu açıklayın.
JPA / EclipseLink katmanıyla elde edilen sonuçlar şunlardır:
Daha önce Hibernate ile olduğu gibi, test başarılıdır (c.a.d). Doğrulamalar gerçekleştirilmiştir ve test yönteminden herhangi bir istisna çıkmamıştır.
Soru: Neler olduğunu açıklayın.
Soru: Bu iki örnekten, JPA uygulamalarının birbirinin yerine kullanılabilirliği konusunda ne gibi bir sonuç çıkarılabilir? Burada tam bir değiştirilebilirlik söz konusu mu?
5.13.5. Diğer testler
[DAO] katmanı test edilip doğru kabul edildikten sonra, [metier] katmanının testlerine ve projenin konsol veya grafik sürümündeki testlere geçilebilir. JPA uygulamasındaki değişiklik, [metier] ve [ui] katmanlarını hiçbir şekilde etkilemez; dolayısıyla bu katmanlar Hibernate ile çalışıyorsa, EclipseLink ile de çalışacaktır; ancak birkaç istisna dışında: önceki örnek, [DAO] katmanları tarafından atılan istisnaların farklı olabileceğini göstermektedir. Dolayısıyla, testin kullanıldığı durumda, Spring / JPA / Hibernate, [PamException] türünde bir istisna atar; bu, [pam] uygulamasına özgü bir istisnadır; oysa Spring / JPA / EclipseLink ise Spring çerçevesine ait bir istisna olan [TransactionSystemException] türünde bir istisna tetikler. Test kullanım senaryosunda, [ui] katmanı, Hibernate ile oluşturulduğu için [PamException] türünde bir istisna bekliyorsa, EclipseLink'e geçildiğinde artık çalışmayacaktır.
5.13.6. Yapılması gerekenler
Uygulamalı çalışma: console ve swing uygulamalarının testlerini farklı SGBD değerleriyle yeniden yapın: MySQL5, Oracle XE, SQL Sunucusu.





















































































