18. Casestudy: Struts 2 / Tiles / Spring / Hibernate / MySQL
We sluiten onze Struts-cursus af met een casestudy. Om het realistisch te houden, zal het voorbeeld dat we behandelen aanzienlijk complexer zijn dan de voorgaande voorbeelden. Voor beginners is het waarschijnlijk beter om de basis van Struts 2 eerst grondig te bestuderen aan de hand van eigen applicaties, voordat ze aan deze casestudy beginnen.
De applicatie maakt gebruik van een gelaagde architectuur:
![]() |
De lagen [metier], [dao] en [jpa/hibernate] worden ons aangeleverd in de vorm van een jar-archief, waarvan we de functionaliteiten in detail zullen bespreken. De integratie van de lagen wordt verzorgd door Spring. De laag [web] wordt geïmplementeerd door Struts 2.
![]() |
18.1. Het probleem
We willen een webapplicatie schrijven waarmee de loonstrook kan worden opgesteld voor de kinderopvangmedewerkers die in dienst zijn bij het „Maison de la petite enfance“ van een gemeente.

Deze casestudy wordt gepresenteerd in het document:
Inleiding tot Java EE 5, beschikbaar op URL [http://tahe.developpez.com/java/javaee]
In dit document is de casestudy geïmplementeerd met de volgende meerlaagse architectuur:
![]() |
De laag [web] is geïmplementeerd met behulp van het framework JSF (Java Server Faces). We zullen dezelfde architectuur hanteren door de laag [web] te implementeren met Struts 2. Om het nut van gelaagde architecturen te illustreren, gaan we het jar-archief van de lagen [metier, dao, jpa] uit versie JSF gebruiken en dit koppelen aan een laag [web / struts2]:
![]() |
We zullen de volgende elementen van de lagen [metier, dao, jpa] bespreken:
- de laag [web] is gekoppeld aan de interface van de laag [métier]. We zullen deze interface bespreken.
- De laag [jpa] heeft toegang tot een database. We zullen deze bespreken.
- De laag [jpa] zet de rijen uit de databasetabellen om in JPA-entiteiten die door alle lagen van de applicatie worden gebruikt. We zullen deze presenteren.
- De lagen [metier, dao, jpa] worden door Spring geïnstantieerd. We zullen het configuratiebestand presenteren dat deze instantiëring en integratie uitvoert.
18.2. De database
We zullen de volgende database MySQL [dbpam_hibernate] gebruiken:

- In [1] bevat de database drie tabellen:
- [employes]: een tabel waarin de medewerksters van een kinderdagverblijf worden geregistreerd
- [cotisations]: een tabel waarin de sociale premiepercentages worden vastgelegd
- [indemnites]: een tabel waarin gegevens worden opgeslagen waarmee het loon van de medewerksters kan worden berekend
Tabel [employes]
![]() |
- in [2], de tabel met werknemers en in [3], de betekenis van de velden
De inhoud van de tabel zou als volgt kunnen zijn:
Tabel [cotisations]
![]() |
- in [4], de tabel met premies en in [5], de betekenis van de velden
De inhoud van de tabel zou als volgt kunnen zijn:
Tabel [indemnites]
![]() |
- in [6], de tabel met vergoedingen en in [7], de betekenis van de velden
De inhoud van de tabel zou als volgt kunnen zijn:
Het exporteren van de databasestructuur naar een bestand SQL levert het volgende resultaat op:
18.3. De entiteiten JPA
In de volgende architectuur
![]() |
De laag [Jpa] fungeert als brug tussen de objecten die door de laag [dao] worden verwerkt en de rijen in de databasetabellen die door de JDBC-driver worden verwerkt. De rijen uit de databasetabellen worden omgezet in objecten die JPA-entiteiten worden genoemd. Omgekeerd, bij het schrijven, worden de JPA-entiteiten omgezet in rijen in de tabellen. Deze entiteiten worden door alle lagen verwerkt, en met name door de weblaag. We moeten ze dus kennen:
De entiteit [Employe] vertegenwoordigt een rij uit de tabel [Employes]
![]() |
|
De entiteit [Employe] is als volgt:
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()
+"]";
}
// getters en setters
...
}
De @-annotaties die betrekking hebben op de laag [Jpa] worden genegeerd. De verschillende velden van de klasse komen overeen met de verschillende kolommen van de tabel [EMPLOYES]. Het veld ‘indemnites’ (regel 29) geeft aan dat de tabel [EMPLOYES] een vreemde sleutel heeft naar de tabel [INDEMNITES]. Wanneer een werknemer wordt bewerkt, worden ook zijn vergoedingen bewerkt.
De entiteit [Indemnite] is de objectuitdrukking van een rij in de tabel [INDEMNITES]:
![]() |
|
De entiteit [Indemnite] is als volgt:
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()
+ "]";
}
// getters en setters
....
}
De verschillende velden van de klasse komen overeen met de verschillende kolommen van de tabel [INDEMNITES].
De entiteit [Cotisation] is de objectuitdrukking van een rij in de tabel [COTISATIONS]:
![]() |
|
De entiteit [Cotisation] is als volgt:
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()+"]";
}
// getters en setters
...
}
De verschillende velden van de klasse komen overeen met de verschillende kolommen van de tabel [INDEMNITES].
18.4. Berekeningswijze van het salaris van een kinderoppas
Met de webapplicatie die we gaan schrijven, kunnen we het salaris van een medewerkster berekenen op basis van drie gegevens:
- de index van de medewerkster
- het aantal gewerkte dagen
- het aantal gewerkte uren
Hieronder ziet u een schermafbeelding van een salarisberekening:

