19. Aplicativo web MVC em uma arquitetura de três camadas – Exemplo 5, MySQL
19.1. O banco de dados MySQL
Nesta versão, vamos instalar a lista de pessoas em uma tabela do banco de dados MySQL 4.x. Utilizamos o pacote [Apache – MySQL – PHP] disponível no URL [http://www.easyphp.org]. A seguir, as capturas de tela são do cliente EMS MySQL Manager Lite [http://www.sqlmanager.net/fr/products/mysql/manager], um cliente de administração gratuito do SGBD MySQL.
O banco de dados se chama [dbpersonnes]. Ele contém uma tabela [PERSONNES]:

A tabela [PERSONNES] conterá a lista de pessoas gerenciadas pelo aplicativo web. Ela foi criada com os seguintes comandos SQL:
MySQL 4.x parece mais simples do que os dois SGBD anteriores. Não consegui definir restrições (checks) para a tabela.
- linha 10: a tabela deve ter o tipo [InnoDB] e não o tipo [MyISAM], que não suporta transações.
- linha 2: a chave primária é do tipo auto_increment. Se inserirmos uma linha sem valor para a coluna ID da tabela, o MySQL gerará automaticamente um número inteiro para essa coluna. Isso nos poupará de ter que gerar as chaves primárias manualmente.
A tabela [PERSONNES] poderia ter o seguinte conteúdo:

Sabemos que, ao inserir um objeto [Personne] por meio de nossa camada [dao], o campo [id] desse objeto é igual a -1 antes da inserção e assume um valor diferente de -1 depois, sendo esse valor a chave primária atribuída à nova linha inserida na tabela [PERSONNES]. Vejamos, por meio de um exemplo, como podemos obter esse valor.
![]() |
![]() |
A ordem SQL
permite obter o último valor inserido no campo ID da tabela. Ela deve ser emitida após a inserção. Essa é uma diferença em relação às ordens SGBD, [Firebird] e [Postgres], nas quais se solicitava o valor da chave primária da pessoa adicionada antes da inserção. Vamos utilizá-la no arquivo [personnes-mysql.xml], que reúne as ordens SQL emitidas no banco de dados.
19.2. O projeto Eclipse das camadas [dao] e [service]
Para desenvolver as camadas [dao] e [service] de nossa aplicação com o banco de dados MySQL, utilizaremos o seguinte projeto Eclipse [mvc-personnes-05]:

O projeto é um projeto Java simples, não um projeto web Tomcat.
Pasta [src]
Essa pasta contém os códigos-fonte das camadas [dao] e [service], bem como os arquivos de configuração dessas duas camadas:

Todos os arquivos que contêm [mysql] no nome podem ou não ter sofrido alterações em relação às versões do Firebird e do Postgres. A seguir, descrevemos aqueles que foram modificados.
Pasta [database]
Esta pasta contém o script de criação do banco de dados MySQL de pessoas:
![]()
Pasta [lib]
Esta pasta contém os arquivos necessários para o aplicativo:
![]() |
Destaque para a presença do driver JDBC do SGBD MySQL. Todos esses arquivos fazem parte do Classpath do projeto Eclipse.
19.3. A camada [dao]
A camada [dao] é a seguinte:

Apresentamos apenas as alterações em relação à versão [Firebird].
O arquivo de mapeamento [personne-mysql.xml] é o seguinte:
<?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 da classe [Personne] -->
<typeAlias alias="Personne.classe"
type="istia.st.mvc.personnes.entites.Personne"/>
<!-- tabela de mapeamento [PERSONNES] - objeto [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>
<!-- lista de todas as pessoas -->
<select id="Personne.getAll" resultMap="Personne.map" > select ID, VERSION, NOM,
PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES</select>
<!-- obter uma pessoa específica -->
<select id="Personne.getOne" resultMap="Personne.map" >select ID, VERSION, NOM,
PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES WHERE ID=#valor#</select>
<!-- adicionar uma pessoa -->
<insert id="Personne.insertOne" parameterClass="Personne.classe">
insert into
PERSONNES(VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS)
VALUES(#versão#, #sobrenome#, #nome#, #dateNaissance#, #marie#,
#nbEnfants#)
<selectKey keyProperty="id">
select LAST_INSERT_ID() as value
</selectKey>
</insert>
<!-- atualizar um usuário -->
<update id="Personne.updateOne" parameterClass="Personne.classe"> update
PERSONNES set VERSION=#versão#+1, NOM=#sobrenome#, PRENOM=#nome#, DATENAISSANCE=#dateNaissance#,
MARIE=#marie#, NBENFANTS=#nbEnfants# WHERE ID=#id# e
VERSION=#versão#</update>
<!-- excluir uma pessoa -->
<delete id="Personne.deleteOne" parameterClass="int"> delete FROM PERSONNES WHERE
ID=#valor# </delete>
<!-- obter o valor da chave primária [id] da última pessoa inserida -->
<select id="Personne.getNextId" resultClass="int">select
LAST_INSERT_ID()</select>
</sqlMap>
O conteúdo é o mesmo do [personnes-firebird.xml], com as seguintes diferenças:
- a ordem SQL " Personne.insertOne " sofreu alterações nas linhas 29 a 37:
- a ordem de inserção SQL é executada antes da ordem SELECT, o que permitirá recuperar o valor da chave primária da linha inserida
- o comando de inserção SQL não possui valor para a coluna ID da tabela [PERSONNES]
Isso reflete o exemplo de inserção que comentamos no parágrafo 19.1.
Observe-se que pode haver aqui uma possível fonte de problemas entre threads concorrentes. Imaginemos duas threads, Th1 e Th2, que realizam uma inserção ao mesmo tempo. Há, no total, quatro ordens SQL a serem emitidas. Suponhamos que elas sejam realizadas na seguinte ordem:
- inserção I1 de Th1
- inserção I2 de Th2
- seleção S1 de Th1
- select S2 de Th2
No passo 3, Th1 recupera a chave primária gerada na última inserção, ou seja, a de Th2 e não a sua própria. Não sei se o método [insert] de iBATIS está protegido para esse caso específico. Vamos supor que ele lide com isso corretamente. Se não fosse o caso, precisaríamos derivar a classe de implementação [DaoImplCommon] da camada [dao] para uma classe [DaoImplMySQL], na qual o método [insertPersonne] seria sincronizado. Isso resolveria o problema apenas para as threads da nossa aplicação. Se, no exemplo acima, Th1 e Th2 forem threads de duas aplicações diferentes, seria necessário resolver o problema tanto com transações quanto com um nível de isolamento adequado entre as transações. O nível [serializable], no qual as transações são executadas como se fossem executadas sequencialmente, seria adequado.
Observe-se que esse problema não existe com o Firebird e o Postgres, que executam o SELECT antes do INSERT. Se tivermos, por exemplo, a sequência:
- select S1 de Th1
- select S2 de Th2
- inserção I1 de Th1
- inserção I2 de Th2
Nas etapas 1 e 2, Th1 e Th2 obtêm valores de chave primária do mesmo gerador. Essa operação é normalmente atômica, e Th1 e Th2 obterão dois valores diferentes. Se a operação não fosse atômica e Th1 e Th2 recuperassem dois valores idênticos, a inserção realizada na etapa 4 por Th2 falharia devido a uma duplicidade na chave primária. Trata-se de um erro totalmente recuperável, e Th2 pode tentar a inserção novamente.
Vamos deixar a operação “Personne.insertOne” como está atualmente no arquivo [personnes-mysql.xml], mas o leitor deve estar ciente de que há ali um problema em potencial.
A classe de implementação [DaoImplCommon] da camada [dao] é a mesma das duas versões anteriores.
A configuração da camada [dao] foi adaptada para SGBD e [MySQL]. Assim, o arquivo de configuração [spring-config-test-dao-mysql.xml] é o seguinte:
<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- a fonte de dados 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>
<!-- a classe de acesso à camada [dao] -->
<bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
<property name="sqlMapClient">
<ref local="sqlMapClient"/>
</property>
</bean>
</beans>
- linhas 5-19: o bean [dataSource] agora aponta para a base de dados [MySQL] [dbpersonnes], cujo administrador é [root], sem senha. O leitor deverá ajustar essa configuração de acordo com seu próprio ambiente.
- linha 31: a classe [DaoImplCommon] é a classe de implementação da camada [dao]
Feitas essas alterações, podemos passar aos testes.
19.4. Testes das camadas [dao] e [service]
Os testes das camadas [dao] e [service] são os mesmos da versão [Firebird]. Os resultados obtidos são os seguintes:
![]() |
Constata-se que os testes foram concluídos com sucesso com a implementação [DaoImplCommon]. Não será necessário derivar essa classe, como foi necessário fazer com as versões SGBD e [Firebird].
19.5. Testes da aplicação [web]
Para testar a aplicação web com o SGBD e o [MySQL], criamos um projeto Eclipse [mvc-personnes-05B] de maneira semelhante à utilizada para criar o projeto [mvc-personnes-03B] com o banco de dados Firebird (ver parágrafo 17.7). No entanto, assim como no Postgres, não precisamos recriar os arquivos [personnes-dao.jar] e [personnes-service.jar], pois não modificamos nenhuma classe.
Implantamos o projeto web [mvc-personnes-05B] no Tomcat:
![]() | ![]() |
O SGBD e o MySQL são iniciados. O conteúdo da tabela [PERSONNES] passa a ser o seguinte:

O Tomcat é iniciado em seguida. Com um navegador, acessamos a URL [http://localhost:8080/mvc-personnes-05B]:

Adicionamos uma nova pessoa pelo link [Ajout]:
![]() | ![]() |
Verificamos a adição no banco de dados:

O leitor é convidado a realizar outros testes com o [modification, suppression].







