18. Vaka çalışması: Struts 2 / Tiles / Spring / Hibernate / MySQL
Struts öğrenimimizi bir vaka çalışmasıyla sonlandıracağız. Gerçekçi olması açısından, inceleyeceğimiz örnek daha önce incelenenlerden çok daha karmaşık olacaktır. Yeni başlayanlar için, bu vaka çalışmasına geçmeden önce kişisel uygulamalarla Struts 2'nin temellerini derinlemesine incelemek şüphesiz daha uygun olacaktır.
Uygulama, katmanlı bir mimari kullanacaktır:
![]() |
[metier], [dao] ve [jpa/hibernate] katmanlarının tümü, işlevlerini ayrıntılı olarak ele alacağımız bir jar arşivi şeklinde bize sağlanacaktır. Katmanların entegrasyonu Spring tarafından sağlanacaktır. [web] katmanı Struts 2 ile uygulanacaktır.
![]() |
18.1. Sorun
Bir belediyenin "Erken Çocukluk Merkezi"nde çalışan çocuk bakıcılarının maaş bordrolarını hazırlamaya olanak tanıyan bir web uygulaması yazmayı planlıyoruz.

Bu vaka çalışması şu belgede sunulmaktadır:
Java'ya Giriş EE 5, URL [http://tahe.developpez.com/java/javaee] adresinde mevcuttur
Bu belgede, vaka çalışması aşağıdaki çok katmanlı mimari ile uygulanmıştır:
![]() |
[web] katmanı, JSF (Java Server Faces) çerçeve yapısı kullanılarak uygulanmıştır. Struts 2 ile [web] katmanını uygulayarak aynı mimariyi kullanacağız. Katmanlı mimarilerin faydalarını göstermek için, JSF sürümüne ait [metier, dao, jpa] katmanlarının jar dosyasını kullanacak ve bunu bir [web / struts2] katmanına bağlayacağız:
![]() |
[metier, dao, jpa] katmanlarının aşağıdaki öğelerini tanıtacağız:
- [web] katmanı, [métier] katmanının arayüzüne yöneliktir. Bu arayüzü tanıtacağız.
- [jpa] katmanı bir veritabanına erişir. Bunu tanıtacağız.
- [jpa] katmanı, veritabanı tablolarındaki satırları, uygulamanın tüm katmanları tarafından işlenen JPA varlıklarına dönüştürür. Bunları tanıtacağız.
- [metier, dao, jpa] katmanları Spring tarafından örneklenir. Bu örneklendirmeyi ve entegrasyonu gerçekleştiren yapılandırma dosyasını tanıtacağız.
18.2. Veritabanı
Aşağıdaki MySQL [dbpam_hibernate] veritabanını kullanacağız:

