18. Caso di studio: Struts 2 / Tiles / Spring / Hibernate / MySQL
Concluderemo il nostro percorso di apprendimento su Struts con un caso di studio. Per garantire un approccio realistico, l’esempio che analizzeremo sarà decisamente più complesso rispetto a quelli esaminati in precedenza. Per i principianti, è senza dubbio preferibile approfondire le basi di Struts 2 con applicazioni personali prima di affrontare questo caso di studio.
L’applicazione utilizzerà un’architettura a livelli:
![]() |
L'insieme dei livelli [metier], [dao], [jpa/hibernate] ci verrà fornito sotto forma di un archivio jar di cui descriveremo in dettaglio le funzionalità. L’integrazione dei livelli sarà gestita da Spring. Il livello [web] sarà implementato da Struts 2.
![]() |
18.1. Il problema
Ci proponiamo di scrivere un’applicazione web che consenta di redigere la busta paga delle assistenti materne impiegate presso la «Maison de la petite enfance» di un comune.

Questo caso di studio è presentato nel documento:
Introduzione a Java EE 5 disponibile all'indirizzo URL [http://tahe.developpez.com/java/javaee]
In questo documento, il caso di studio è implementato con la seguente architettura a più livelli:
![]() |
Il livello [web] è implementato utilizzando il framework JSF (Java Server Faces). Adotteremo questa stessa architettura implementando il livello [web] con Struts 2. Per illustrare i vantaggi delle architetture a livelli, utilizzeremo l’archivio jar dei livelli [metier, dao, jpa] della versione JSF e lo collegheremo a un livello [web / struts2]:
![]() |
Presenteremo i seguenti elementi del livello [metier, dao, jpa]:
- il livello [web] si interfaccia con il livello [métier]. Presenteremo tale interfaccia.
- il livello [jpa] accede a un database. Lo presenteremo.
- Il livello [jpa] trasforma le righe delle tabelle del database in entità JPA gestite da tutti i livelli dell’applicazione. Le presenteremo.
- I livelli [metier, dao, jpa] vengono istanziati da Spring. Presenteremo il file di configurazione che realizza tale istanziazione e integrazione.
18.2. Il database
Utilizzeremo il seguente database MySQL [dbpam_hibernate]:

- In [1], il database presenta tre tabelle:
- [employes]: una tabella che registra le dipendenti di un asilo nido
- [cotisations]: una tabella che registra le aliquote dei contributi sociali
- [indemnites]: una tabella che registra le informazioni necessarie per calcolare la busta paga delle dipendenti
Tabella [employes]
![]() |
- in [2], la tabella delle dipendenti e in [3], il significato dei suoi campi
Il contenuto della tabella potrebbe essere il seguente:
Tabella [cotisations]
![]() |
- in [4], la tabella dei contributi e in [5], il significato dei suoi campi
Il contenuto della tabella potrebbe essere il seguente:
Tabella [indemnites]
![]() |
- in [6], la tabella delle indennità e in [7], il significato dei suoi campi
Il contenuto della tabella potrebbe essere il seguente:
L’esportazione della struttura del database in un file SQL fornisce il seguente risultato:
18.3. Le entità JPA
Nella seguente architettura
![]() |
Il livello [Jpa] funge da ponte tra gli oggetti gestiti dal livello [dao] e le righe delle tabelle del database gestite dal driver JDBC. Le righe delle tabelle lette dal database vengono trasformate in oggetti denominati entità JPA. Viceversa, in fase di scrittura, le entità JPA vengono trasformate in righe delle tabelle. Queste entità vengono gestite da tutti i livelli, in particolare dal livello web. È quindi necessario conoscerle:
L'entità [Employe] rappresenta una riga della tabella [Employes]
![]() |
|
L'entità [Employe] è la seguente:
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()
+"]";
}
// getter e setter
...
}
Si ignoreranno le annotazioni @ destinate al livello [Jpa]. I diversi campi della classe riflettono le diverse colonne della tabella [EMPLOYES]. Il campo «indemnites» (riga 29) riflette il fatto che la tabella [EMPLOYES] ha una chiave esterna sulla tabella [INDEMNITES]. Quando si modifica un dipendente, si modificano anche le sue indennità.
L’entità [Indemnite] è l’espressione oggetto di una riga della tabella [INDEMNITES]:
![]() |
|
L'entità [Indemnite] è la seguente:
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 e setter
....
}
I diversi campi della classe riflettono le diverse colonne della tabella [INDEMNITES].
L'entità [Cotisation] è l'espressione oggetto di una riga della tabella [COTISATIONS]:
![]() |
|
L'entità [Cotisation] è la seguente:
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 e setter
...
}
I diversi campi della classe rispecchiano le diverse colonne della tabella [INDEMNITES].
18.4. Modalità di calcolo dello stipendio di una tata
L'applicazione web che scriveremo ci consentirà di calcolare lo stipendio di una dipendente sulla base di tre informazioni:
- l’indice della dipendente
- il numero di giorni lavorati
- il numero di ore lavorate
Ecco uno screenshot del calcolo di uno stipendio:

