Skip to content

20. Aplicación web MVC en una arquitectura de tres capas – Ejemplo 6, SQL Server Express

20.1. La base de datos SQL Server Express

En esta versión, vamos a instalar la lista de personas en una tabla de la base de datos SQL Server Express 2005, disponible en la URL [http://msdn.microsoft.com/vstudio/express/sql/]. A continuación, las capturas de pantalla proceden del cliente EMS Manager Lite para SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager], un cliente de administración gratuito de SGBD SQL Server Express.

La base de datos se llama [dbpersonnes]. Contiene una tabla [PERSONNES]:

Image

La tabla [PERSONNES] contendrá la lista de personas gestionadas por la aplicación web. Se ha creado con las siguientes órdenes 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
  • línea 2: la clave primaria [ID] es de tipo entero. El atributo IDENTITY indica que, si se inserta una fila sin valor en la columna ID de la tabla, SQL Express generará por sí mismo un número entero para dicha columna. En IDENTITY(1, 1), el primer parámetro es el primer valor posible para la clave primaria, y el segundo, el incremento utilizado en la generación de los números.

La tabla [PERSONNES] podría tener el siguiente contenido:

Image

Sabemos que, al insertar un objeto [Personne] mediante nuestra capa [dao], el campo [id] de dicho objeto es igual a -1 antes de la inserción y tiene un valor distinto de -1 después; este valor es la clave primaria asignada a la nueva fila insertada en la tabla [PERSONNES]. Veamos con un ejemplo cómo podemos averiguar este valor.

El pedido SQL

SELECT @@IDENTITY

permite conocer el último valor introducido en el campo ID de la tabla. Debe ejecutarse tras la inserción. Esto difiere de las consultas SGBD, [Firebird] y [Postgres], en las que se solicitaba el valor de la clave primaria de la persona añadida antes de la inserción, pero es similar a la generación de la clave primaria de los archivos SGBD y MySQL. Lo utilizaremos en el archivo [personnes-sqlexpress.xml], que recopila las órdenes SQL emitidas en la base de datos.

20.2. El proyecto de Eclipse de las capas [dao] y [service]

Para desarrollar las capas [dao] y [service] de nuestra aplicación con la base de datos [SQL Server Express], utilizaremos el siguiente proyecto de Eclipse [mvc-personnes-06]:

Image

El proyecto es un simple proyecto Java, no un proyecto web de Tomcat.


Carpeta [src]


Esta carpeta contiene los códigos fuente de las capas [dao] y [service]:

Image

Todos los archivos que incluyen [sqlexpress] en su nombre pueden haber sufrido o no modificaciones con respecto a las versiones de Firebird, Postgres y MySQL. A continuación, solo describimos aquellos que han sido modificados.


Carpeta [database]


Esta carpeta contiene el script de creación de la base de datos SQL Express de personas:

Image

-- SQL Manager 2005 Lite para SQL Server (2.2.0.1)
-- ---------------------------------------
-- Host     : (local)\SQLEXPRESS
-- Base de datos: dbpersonnes


--
-- Estructura de la tabla 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

--
-- Datos de la tabla 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

--
-- Definición de í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

Carpeta [lib]


Esta carpeta contiene los archivos necesarios para la aplicación:

Cabe destacar la presencia del controlador JDBC [sqljdbc.jar], junto con SGBD y [Sql Server Express]. Todos estos archivos forman parte del Classpath del proyecto Eclipse.


20.3. La capa [dao]

La capa [dao] es la siguiente:

Image

Solo presentamos los cambios respecto a la versión [Firebird].

El archivo de mapeo [personne-sqlexpress.xml] es el siguiente:


<?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 de la clase [Personne] -->
    <typeAlias alias="Personne.classe" 
        type="istia.st.mvc.personnes.entites.Personne"/>
    <!-- tabla de correspondencias [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 las personas -->
    <select id="Personne.getAll" resultMap="Personne.map" > select ID, VERSION, NOM, 
        PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES</select>
    <!-- obtener una persona en concreto -->
        <select id="Personne.getOne" resultMap="Personne.map" >select ID, VERSION, NOM, 
        PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES WHERE ID=#valor#</select>
    <!-- añadir una persona -->
    <insert id="Personne.insertOne" parameterClass="Personne.classe">
        insert into 
        PERSONNES(VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS) 
        VALUES(#versión#, #apellidos#, #nombre#, #dateNaissance#, #marie#, 
        #nbEnfants#) 
        <selectKey keyProperty="id">
            select @@IDENTITY as value
        </selectKey>         
    </insert>
    <!-- actualizar un usuario -->
    <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
        PERSONNES set VERSION=#versión#+1, NOM=#apellidos#, PRENOM=#nombre#, DATENAISSANCE=#dateNaissance#, 
        MARIE=#marie#, NBENFANTS=#nbEnfants# WHERE ID=#id# y 
        VERSION=#versión#</update>
    <!-- eliminar a una persona -->
    <delete id="Personne.deleteOne" parameterClass="int"> delete FROM PERSONNES WHERE 
        ID=#valor# </delete>
    <!-- obtener el valor de la clave primaria [id] de la última persona insertada -->
    <select id="Personne.getNextId" resultClass="int">select 
        LAST_INSERT_ID()</select>
</sqlMap>

El contenido es el mismo que el de [personnes-firebird.xml], salvo por los siguientes detalles:

  • la orden SQL «Personne.insertOne» ha cambiado las líneas 29-37:
  • la orden de inserción SQL se ejecuta antes que la orden SELECT, lo que permitirá recuperar el valor de la clave primaria de la línea insertada
  • la orden de inserción SQL no tiene ningún valor para la columna ID de la tabla [PERSONNES]

Esto refleja el ejemplo de inserción que hemos comentado en el apartado 20.1. Cabe señalar que aquí se repite el problema de las inserciones simultáneas por parte de diferentes subprocesos descrito para MySQL en el apartado 19.3.

La clase de implementación [DaoImplCommon] de la capa [dao] es la misma que la de las tres versiones anteriores.

La configuración de la capa [dao] se ha adaptado a SGBD y [SQL Express]. Así pues, el archivo de configuración [spring-config-test-dao-sqlexpress.xml] es el siguiente:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- la fuente de datos 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>
    <!-- las clases de acceso a la capa [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • líneas 5-19: el bean [dataSource] ahora hace referencia a la base de datos [SQL Express] [dbpersonnes], cuyo administrador es [sa] con la contraseña [msde]. El lector deberá modificar esta configuración según su propio entorno.
  • Línea 31: la clase [DaoImplCommon] es la clase de implementación de la capa [dao]

La línea 11 requiere una explicación:

            <value>jdbc:sqlserver://localhost\\SQLEXPRESS:4000;databaseName=dbpersonnes</value>
  • //localhost: indica que el servidor SQL Express se encuentra en el mismo equipo que nuestra aplicación Java
  • \\SQLEXPRESS: es el nombre de una instancia de SQL Server. Parece que pueden ejecutarse varias instancias al mismo tiempo. Por lo tanto, parece lógico nombrar la instancia a la que nos dirigimos. Este nombre se puede obtener a través de [SQL Server Configuration Manager], que normalmente se instala al mismo tiempo que SQL Express:

Image

Image

  • 4000: puerto de escucha de SQL Express. Esto depende de la configuración del servidor. Por defecto, funciona con puertos dinámicos, por lo que no se conocen de antemano. Por lo tanto, no se especifica ningún puerto en la URL JDBC. En este caso, hemos trabajado con un puerto fijo, el puerto 4000. Esto se consigue mediante la siguiente configuración:
  • El atributo dataBaseName establece la base de datos con la que se desea trabajar. Es la que se ha creado con el cliente EMS:

Image

Una vez realizadas estas modificaciones, podemos pasar a las pruebas.

20.4. Las pruebas de las capas [dao] y [service]

Las pruebas de las capas [dao] y [service] son las mismas que para la versión [Firebird]. Los resultados obtenidos son los siguientes:

Se observa que las pruebas se han superado con éxito con la implementación [DaoImplCommon]. No tendremos que derivar esta clase, como había sido necesario hacer con las versiones SGBD y [Firebird].

20.5. Pruebas de la aplicación [web]

Para probar la aplicación web con SGBD y [SQL Server Express], creamos un proyecto Eclipse [mvc-personnes-06B] de forma similar a como se hizo para crear los proyectos web anteriores.

Implementamos el proyecto web [mvc-personnes-05B] en Tomcat:

Se inicia el servidor Express SGBD SQL. El contenido de la tabla [PERSONNES] es entonces el siguiente:

Image

A continuación, se inicia Tomcat. Desde un navegador, accedemos a la URL [http://localhost:8080/mvc-personnes-06B]:

Image

Añadimos una nueva persona mediante el enlace [Ajout]:

Comprobamos la incorporación en la base de datos:

Image

Se invita al lector a realizar más pruebas con [modification, suppression].