We laten nu zien hoe het maandsalaris van een kinderoppas wordt berekend. Dit is niet de berekeningswijze die in de praktijk wordt gebruikt. We nemen als voorbeeld het salaris van mevrouw Marie Jouveinal, die in de te betalen maand 150 uur heeft gewerkt, verdeeld over 20 dagen.
Er wordt rekening gehouden met de volgende elementen: | ||
Het basissalaris van de kleuterleidster wordt berekend volgens de : | ||
Een aantal sociale premies moeten worden ingehouden op dit : | ||
Totaal aan sociale premies: | ||
Bovendien heeft de kinderopvangmoeder recht op voor elke gewerkte dag op een onderhoudsvergoeding en een maaltijdvergoeding maaltijdvergoeding. Hiervoor ontvangt zij de : | ||
Uiteindelijk bedraagt het aan de kinderopvangmoeder uit te betalen nettoloon: |
18.5. De interface van de laag [métier]
Laten we nog eens terugkomen op de architectuur van de applicatie die we aan het bouwen zijn:
![]() |
De laag [web / struts 2] communiceert met de interface van de laag [métier]. Deze ziet er als volgt uit:
package metier;
import java.util.List;
import jpa.Employe;
public interface IMetier {
// loonstrook ophalen
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// lijst met werknemers
List<Employe> findAllEmployes();
}
- regel 8: de methode waarmee we het salaris van een medewerker kunnen berekenen
- regel 10: de methode waarmee we de keuzelijst met werknemers kunnen vullen
De methode calculerFeuillesalaire retourneert een instantie van de klasse [FeuilleSalaire], namelijk:
package metier;
import java.io.Serializable;
import jpa.Cotisation;
import jpa.Employe;
public class FeuilleSalaire implements Serializable {
// privévelden
private Employe employe;
private Cotisation cotisation;
private ElementsSalaire elementsSalaire;
// constructors
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 + "]";
}
// getters en setters
...
}
De loonlijst bevat de volgende informatie:
- regel 10: informatie over de werknemer wiens salaris wordt berekend
- regel 11: de verschillende premietarieven
- regel 12: looncomponenten
De klasse [ElementsSalaire] ziet er als volgt uit:
package metier;
import java.io.Serializable;
public class ElementsSalaire implements Serializable{
// privévelden
private double salaireBase;
private double cotisationsSociales;
private double indemnitesEntretien;
private double indemnitesRepas;
private double salaireNet;
// constructors
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 + "]";
}
// getters en setters
...
}
- regels 8-12: de looncomponenten
18.6. Het Spring-configuratiebestand
De integratie van de lagen [métier, dao, jpa] wordt verzorgd door het volgende Spring-configuratiebestand:
<?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">
<!-- toepassingslagen -->
<!-- bedrijfslogica -->
<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" />
<!-- configuratie 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>
<!-- de gegevensbron 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>
<!-- de transactiebeheerder -->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- vertaling van uitzonderingen -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- persistentie -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
We zullen deze configuratie niet proberen uit te leggen. Ze is noodzakelijk voor het instantiëren en integreren van de [métier, dao, jpa]-lagen. Onze webapplicatie, die op deze lagen zal steunen, moet deze configuratie dus overnemen. Merk op dat de regels 32-37 de JDBC-instellingen van de database configureren. De lezer die van database wil wisselen, moet deze regels aanpassen.
Voor meer informatie over deze configuratie kunt u het document „Inleiding tot Java EE 5“ raadplegen, dat beschikbaar is op de URL [http://tahe.developpez.com/java/javaee].











