Skip to content

20. Aplicação Web MVC numa arquitetura de 3 camadas – Exemplo 6, SQL Server Express

20.1. A base de dados SQL Server Express

Nesta versão, iremos armazenar a lista de pessoas numa tabela da base de dados SQL Server Express 2005 disponível no URL [http://msdn.microsoft.com/vstudio/express/sql/]. As capturas de ecrã seguintes são do cliente EMS Manager Lite para SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager], um cliente de administração gratuito para o SGBD SQL Server Express.

A base de dados chama-se [dbpersonnes]. Contém uma tabela chamada [PERSONNES]:

Image

A tabela [PERSONNES] conterá a lista de pessoas geridas pela aplicação web. Foi criada utilizando as seguintes instruções SQL:

CREATE TABLE [dbo].[PERSONNES] (
  [ID] int IDENTITY(1, 1) NOT NULL,
  [VERSION] int NOT NULL,
  [NOM] varchar(30) COLLATE French_CI_AS NOT NULL,
  [PRENOM] varchar(30) COLLATE French_CI_AS NOT NULL,
  [DATENAISSANCE] datetime NOT NULL,
  [MARIE] tinyint NOT NULL,
  [NBENFANTS] tinyint NOT NULL,
  PRIMARY KEY CLUSTERED ([ID]),
  CONSTRAINT [PERSONNES_ck_NOM] CHECK ([NOM]<>''),
  CONSTRAINT [PERSONNES_ck_PRENOM] CHECK ([PRENOM]<>''),
  CONSTRAINT [PERSONNES_ck_NBENFANTS] CHECK ([NBENFANTS]>=(0))
)
ON [PRIMARY]
GO
  • Linha 2: A chave primária [ID] é do tipo inteiro. O atributo IDENTITY indica que, se uma linha for inserida sem um valor para a coluna ID da tabela, o SQL Express irá gerar um inteiro para esta coluna. Em IDENTITY(1, 1), o primeiro parâmetro é o primeiro valor possível para a chave primária e o segundo é o incremento utilizado na geração dos números.

A tabela [PERSONS] poderia ter o seguinte conteúdo:

Image

Sabemos que, ao inserir um objeto [Person] através da nossa camada [DAO], o campo [id] deste objeto é igual a -1 antes da inserção e tem um valor diferente de -1 depois; este valor é a chave primária atribuída à nova linha inserida na tabela [PERSONNES]. Vejamos um exemplo para perceber como podemos determinar este valor.

A instrução SQL

SELECT @@IDENTITY

retorna o último valor inserido no campo ID da tabela. Deve ser executada após a inserção. Isto difere dos SGBDs [Firebird] e [Postgres], onde o valor da chave primária do registo adicionado era consultado antes da inserção, mas é análogo à geração da chave primária no SGBD MySQL. Iremos utilizá-lo no ficheiro [people-sqlexpress.xml], que contém as instruções SQL executadas na base de dados.

20.2. O projeto Eclipse para as camadas [DAO] e [service]

Para desenvolver as camadas [DAO] e [service] da nossa aplicação com a base de dados [SQL Server Express], utilizaremos o seguinte projeto Eclipse [mvc-personnes-06]:

Image

O projeto é um projeto Java simples, não um projeto web Tomcat.


Pasta [src]


Esta pasta contém o código-fonte das camadas [dao] e [service]:

Image

Todos os ficheiros com [sqlexpress] no nome podem ou não ter sido modificados em comparação com as versões Firebird, Postgres e MySQL. A seguir, descrevemos apenas aqueles que foram modificados.


Pasta [database]


Esta pasta contém o script para criar a base de dados SQL Express para as pessoas:

Image

-- SQL Manager 2005 Lite for SQL Server (2.2.0.1)
-- ---------------------------------------
-- Host : (local)\SQLEXPRESS
-- Database : dbpersonnes


--
-- Structure for table PERSONNES : 
--

CREATE TABLE [dbo].[PERSONNES] (
  [ID] int IDENTITY(1, 1) NOT NULL,
  [VERSION] int NOT NULL,
  [NOM] varchar(30) COLLATE French_CI_AS NOT NULL,
  [PRENOM] varchar(30) COLLATE French_CI_AS NOT NULL,
  [DATENAISSANCE] datetime NOT NULL,
  [MARIE] tinyint NOT NULL,
  [NBENFANTS] tinyint NOT NULL,
  CONSTRAINT [PERSONNES_ck_NBENFANTS] CHECK ([NBENFANTS]>=(0)),
  CONSTRAINT [PERSONNES_ck_NOM] CHECK ([NOM]<>''),
  CONSTRAINT [PERSONNES_ck_PRENOM] CHECK ([PRENOM]<>'')
)
ON [PRIMARY]
GO

--
-- Data for table PERSONNES  (LIMIT 0,500)
--

SET IDENTITY_INSERT [dbo].[PERSONNES] ON
GO

INSERT INTO [dbo].[PERSONNES] ([ID], [VERSION], [NOM], [PRENOM], [DATENAISSANCE], [MARIE], [NBENFANTS])
VALUES 
  (1, 1, 'Major', 'Joachim', '19541113', 1, 2)
GO

INSERT INTO [dbo].[PERSONNES] ([ID], [VERSION], [NOM], [PRENOM], [DATENAISSANCE], [MARIE], [NBENFANTS])
VALUES 
  (2, 1, 'Humbort', 'Mélanie', '19850212', 0, 1)
GO

INSERT INTO [dbo].[PERSONNES] ([ID], [VERSION], [NOM], [PRENOM], [DATENAISSANCE], [MARIE], [NBENFANTS])
VALUES 
  (3, 1, 'Lemarchand', 'Charles', '19860301', 0, 0)
GO

SET IDENTITY_INSERT [dbo].[PERSONNES] OFF
GO

--
-- Definition for indices : 
--

ALTER TABLE [dbo].[PERSONNES]
ADD PRIMARY KEY CLUSTERED ([ID])
WITH (
  PAD_INDEX = OFF,
  IGNORE_DUP_KEY = OFF,
  STATISTICS_NORECOMPUTE = OFF,
  ALLOW_ROW_LOCKS = ON,
  ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
GO

pasta [lib]


Esta pasta contém os arquivos necessários para a aplicação:

Repare na presença do controlador JDBC [sqljdbc.jar] para o SGBD [SQL Server Express]. Todos estes ficheiros fazem parte do classpath do projeto Eclipse.


20.3. A camada [DAO]

A camada [dao] é a seguinte:

Image

Estamos apenas a destacar as alterações em relação à versão [Firebird].

O ficheiro de mapeamento [personne-sqlexpress.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 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 @@IDENTITY 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>

Este é o mesmo conteúdo que [people-firebird.xml], com as seguintes pequenas diferenças:

  • a instrução SQL «Person.insertOne» foi alterada nas linhas 29–37:
  • a instrução SQL INSERT é executada antes da instrução SELECT, que recupera o valor da chave primária da linha inserida
  • a instrução SQL INSERT não especifica um valor para a coluna ID na tabela [PERSONNES]

Isto reflete o exemplo de inserção que discutimos na Secção 20.1. Note-se que a questão das inserções simultâneas por diferentes threads, descrita para o MySQL na Secção 19.3, também está presente aqui.

A classe de implementação [DaoImplCommon] da camada [dao] é a mesma das três versões anteriores.

A configuração da camada [dao] foi adaptada ao SGBD [SQL Express]. Assim, o ficheiro de configuração [spring-config-test-dao-sqlexpress.xml] é o seguinte:


<?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.microsoft.sqlserver.jdbc.SQLServerDriver</value>
        </property>
        <property name="url">
            <value>jdbc:sqlserver://localhost\\SQLEXPRESS:4000;databaseName=dbpersonnes</value>
        </property>
        <property name="username">
            <value>sa</value>
        </property>
        <property name="password">
            <value>msde</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-sqlexpress.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>
  • linhas 5-19: O bean [dataSource] refere-se agora à base de dados [SQL Express] [dbpersonnes], cujo administrador é [sa] com a palavra-passe [msde]. O leitor deve modificar esta configuração de acordo com o seu próprio ambiente.
  • Linha 31: A classe [DaoImplCommon] é a classe de implementação para a camada [dao]

A linha 11 requer alguma explicação:


            <value>jdbc:sqlserver://localhost\\SQLEXPRESS:4000;databaseName=dbpersonnes</value>
  • //localhost: indica que o servidor SQL Express está na mesma máquina que a nossa aplicação Java
  • \\SQLEXPRESS: é o nome de uma instância do SQL Server. Parece que várias instâncias podem ser executadas simultaneamente. Por isso, faz sentido nomear a instância específica a que se refere. Este nome pode ser obtido utilizando o [SQL Server Configuration Manager], que normalmente é instalado juntamente com o SQL Express:

Image

Image

  • 4000: porta de escuta do SQL Express. Isto depende da configuração do servidor. Por predefinição, utiliza portas dinâmicas, que não são conhecidas antecipadamente. Nesse caso, não é especificada nenhuma porta no URL JDBC. Aqui, utilizámos uma porta fixa, a porta 4000. Esta é definida através da configuração:
  • O atributo dataBaseName especifica a base de dados com a qual pretende trabalhar. Esta é a que foi criada com o cliente EMS:

Image

Com estas alterações feitas, podemos passar aos testes.

20.4. Testes para as camadas [dao] e [service]

Os testes para as camadas [dao] e [service] são os mesmos da versão [Firebird]. Os resultados obtidos são os seguintes:

Podemos ver que os testes foram bem-sucedidos com a implementação [DaoImplCommon]. Não precisaremos de derivar esta classe, como era necessário com o SGBD [Firebird].

20.5. Testes da aplicação [Web]

Para testar a aplicação web com o SGBD [SQL Server Express], criamos um projeto Eclipse [mvc-personnes-06B] de forma semelhante à utilizada para criar os projetos web anteriores.

Implementamos o projeto web [mvc-personnes-05B] no Tomcat:

O SGBD SQL Server Express é iniciado. O conteúdo da tabela [PERSONNES] é então o seguinte:

Image

O Tomcat é então iniciado. Utilizando um navegador, solicitamos o URL [http://localhost:8080/mvc-personnes-06B]:

Image

Adicionamos uma nova pessoa utilizando o link [Adicionar]:

Verificamos a adição na base de dados:

Image

Convidamos o leitor a realizar outros testes [editar, eliminar].