Skip to content

20. 三层架构中的 MVC Web 应用程序 – 示例 6,SQL Server Express

20.1. SQL Server Express 数据库

在此版本中,我们将人员列表存储在 SQL Server Express 2005 数据库表中,该表可通过 URL [http://msdn.microsoft.com/vstudio/express/sql/] 访问。以下屏幕截图来自 EMS Manager Lite for SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager],这是一个用于 SQL Server Express 数据库管理系统(DBMS)的免费管理客户端。

该数据库名为 [dbpersonnes]。其中包含一个名为 [PERSONNES] 的表:

Image

[PERSONNES] 表将包含由 Web 应用程序管理的人员列表。该表是通过以下 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
  • 第 2 行:主键 [ID] 的类型为整数。IDENTITY 属性表示,如果插入一行时未为表的 ID 列指定值,SQL Express 将自动为该列生成一个整数。在 IDENTITY(1, 1) 中,第一个参数是主键的起始值,第二个参数是生成数字时使用的增量。

[PERSONS] 表可能包含以下内容:

Image

我们知道,当通过 [DAO] 层插入 [Person] 对象时,该对象的 [id] 字段在插入前等于 -1,插入后则取其他值;该值即为插入到 [PERSONNES] 表的新行所分配的主键。让我们通过一个示例来了解如何确定该值。

SQL语句

SELECT @@IDENTITY

会返回插入到表的 ID 字段中的最后一个值。该语句应在插入操作之后执行。这与 [Firebird] 和 [Postgres] 数据库管理系统不同,后者是在插入之前查询已添加记录的主键值,但它与 MySQL 数据库管理系统中的主键生成机制类似。 我们将在 [people-sqlexpress.xml] 文件中使用它,该文件包含在数据库上执行的 SQL 语句。

20.2. 用于 [DAO] 和 [service] 层的 Eclipse 项目

为了使用 [SQL Server Express] 数据库开发应用程序的 [DAO] 和 [service] 层,我们将使用以下 Eclipse 项目 [mvc-personnes-06]:

Image

该项目是一个简单的 Java 项目,而非 Tomcat Web 项目。


[src] 文件夹


该文件夹包含 [dao] 和 [service] 层的源代码:

Image

所有文件名中包含 [sqlexpress] 的文件,与 Firebird、Postgres 和 MySQL 版本相比,可能已被修改,也可能未被修改。下文仅描述那些已被修改的文件。


[database] 文件夹


此文件夹包含用于创建 SQL Express 数据库的脚本:

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

[lib] 文件夹


此文件夹包含应用程序所需的资源包:

请注意,这里包含用于数据库管理系统 [SQL Server Express] 的 JDBC 驱动程序 [sqljdbc.jar]。所有这些文件均属于 Eclipse 项目的类路径


20.3. [DAO] 层

[DAO] 层如下所示:

Image

我们仅突出显示了与 [Firebird] 版本相比的变更。

映射文件 [personne-sqlexpress.xml] 如下所示:


<?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>

这与 [people-firebird.xml] 的内容相同,仅有以下细微差异:

  • 第 29–37 行中的 SQL 语句 "Person.insertOne" 已发生变更:
  • SQL插入语句在SELECT语句之前执行,该SELECT语句用于检索已插入行主键的值
  • SQL 插入语句未为 [PERSONNES] 表中的 ID 列指定值

这反映了我们在第 20.1 节中讨论的插入示例。请注意,第 19.3 节中针对 MySQL 描述的不同线程同时插入的问题,在此处同样存在。

[dao] 层的实现类 [DaoImplCommon] 与前三个版本中的相同。

[dao]层的配置已针对[SQL Express]数据库管理系统进行了调整。因此,配置文件[spring-config-test-dao-sqlexpress.xml]如下所示:


<?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>
  • 第 5-19 行:[dataSource] Bean 现指向 [SQL Express] 数据库 [dbpersonnes],其管理员为 [sa],密码为 [msde]。读者应根据自身环境修改此配置。
  • 第 31 行:[DaoImplCommon] 类是 [dao] 层的实现类

第 11 行需要稍作说明:


            <value>jdbc:sqlserver://localhost\\SQLEXPRESS:4000;databaseName=dbpersonnes</value>
  • //localhost: 表示 SQL Express 服务器与我们的 Java 应用程序位于同一台机器上
  • \\SQLEXPRESS: 是 SQL Server 实例的名称。由于多个实例可以同时运行,因此明确指定要连接的具体实例名称是合理的。该名称可通过 [SQL Server 配置管理器] 获取,该工具通常随 SQL Express 一起安装:

Image

Image

  • 4000:SQL Express 的监听端口。该端口取决于服务器配置。默认情况下,它使用动态端口,这些端口无法提前确定。在这种情况下,JDBC URL 中不指定端口。在此,我们使用了固定端口 4000。该端口是通过配置设置的:
  • dataBaseName 属性指定要操作的数据库。这是使用 EMS 客户端创建的数据库:

Image

完成这些修改后,我们可以进行测试。

20.4. 针对 [dao] 和 [service] 层的测试

针对 [dao] 和 [service] 层的测试与 [Firebird] 版本的测试相同。所得结果如下:

我们可以看到,使用 [DaoImplCommon] 实现时测试已成功通过。与 [Firebird] 数据库管理系统(DBMS)的情况不同,我们无需派生该类。

20.5. [Web] 应用程序测试

为了使用 [SQL Server Express] 数据库管理系统测试 Web 应用程序,我们将按照与构建先前 Web 项目类似的方式,构建一个 Eclipse 项目 [mvc-personnes-06B]。

我们将 Web 项目 [mvc-personnes-05B] 部署到 Tomcat 中:

SQL Server Express 数据库管理系统已启动。此时 [PERSONNES] 表的内容如下:

Image

随后启动 Tomcat。使用浏览器访问 URL [http://localhost:8080/mvc-personnes-06B]:

Image

我们通过 [添加]链接添加了一位新用户:

我们在数据库中验证新增记录:

Image

欢迎读者进行其他测试 [编辑、删除]。