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 provienen del cliente EMS Manager Lite para SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager], un cliente de administración gratuito para el SGBD SQL Server Express.

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

Image

La tabla [PERSONNES] contendrá la lista de personas administradas por la aplicación web. Se creó 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 para la columna ID de la tabla, SQL Express generará por sí mismo un número entero para esa 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 ese 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 conocer este valor.

El pedido SQL

SELECT @@IDENTITY

permite conocer el último valor insertado en el campo ID de la tabla. Debe emitirse después de 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 agregada 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 proyecto sencillo de 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 para crear 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 PERSONN ES (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 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 particular -->
        <select id="Personne.getOne" resultMap="Personne.map" >select ID, VERSION, NOM, 
        PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES WHERE ID=#valor#</select>
    <!-- agregar una persona -->
    <insert id="Personne.insertOne" parameterClass="Personne.classe">
        insert into 
        PERSONNES(VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS) 
        VALUES(#versión#, #apellido#, #nombre#, #dateNaissance#, #esposa#, 
         #nbEnfants#) 
        <selectKey keyProperty="id">
            select @@IDENTITY as value
        </selectKey>         
    </insert>
    <!-- actualizar una persona -->
    <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
        PERSONNES set VERSION=#versión#+1, NOM=#apellido#, 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# </eliminar>
    <!-- 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>

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

  • la orden SQL «Personne.insertOne» ha cambiado en las líneas 29 a 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 fila 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 comentamos en el párrafo 20.1. Cabe señalar que aquí se presenta el problema de inserciones simultáneas por parte de diferentes hilos, descrito para MySQL en el párrafo 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]. Por lo tanto, 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=dbpersonas</valor>
        </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 apunta a la base de datos [SQL Express] [dbpersonnes], cuyo administrador es [sa] con la contraseña [msde]. El lector deberá modificar esta configuración de acuerdo con 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 la misma máquina que nuestra aplicación Java
  • \\SQLEXPRESS: es el nombre de una instancia de SQL Server. Al parecer, 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 configura de la siguiente manera:
  • El atributo dataBaseName establece la base de datos con la que queremos trabajar. Es la que se creó 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 hacerlo con 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 de Eclipse [mvc-personnes-06B] de manera similar a la utilizada 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, solicitamos la URL [http://localhost:8080/mvc-personnes-06B]:

Image

Agregamos una nueva persona mediante el enlace [Ajout]:

Verificamos la adición en la base de datos:

Image

Se invita al lector a realizar otras pruebas con [modification, suppression].