19. Applicazione web MVC in un'architettura a tre livelli – Esempio 5, MySQL
19.1. Il database MySQL
In questa versione, installeremo l’elenco delle persone in una tabella del database MySQL 4.x. Abbiamo utilizzato il pacchetto [Apache – MySQL – PHP] disponibile all’URL [http://www.easyphp.org]. Di seguito, le schermate provengono dal client EMS MySQL Manager Lite [http://www.sqlmanager.net/fr/products/mysql/manager], un client di amministrazione gratuito di SGBD MySQL.
Il database si chiama [dbpersonnes]. Contiene una tabella denominata [PERSONNES]:

La tabella [PERSONNES] conterrà l’elenco delle persone gestite dall’applicazione web. È stata creata con i seguenti comandi SQL:
MySQL 4.x sembra più semplice rispetto ai due precedenti SGBD. Non sono riuscito a inserire alcun vincolo (check) nella tabella.
- riga 10: la tabella deve avere il tipo [InnoDB] e non il tipo [MyISAM], che non supporta le transazioni.
- riga 2: la chiave primaria è di tipo auto_increment. Se si inserisce una riga senza valore per la colonna ID della tabella, MySQL genererà automaticamente un numero intero per quella colonna. Questo ci eviterà di dover generare noi stessi le chiavi primarie.
La tabella [PERSONNES] potrebbe avere il seguente contenuto:

Sappiamo che quando il nostro livello [dao] inserisce un oggetto [Personne], il campo [id] di tale oggetto è pari a -1 prima dell’inserimento e assume un valore diverso da -1 in seguito, valore che corrisponde alla chiave primaria assegnata alla nuova riga inserita nella tabella [PERSONNES]. Vediamo con un esempio come possiamo determinare questo valore.
![]() |
![]() |
L’ordine SQL
consente di conoscere l’ultimo valore inserito nel campo ID della tabella. Deve essere emesso dopo l’inserimento. Si tratta di una differenza rispetto ai comandi SGBD, [Firebird] e [Postgres], in cui si richiedeva il valore della chiave primaria della persona aggiunta prima dell’inserimento. Lo useremo nel file [personnes-mysql.xml] che raggruppa gli ordini SQL generati nel database.
19.2. Il progetto Eclipse dei livelli [dao] e [service]
Per sviluppare i livelli [dao] e [service] della nostra applicazione con il database MySQL, utilizzeremo il seguente progetto Eclipse [mvc-personnes-05]:

Il progetto è un semplice progetto Java, non un progetto web Tomcat.
Cartella [src]
Questa cartella contiene i codici sorgente dei livelli [dao] e [service], nonché i file di configurazione di questi due livelli:

Tutti i file che contengono [mysql] nel nome potrebbero aver subito o meno una modifica rispetto alle versioni Firebird e Postgres. Di seguito descriviamo quelli che sono stati modificati.
Cartella [database]
Questa cartella contiene lo script per la creazione del database MySQL relativo alle persone:
![]()
Cartella [lib]
Questa cartella contiene gli archivi necessari all'applicazione:
![]() |
Si noti la presenza del driver JDBC SGBD MySQL. Tutti questi file fanno parte del progetto Eclipse Classpath.
19.3. Il livello [dao]
Il livello [dao] è il seguente:

Presentiamo solo le modifiche rispetto alla versione [Firebird].
Il file di mappatura [personne-mysql.xml] è il seguente:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap>
<!-- alias della classe [Personne] -->
<typeAlias alias="Personne.classe"
type="istia.st.mvc.personnes.entites.Personne"/>
<!-- tabella di mappatura [PERSONNES] - oggetto [Personne] -->
<resultMap id="Personne.map"
class="istia.st.mvc.personnes.entites.Personne">
<result property="id" column="ID" />
<result property="version" column="VERSION" />
<result property="nom" column="NOM"/>
<result property="prenom" column="PRENOM"/>
<result property="dateNaissance" column="DATENAISSANCE"/>
<result property="marie" column="MARIE"/>
<result property="nbEnfants" column="NBENFANTS"/>
</resultMap>
<!-- elenco di tutte le persone -->
<select id="Personne.getAll" resultMap="Personne.map" > select ID, VERSION, NOM,
PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES</select>
<!-- recupera una persona specifica -->
<select id="Personne.getOne" resultMap="Personne.map" >select ID, VERSION, NOM,
PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES WHERE ID=#valore#</select>
<!-- aggiungere una persona -->
<insert id="Personne.insertOne" parameterClass="Personne.classe">
insert into
PERSONNES(VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS)
VALUES(#versione#, #cognome#, #nome#, #dateNaissance#, #maria#,
#nbEnfants#)
<selectKey keyProperty="id">
select LAST_INSERT_ID() as value
</selectKey>
</insert>
<!-- aggiornare una persona -->
<update id="Personne.updateOne" parameterClass="Personne.classe"> update
PERSONNES set VERSION=#versione#+1, NOM=#cognome#, PRENOM=#nome#, DATENAISSANCE=#dateNaissance#,
MARIE=#marie#, NBENFANTS=#nbEnfants# WHERE ID=#id# e
VERSION=#versione#</aggiornamento>
<!-- eliminare una persona -->
<delete id="Personne.deleteOne" parameterClass="int"> delete FROM PERSONNES WHERE
ID=#valore# </delete>
<!-- ottenere il valore della chiave primaria [id] dell'ultima persona inserita -->
<select id="Personne.getNextId" resultClass="int">select
LAST_INSERT_ID()</select>
</sqlMap>
Il contenuto è identico a quello di [personnes-firebird.xml], salvo le seguenti precisazioni:
- l'istruzione SQL " Personne.insertOne " è stata modificata alle righe 29-37:
- il comando di inserimento SQL viene eseguito prima del comando SELECT, che consentirà di recuperare il valore della chiave primaria della riga inserita
- il comando di inserimento SQL non ha alcun valore per la colonna ID della tabella [PERSONNES]
Ciò rispecchia l’esempio di inserimento che abbiamo commentato nel paragrafo 19.1.
Si noti che qui potrebbe esserci una potenziale fonte di problemi tra thread concorrenti. Immaginiamo due thread Th1 e Th2 che effettuano un inserimento contemporaneamente. Ci sono in totale quattro comandi SQL da inviare. Supponiamo che vengano eseguiti nel seguente ordine:
- inserimento I1 da parte di Th1
- inserimento I2 da parte di Th2
- select S1 da Th1
- select S2 da Th2
Al punto 3, Th1 recupera la chiave primaria generata durante l’ultimo inserimento, quindi quella di Th2 e non la propria. Non so se il metodo [insert] di iBATIS sia protetto per questo caso specifico. Supponiamo che lo gestisca correttamente. Se così non fosse, dovremmo derivare la classe di implementazione [DaoImplCommon] dal livello [dao] in una classe [DaoImplMySQL] in cui il metodo [insertPersonne] sarebbe sincronizzato. Ciò risolverebbe il problema solo per i thread della nostra applicazione. Se, come sopra, Th1 e Th2 sono thread di due applicazioni diverse, sarebbe quindi necessario risolvere il problema sia con le transazioni sia con un adeguato livello di isolamento (isolation level) tra le transazioni. Sarebbe appropriato il livello [serializable], in cui le transazioni vengono eseguite come se fossero sequenziali.
Si noti che questo problema non si verifica con Firebird e Postgres, che eseguono il SELECT prima del INSERT. Se, ad esempio, si ha la sequenza:
- select S1 da Th1
- select S2 da Th2
- inserimento I1 da Th1
- inserimento I2 da Th2
Nelle fasi 1 e 2, Th1 e Th2 recuperano i valori della chiave primaria dallo stesso generatore. Questa operazione è normalmente atomica e Th1 e Th2 recupereranno due valori diversi. Se l’operazione non fosse atomica e Th1 e Th2 recuperassero due valori identici, l’inserimento effettuato al punto 4 da Th2 fallirebbe a causa di un duplicato della chiave primaria. Si tratta di un errore perfettamente recuperabile e Th2 può riprovare l’inserimento.
Lasceremo l’operazione “Personne.insertOne” così com’è attualmente nel file [personnes-mysql.xml], ma il lettore deve essere consapevole che qui potrebbe esserci un potenziale problema.
La classe di implementazione [DaoImplCommon] del livello [dao] è quella delle due versioni precedenti.
La configurazione del livello [dao] è stata adattata a SGBD e [MySQL]. Pertanto, il file di configurazione [spring-config-test-dao-mysql.xml] è il seguente:
<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 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</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/dbpersonnes</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
<!-- SqlMapCllient -->
<bean id="sqlMapClient"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="configLocation">
<value>classpath:sql-map-config-mysql.xml</value>
</property>
</bean>
<!-- classe di accesso al livello [dao] -->
<bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
<property name="sqlMapClient">
<ref local="sqlMapClient"/>
</property>
</bean>
</beans>
- righe 5-19: il bean [dataSource] ora fa riferimento al database [MySQL] [dbpersonnes], il cui amministratore è [root] senza password. Il lettore dovrà modificare questa configurazione in base al proprio ambiente.
- riga 31: la classe [DaoImplCommon] è la classe di implementazione del livello [dao]
Una volta apportate queste modifiche, si può procedere ai test.
19.4. Test dei livelli [dao] e [service]
I test dei livelli [dao] e [service] sono gli stessi della versione [Firebird]. I risultati ottenuti sono i seguenti:
![]() |
Si constata che i test sono stati superati con successo con l'implementazione [DaoImplCommon]. Non sarà necessario derivare questa classe, come invece era stato necessario fare con SGBD e [Firebird].
19.5. Test dell’applicazione [web]
Per testare l’applicazione web con SGBD e [MySQL], creiamo un progetto Eclipse [mvc-personnes-05B] in modo analogo a quello utilizzato per creare il progetto [mvc-personnes-03B] con il database Firebird (cfr. paragrafo 17.7). Tuttavia, come nel caso di Postgres, non è necessario ricreare gli archivi [personnes-dao.jar] e [personnes-service.jar] poiché non abbiamo modificato alcuna classe.
Distribuiamo il progetto web [mvc-personnes-05B] all’interno di Tomcat:
![]() | ![]() |
Viene avviato SGBD MySQL. Il contenuto della tabella [PERSONNES] è quindi il seguente:

A sua volta viene avviato Tomcat. Tramite un browser, richiediamo l’URL [http://localhost:8080/mvc-personnes-05B]:

Aggiungiamo una nuova persona tramite il link [Ajout]:
![]() | ![]() |
Verifichiamo l’aggiunta nel database:

Il lettore è invitato a effettuare ulteriori test [modification, suppression].







