19. Applicazione Web MVC in un'architettura a 3 livelli – Esempio 5, MySQL
19.1. Il database MySQL
In questa versione, salveremo 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 MySQL Manager Lite [http://www.sqlmanager.net/fr/products/mysql/manager], un client di amministrazione gratuito per il DBMS 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 utilizzando le seguenti istruzioni SQL:
MySQL 4.x sembra meno potente rispetto ai due DBMS precedenti. Non sono riuscito ad aggiungere vincoli (controlli) alla tabella.
- Riga 10: La tabella deve essere di tipo [InnoDB] e non [MyISAM], che non supporta le transazioni.
- Riga 2: La chiave primaria è di tipo auto_increment. Se viene inserita una riga senza un 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 si inserisce un oggetto [Person] tramite il nostro livello [DAO], il campo [id] di questo oggetto è uguale a -1 prima dell'inserimento e ha un valore diverso da -1 in seguito; questo valore è la chiave primaria assegnata alla nuova riga inserita nella tabella [PERSONNES]. Vediamo un esempio per capire come determinare questo valore.
![]() |
![]() |
L'istruzione SQL
ci permette di determinare l'ultimo valore inserito nel campo ID della tabella. Deve essere eseguita dopo l'inserimento. Ciò differisce dai DBMS [Firebird] e [Postgres], dove richiedevamo il valore della chiave primaria della persona aggiunta prima dell'inserimento. La useremo nel file [people-mysql.xml], che contiene le istruzioni SQL eseguite sul database.
19.2. Il progetto Eclipse per i 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.
Directory [src]
Questa cartella contiene il codice sorgente per i livelli [dao] e [service], oltre ai file di configurazione per questi due livelli:

Tutti i file con [mysql] nel nome potrebbero essere stati modificati o meno per le 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 per gli utenti:
![]()
Cartella [lib]
Questa directory contiene i file richiesti dall'applicazione:
![]() |
Si noti la presenza del driver JDBC del DBMS MySQL. Tutti questi file fanno parte del classpath del progetto Eclipse.
19.3. Il livello [dao]
Il livello [dao] è il seguente:

Presentiamo solo le modifiche rispetto alla versione [Firebird].
Il file di mappatura [person-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 class [Person] -->
<typeAlias alias="Personne.classe"
type="istia.st.mvc.personnes.entites.Personne"/>
<!-- mapping table [PERSONNES] - object [Person] -->
<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>
<!-- list of all persons -->
<select id="Personne.getAll" resultMap="Personne.map" > select ID, VERSION, NOM,
PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES</select>
<!-- get a specific person -->
<select id="Personne.getOne" resultMap="Personne.map" >select ID, VERSION, NOM,
PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES WHERE ID=#value#</select>
<!-- add a person -->
<insert id="Personne.insertOne" parameterClass="Personne.classe">
insert into
PERSONNES(VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS)
VALUES(#version#, #nom#, #prenom#, #dateNaissance#, #marie#,
#nbEnfants#)
<selectKey keyProperty="id">
select LAST_INSERT_ID() as value
</selectKey>
</insert>
<!-- update a person -->
<update id="Personne.updateOne" parameterClass="Personne.classe"> update
PERSONNES set VERSION=#version#+1, NOM=#nom#, PRENOM=#prenom#, DATENAISSANCE=#dateNaissance#,
MARIE=#marie#, NBENFANTS=#nbEnfants# WHERE ID=#id# and
VERSION=#version#</update>
<!-- delete a person -->
<delete id="Personne.deleteOne" parameterClass="int"> delete FROM PERSONNES WHERE
ID=#value# </delete>
<!-- obtain the value of the primary key [id] of the last person inserted -->
<select id="Personne.getNextId" resultClass="int">select
LAST_INSERT_ID()</select>
</sqlMap>
Questo è lo stesso contenuto di [people-firebird.xml] con le seguenti piccole differenze:
- l'istruzione SQL "Person.insertOne" è stata modificata alle righe 29–37:
- l'istruzione SQL INSERT viene eseguita prima dell'istruzione SELECT, che recupera il valore della chiave primaria della riga inserita
- l'istruzione SQL di inserimento non ha un valore per la colonna ID nella tabella [PERSONNES]
Ciò rispecchia l'esempio di inserimento discusso nella Sezione 19.1.
Si noti che questo potrebbe essere una potenziale fonte di problemi tra thread concorrenti. Immaginiamo due thread, Th1 e Th2, che eseguono un inserimento contemporaneamente. Ci sono in totale quattro istruzioni SQL da eseguire. Supponiamo che vengano eseguite nel seguente ordine:
- insert I1 da Th1
- insert I2 da Th2
- select S1 da Th1
- select S2 da Th2
Nel passaggio 3, Th1 recupera la chiave primaria generata durante l'ultimo inserimento, che è la chiave di Th2 e non la propria. Non sono sicuro che il metodo [insert] di iBATIS sia protetto contro questo scenario. 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. Questo risolverebbe il problema solo per i thread della nostra applicazione. Se, nell'esempio sopra riportato, Th1 e Th2 fossero thread provenienti da due applicazioni diverse, il problema dovrebbe essere risolto utilizzando sia le transazioni che un livello di isolamento appropriato tra le transazioni. Il livello [serializable], in cui le transazioni vengono eseguite come se fossero in esecuzione sequenziale, sarebbe appropriato.
Si noti che questo problema non esiste con i DBMS Firebird e Postgres, che eseguono il SELECT prima dell'INSERT. Ad esempio, si consideri la seguente sequenza:
- select S1 from Th1
- SELECT S2 da Th2
- insert I1 from Th1
- insert I2 from Th2
Nei passaggi 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 eseguito nel passaggio 4 da Th2 fallirebbe a causa di un duplicato della chiave primaria. Si tratta di un errore completamente 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 c'è un potenziale problema.
La classe di implementazione [DaoImplCommon] del livello [dao] è la stessa delle due versioni precedenti.
La configurazione del livello [dao] è stata adattata al DBMS [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>
<!-- data source 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>
<!-- the [dao] layer access classes -->
<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 dovrebbe modificare questa configurazione in base al proprio ambiente.
- riga 31: la classe [DaoImplCommon] è la classe di implementazione per il livello [dao]
Una volta apportate queste modifiche, possiamo passare alla fase di test.
19.4. Test per i livelli [dao] e [service]
I test per i livelli [dao] e [service] sono gli stessi della versione [Firebird]. I risultati ottenuti sono i seguenti:
![]() |
Possiamo vedere che i test sono stati superati con successo con l'implementazione [DaoImplCommon]. Non sarà necessario derivare questa classe come era necessario con il DBMS [Firebird].
19.5. Test dell'applicazione [Web]
Per testare l'applicazione web con il DBMS [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 (vedere la Sezione 17.7). Tuttavia, come con 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:
![]() | ![]() |
Il DBMS MySQL viene avviato. Il contenuto della tabella [PERSONNES] è quindi il seguente:

Viene quindi avviato Tomcat. Utilizzando un browser, richiediamo l'URL [http://localhost:8080/mvc-personnes-05B]:

Aggiungiamo una nuova persona utilizzando il link [Add]:
![]() | ![]() |
Verifichiamo l'aggiunta nel database:

Il lettore è invitato a eseguire altre operazioni [modifica, elimina].







