Skip to content

20. Aplicativo web MVC em uma arquitetura de três camadas – Exemplo 6, SQL Server Express

20.1. O banco de dados SQL Server Express

Nesta versão, vamos inserir a lista de pessoas em uma tabela do banco de dados do SQL Server Express 2005, disponível no URL [http://msdn.microsoft.com/vstudio/express/sql/]. A seguir, as capturas de tela foram obtidas a partir do cliente EMS Manager Lite para o SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager], um cliente de administração gratuito do SGBD SQL Server Express.

O banco de dados se chama [dbpersonnes]. Ele contém uma tabela [PERSONNES]:

Image

A tabela [PERSONNES] conterá a lista de pessoas gerenciadas pelo aplicativo web. Ela foi criada com os seguintes comandos 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 for inserida uma linha sem valor para a coluna ID da tabela, o SQL Express gerará automaticamente um número inteiro para essa 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 [PERSONNES] poderia ter o seguinte conteúdo:

Image

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 determinar esse valor.

A ordem SQL

SELECT @@IDENTITY

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 aos SGBD, [Firebird] e [Postgres], nos quais se solicitava o valor da chave primária da pessoa adicionada antes da inserção, mas é semelhante à geração da chave primária dos SGBD e MySQL. Vamos utilizá-la no arquivo [personnes-sqlexpress.xml], que reúne as ordens SQL emitidas no banco de dados.

20.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 [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 os códigos-fonte das camadas [dao] e [service]:

Image

Todos os arquivos que contêm [sqlexpress] no nome podem ou não ter sofrido alterações em relação às versões Firebird, Postgres e MySQL. A seguir, descrevemos apenas aqueles que foram modificados.


Pasta [database]


Esta pasta contém o script de criação do banco de dados SQL Express de pessoas:

Image

-- SQL Manager 2005 Lite para o servidor SQL (2.2.0.1)
-- ---------------------------------------
-- Host      : (local)\SQLEXPRESS
-- Banco de dados: dbpersonnes


--
-- Estrutura da tabela 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

--
-- Dados da tabela 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

--
-- Definição dos índices: 
--

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 o funcionamento do aplicativo:

Destaque para a presença do driver JDBC [sqljdbc.jar], do SGBD e do [Sql Server Express]. Todos esses arquivos fazem parte do Classpath do projeto Eclipse.


20.3. A camada [dao]

A camada [dao] é a seguinte:

Image

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

O arquivo 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 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 @@IDENTITY 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 20.1. Vale ressaltar que aqui se repete o problema de inserções simultâneas por threads diferentes, descrito para MySQL no parágrafo 19.3.

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 para SGBD e [SQL Express]. Assim, o arquivo 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>
    <!-- a fonte de dados 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>
    <!-- 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 [SQL Express] [dbpersonnes], cujo administrador é [sa] com a senha [msde]. O leitor deverá adaptar essa configuração de acordo com seu próprio ambiente.
  • linha 31: a classe [DaoImplCommon] é a classe de implementação da camada [dao]

A linha 11 merece algumas explicações:

            <value>jdbc:sqlserver://localhost\\SQLEXPRESS:4000;databaseName=dbpersonnes</value>
  • //localhost: indica que o servidor SQL Express está na mesma máquina que nossa aplicação Java
  • \\SQLEXPRESS: é o nome de uma instância do SQL Server. Parece que várias instâncias podem ser executadas ao mesmo tempo. Portanto, parece lógico nomear a instância à qual estamos nos dirigindo. Esse nome pode ser obtido por meio do [SQL Server Configuration Manager], normalmente instalado junto com o SQL Express:

Image

Image

  • 4000: porta de escuta do SQL Express. Isso depende da configuração do servidor. Por padrão, ele opera com portas dinâmicas, portanto, desconhecidas antecipadamente. Portanto, não se especifica uma porta na URL JDBC. Neste caso, trabalhamos com uma porta fixa, a porta 4000. Isso é obtido por meio da configuração:
  • o atributo dataBaseName define o banco de dados com o qual se deseja trabalhar. É aquele que foi criado com o cliente EMS:

Image

Feitas essas alterações, podemos passar para os testes.

20.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].

20.5. Testes da aplicação [web]

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

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

O servidor Express SGBD SQL é iniciado. O conteúdo da tabela [PERSONNES] passa a ser o seguinte:

Image

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

Image

Adicionamos uma nova pessoa pelo link [Ajout]:

Verificamos a adição no banco de dados:

Image

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