Skip to content

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

20.1. SQL Server Express 数据库

在此版本中,我们将把人员列表导入 SQL Server Express 2005 数据库表中,该表可通过网址 [http://msdn.microsoft.com/vstudio/express/sql/] 获取。 下文中的屏幕截图均来自 EMS Manager Lite 客户端(适用于 SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager]), 这是 SGBD SQL Server Express 的免费管理客户端。

该数据库名为 [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) 中,第一个参数是主键的第一个可能值,第二个参数是生成数字时使用的增量。

表 [PERSONNES] 的内容可能如下:

Image

我们知道,当通过我们的 [dao] 层插入 [Personne] 对象时, 该对象的 [id] 字段在插入前等于 -1,插入后则取一个不同于 -1 的值,该值即为分配给插入到 [PERSONNES] 表中新行的一级主键。让我们通过一个示例来看看如何获取该值。

订单 SQL

SELECT @@IDENTITY

可用于获取表中字段 ID 的最新插入值。该查询应在插入操作完成后执行。 这与 SGBD、[Firebird] 和 [Postgres] 不同,后者是在插入之前查询所添加人员的primary key值, 但这与 SGBD 和 MySQL 中的主键生成机制类似。我们将在 [personnes-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)
-- ---------------------------------------
-- 主机     : (local)\SQLEXPRESS
-- 数据库:dbpersonnes


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

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

--
-- 索引定义: 
--

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]


该文件夹包含应用程序所需的存档文件:

值得注意的是,其中包含 [sqljdbc.jar]、SGBD 和 [Sql Server Express] 的 JDBC 驱动程序。所有这些存档文件均属于 Eclipse 项目的 Classpath


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>
    <!-- 别名类 [Personne] -->
    <typeAlias alias="Personne.classe" 
        type="istia.st.mvc.personnes.entites.Personne"/>
    <!-- 映射表 [PERSONNES] - 对象 [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>
    <!-- 所有人员的列表 -->
    <select id="Personne.getAll" resultMap="Personne.map" > select ID, VERSION, NOM, 
        PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES</select>
    <!-- 获取特定人员 -->
        <select id="Personne.getOne" resultMap="Personne.map" >select ID, VERSION, NOM, 
        PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES WHERE ID=#值#</select>
    <!-- 添加人员 -->
    <insert id="Personne.insertOne" parameterClass="Personne.classe">
        insert into 
        PERSONNES(VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS) 
        VALUES(#版本#, #姓#, #名#, #dateNaissance#, #已婚#, 
        #nbEnfants#) 
        <selectKey keyProperty="id">
            select @@IDENTITY as value
        </selectKey>         
    </insert>
    <!-- 更新人员信息 -->
    <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
        PERSONNES set VERSION=#版本#+1, NOM=#姓#, PRENOM=#名#, DATENAISSANCE=#dateNaissance#, 
        MARIE=#玛丽#, NBENFANTS=#nbEnfants# WHERE ID=#id# 以及 
        VERSION=#版本#</update>
    <!-- 删除人员 -->
    <delete id="Personne.deleteOne" parameterClass="int"> delete FROM PERSONNES WHERE 
        ID=#值# </delete>
    <!-- 获取最后插入人员的主键值 [id] -->
    <select id="Personne.getNextId" resultClass="int">select 
        LAST_INSERT_ID()</select>
</sqlMap>

其内容与 [personnes-firebird.xml] 相同,仅存在以下细微差异:

  • SQL 中的“Personne.insertOne”指令在第29-37行发生了变更:
  • 插入指令 SQL 在 SELECT 之前执行,后者将用于获取插入行主键的值
  • 插入语句 SQL 未为表 [PERSONNES] 的列 ID 提供值

这反映了我们在第 20.1 节中讨论过的插入示例。需要注意的是,这里出现了第 19.3 节中针对 MySQL 描述的不同线程同时插入的问题。

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

[dao]层的配置已调整为SGBD和[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>
    <!-- 数据源 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>
    <!-- 访问 [dao] 层的权限 -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • 第 5-19 行: Bean [dataSource] 现指向数据库 [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 Express 同时安装的 [SQL Server Configuration Manager] 获取:

Image

Image

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

Image

完成这些修改后,即可进行测试。

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

[dao] 和 [service] 这两款尿布的测试结果与 [Firebird] 版本相同。测试结果如下:

测试结果表明,[DaoImplCommon] 实现版本已成功通过测试。与 SGBD 和 [Firebird] 版本不同,我们无需对该类进行派生。

20.5. [web] 应用程序测试

为了测试基于 SGBD 和 [SQL Server Express] 的 Web 应用程序,我们将按照构建先前 Web 项目的方法,构建一个 Eclipse 项目 [mvc-personnes-06B]。

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

Express 服务器 SGBD SQL 已启动。此时表 [PERSONNES] 的内容如下:

Image

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

Image

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

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

Image

请读者进行其他测试:[modification, suppression]。