Presentiamo ora il metodo di calcolo dello stipendio mensile di una tata. Non si tratta di quello effettivamente utilizzato nella realtà. Prendiamo ad esempio lo stipendio della signora Marie Jouveinal, che ha lavorato 150 ore in 20 giorni durante il mese di riferimento.
Vengono presi in considerazione i seguenti elementi: | ||
Lo stipendio base dell'assistente è dato dalla formula seguente: | ||
Una certa quota di contributi sociali devono essere trattenute da questo stipendio base: | ||
Totale dei contributi sociali: | ||
Inoltre, l'assistente all'infanzia ha diritto, per ogni giorno lavorato, a un'indennità di mantenimento e a un'indennità di pasti. A tal fine riceve le indennità seguenti: | ||
In definitiva, lo stipendio netto da corrispondere alla tata è il seguente: |
18.5. L'interfaccia del livello [métier]
Torniamo all’architettura dell’applicazione che stiamo realizzando:
![]() |
Il livello [web / struts 2] comunica con l’interfaccia del livello [métier]. Quest’ultima è la seguente:
package metier;
import java.util.List;
import jpa.Employe;
public interface IMetier {
// ottenere la busta paga
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// elenco dei dipendenti
List<Employe> findAllEmployes();
}
- riga 8: il metodo che ci consentirà di calcolare lo stipendio di un dipendente
- riga 10: il metodo che ci consentirà di compilare il menu a tendina dei dipendenti
Il metodo calculerFeuillesalaire restituisce un'istanza della classe [FeuilleSalaire] come segue:
package metier;
import java.io.Serializable;
import jpa.Cotisation;
import jpa.Employe;
public class FeuilleSalaire implements Serializable {
// campi privati
private Employe employe;
private Cotisation cotisation;
private ElementsSalaire elementsSalaire;
// costruttori
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 + "]";
}
// getter e setter
...
}
Il foglio paga racchiude le seguenti informazioni:
- riga 10: informazioni sul dipendente di cui si calcola lo stipendio
- riga 11: le diverse aliquote contributive
- riga 12: elementi della retribuzione
La classe [ElementsSalaire] è la seguente:
package metier;
import java.io.Serializable;
public class ElementsSalaire implements Serializable{
// campi privati
private double salaireBase;
private double cotisationsSociales;
private double indemnitesEntretien;
private double indemnitesRepas;
private double salaireNet;
// costruttori
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 e setter
...
}
- righe 8-12: le componenti della retribuzione
18.6. Il file di configurazione di Spring
L'integrazione dei livelli [métier, dao, jpa] è garantita dal seguente file di configurazione Spring:
<?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">
<!-- livelli applicativi -->
<!-- logica di business -->
<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" />
<!-- configurazione 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>
<!-- la fonte dei dati 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>
<!-- gestore delle transazioni -->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- traduzione delle eccezioni -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- persistenza -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
Non cercheremo di spiegare questa configurazione. È necessaria per l’istanziazione e l’integrazione dei livelli [métier, dao, jpa]. La nostra applicazione web, che si baserà su questi livelli, dovrà quindi riprendere questa configurazione. Si noti che le righe 32-37 configurano le caratteristiche JDBC del database. Il lettore che desiderasse cambiare database dovrà modificare queste righe.
Per ulteriori informazioni su questa configurazione, si rimanda al documento «Introduzione a Java EE 5» disponibile su URL [http://tahe.developpez.com/java/javaee].