- [1]'te, veritabanında üç tablo bulunmaktadır:
- [employes]: Bir kreşin çalışanlarını kaydeden tablo
- [cotisations]: sosyal güvenlik prim oranlarını kaydeden bir tablo
- [indemnites]: Çalışanların maaş hesaplamasına yönelik bilgileri kaydeden bir tablo
[employes] tablosu
![]() |
- [2], çalışan tablosu; [3] ise bu tablonun alanlarının açıklamalarıdır
Tablonun içeriği şu şekilde olabilir:
[cotisations] tablosu
![]() |
- [4]'te prim tablosu ve [5]'te bu tablonun alanlarının açıklamaları
Tablonun içeriği şu şekilde olabilir:
[indemnites] tablosu
![]() |
- [6]'te tazminat tablosu ve [7]'te bu tablonun alanlarının açıklamaları
Tablonun içeriği şu şekilde olabilir:
Veritabanı yapısının SQL dosyasına aktarılması sonucunda şu sonuç elde edilir:
18.3. JPA varlıkları
Aşağıdaki mimaride
![]() |
[Jpa] katmanı, [dao] katmanı tarafından işlenen nesneler ile Jdbc sürücüsü tarafından işlenen veritabanı tablolarındaki satırlar arasında köprü görevi görür. Veritabanından okunan tablo satırları, JPA varlıkları olarak adlandırılan nesnelere dönüştürülür. Tersine, yazma işleminde JPA varlıkları tablo satırlarına dönüştürülür. Bu varlıklar tüm katmanlar tarafından, özellikle de web katmanı tarafından işlenir. Bu nedenle bunları bilmemiz gerekir:
[Employe] varlığı, [Employes] tablosundaki bir satırı temsil eder
![]() |
|
[Employe] varlığı şöyledir:
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
@JoinColumn(name="INDEMNITE_ID",nullable=false)
private Indemnite indemnite;
public Employe() {
}
public Employe(String SS, String nom, String prenom, String adresse, String ville, String codePostal, Indemnite indemnite){
setSS(SS);
setNom(nom);
setPrenom(prenom);
setAdresse(adresse);
setVille(ville);
setCodePostal(codePostal);
setIndemnite(indemnite);
}
@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()
+"]";
}
// alıcı ve ayarlayıcılar
...
}
[Jpa] katmanına yönelik @ ek açıklamaları göz ardı edilecektir. Sınıfın çeşitli alanları, [EMPLOYES] tablosunun çeşitli sütunlarını yansıtmaktadır. indemnites alanı (29. satır), [EMPLOYES] tablosunun [INDEMNITES] tablosuna bir yabancı anahtara sahip olduğunu gösterir. Bir çalışanı düzenlediğinizde, onun tazminatları da düzenlenir.
[Indemnite] varlığı, [INDEMNITES] tablosundaki bir satırın nesne ifadesidir:
![]() |
|
[Indemnite] öğesi şu şekildedir:
package jpa;
...
@Entity
@Table(name="INDEMNITES")
public class Indemnite implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
@Column(name="VERSION",nullable=false)
private int version;
@Column(name="INDICE", nullable=false,unique=true)
private int indice;
@Column(name="BASE_HEURE",nullable=false)
private double baseHeure;
@Column(name="ENTRETIEN_JOUR",nullable=false)
private double entretienJour;
@Column(name="REPAS_JOUR",nullable=false)
private double repasJour;
@Column(name="INDEMNITES_CP",nullable=false)
private double indemnitesCP;
public Indemnite() {
}
public Indemnite(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP){
setIndice(indice);
setBaseHeure(baseHeure);
setEntretienJour(entretienJour);
setRepasJour(repasJour);
setIndemnitesCP(indemnitesCP);
}
@Override
public String toString() {
return "jpa.Indemnite[id=" + getId()
+ ",version="+getVersion()
+",indice="+getIndice()
+",base heure="+getBaseHeure()
+",entretien jour"+getEntretienJour()
+",repas jour="+getRepasJour()
+",indemnités CP="+getIndemnitesCP()
+ "]";
}
// getter ve setter'lar
....
}
Sınıfın çeşitli alanları, [INDEMNITES] tablosunun çeşitli sütunlarını yansıtmaktadır.
[Cotisation] varlığı, [COTISATIONS] tablosundaki bir satırın nesne ifadesidir:
![]() |
|
[Cotisation] varlığı şöyledir:
package jpa;
...
@Entity
@Table(name="COTISATIONS")
public class Cotisation implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
@Column(name="VERSION",nullable=false)
private int version;
@Column(name="CSGRDS",nullable=false)
private double csgrds;
@Column(name="CSGD",nullable=false)
private double csgd;
@Column(name="SECU",nullable=false)
private double secu;
@Column(name="RETRAITE",nullable=false)
private double retraite;
public Cotisation() {
}
public Cotisation(double csgrds, double csgd, double secu, double retraite){
setCsgrds(csgrds);
setCsgd(csgd);
setSecu(secu);
setRetraite(retraite);
}
@Override
public String toString() {
return "jpa.Cotisation[id=" + getId() + ",version=" + getVersion()+",csgrds="+getCsgrds()+"" +
",csgd="+getCsgd()+",secu="+getSecu()+",retraite="+getRetraite()+"]";
}
// getter ve setter'lar
...
}
Sınıfın çeşitli alanları, [INDEMNITES] tablosunun çeşitli sütunlarını yansıtmaktadır.
18.4. Bir çocuk bakıcısının maaşının hesaplanma yöntemi
Yazacağımız web uygulaması, üç bilgiye dayanarak bir çalışanın maaşını hesaplamamızı sağlayacaktır:
- çalışanın endeksi
- çalışılan gün sayısı
- çalışılan saat sayısı
İşte maaş hesaplamasına ait bir ekran görüntüsü:

Ş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: | ||
Anaokulu asistanının aşağıdaki formülle hesaplanır şeklindedir: | ||
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 bakım ve yemek ödeneği yemek ödeneği almaya hak kazanır. Bu kapsamda, şunlardır: | ||
Sonuç olarak, çocuk bakıcısına ödenecek net maaş şu şekildedir: |
18.5. [métier] katmanının arayüzü
Geliştirmekte olduğumuz uygulamanın mimarisine tekrar göz atalım:
![]() |
[web / struts 2] katmanı, [métier] katmanının arayüzüyle iletişim kurar. Bu arayüz şöyledir:
package metier;
import java.util.List;
import jpa.Employe;
public interface IMetier {
// maaş bordrosunu alma
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// çalışan listesi
List<Employe> findAllEmployes();
}
- 8. satır: Bir çalışanın maaşını hesaplamamızı sağlayacak yöntem
- 10. satır: çalışanlar açılır menüsünü doldurmamızı sağlayacak yöntem
calculerFeuillesalaire yöntemi, aşağıdaki [FeuilleSalaire] sınıfının bir örneğini döndürür:
package metier;
import java.io.Serializable;
import jpa.Cotisation;
import jpa.Employe;
public class FeuilleSalaire implements Serializable {
// özel alanlar
private Employe employe;
private Cotisation cotisation;
private ElementsSalaire elementsSalaire;
// oluşturucular
public FeuilleSalaire() {
}
public FeuilleSalaire(Employe employe, Cotisation cotisation,
ElementsSalaire elementsSalaire) {
setEmploye(employe);
setCotisation(cotisation);
setElementsSalaire(elementsSalaire);
}
// toString
@Override
public String toString() {
return "[" + employe + "," + cotisation + ","
+ elementsSalaire + "]";
}
// alıcı ve ayarlayıcılar
...
}
Maaş bordrosu aşağıdaki bilgileri içerir:
- 10. satır: Maaşı hesaplanan çalışanla ilgili bilgiler
- 11. satır: çeşitli prim oranları
- 12. satır: maaş bileşenleri
[ElementsSalaire] sınıfı şu şekildedir:
package metier;
import java.io.Serializable;
public class ElementsSalaire implements Serializable{
// özel alanlar
private double salaireBase;
private double cotisationsSociales;
private double indemnitesEntretien;
private double indemnitesRepas;
private double salaireNet;
// yapıcılar
public ElementsSalaire() {
}
public ElementsSalaire(double salaireBase, double cotisationsSociales,
double indemnitesEntretien, double indemnitesRepas,
double salaireNet) {
setSalaireBase(salaireBase);
setCotisationsSociales(cotisationsSociales);
setIndemnitesEntretien(indemnitesEntretien);
setIndemnitesRepas(indemnitesRepas);
setSalaireNet(salaireNet);
}
// toString
@Override
public String toString() {
return "[salaire base=" + salaireBase + ",cotisations sociales=" + cotisationsSociales + ",indemnités d'entretien="
+ indemnitesEntretien + ",indemnités de repas=" + indemnitesRepas + ",salaire net="
+ salaireNet + "]";
}
// getter ve setter'lar
...
}
- 8-12. satırlar: maaş bileşenleri
18.6. Spring yapılandırma dosyası
[métier, dao, jpa] katmanlarının entegrasyonu, aşağıdaki Spring yapılandırma dosyası ile sağlanır:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!-- uygulama katmanları -->
<!-- iş alanı -->
<bean id="metier" class="metier.Metier">
<property name="employeDao" ref="employeDao"/>
<property name="cotisationDao" ref="cotisationDao"/>
</bean>
<!-- DAO -->
<bean id="employeDao" class="dao.EmployeDao" />
<bean id="indemniteDao" class="dao.IndemniteDao" />
<bean id="cotisationDao" class="dao.CotisationDao" />
<!-- yapılandırma JPA -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<!-- veri kaynağı DBCP -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="URL" value="jdbc:mysql://localhost:3306/dbpam_hibernate" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<!-- işlem yöneticisi -->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- istisna çevirisi -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- kalıcılık -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
Bu yapılandırmayı açıklamaya çalışmayacağız. Bu yapılandırma, [métier, dao, jpa] katmanlarının örneklenmesi ve entegrasyonu için gereklidir. Bu katmanlara dayanacak web uygulamamızda bu yapılandırmayı kullanmamız gerekecektir. 32-37. satırların veritabanının JDBC özelliklerini yapılandırdığına dikkat edilmelidir. Veritabanını değiştirmek isteyen okuyucular bu satırları düzenlemelidir.
Bu yapılandırma hakkında daha fazla bilgi için, URL [http://tahe.developpez.com/java/javaee] adresinde bulunan “Java EE 5’e Giriş” belgesini inceleyebilirsiniz.











