Skip to content

20. MVC-Webanwendung in einer 3-Tier-Architektur – Beispiel 6, SQL Server Express

20.1. Die SQL Server Express-Datenbank

In dieser Version speichern wir die Liste der Personen in einer SQL Server Express 2005-Datenbanktabelle, die unter der URL [http://msdn.microsoft.com/vstudio/express/sql/] verfügbar ist. Die folgenden Screenshots stammen aus dem EMS Manager Lite-Client für SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager], einem kostenlosen Verwaltungsprogramm für das SQL Server Express-DBMS.

Die Datenbank trägt den Namen [dbpersonnes]. Sie enthält eine Tabelle mit dem Namen [PERSONNES]:

Image

Die Tabelle [PERSONNES] enthält die Liste der Personen, die von der Webanwendung verwaltet werden. Sie wurde mit den folgenden SQL-Anweisungen erstellt:

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
  • Zeile 2: Der Primärschlüssel [ID] ist vom Typ Integer. Das IDENTITY-Attribut gibt an, dass SQL Express, wenn eine Zeile ohne Wert für die ID-Spalte der Tabelle eingefügt wird, selbst eine Ganzzahl für diese Spalte generiert. Bei IDENTITY(1, 1) ist der erste Parameter der erste mögliche Wert für den Primärschlüssel und der zweite der Schritt, der bei der Generierung der Zahlen verwendet wird.

Die Tabelle [PERSONS] könnte folgenden Inhalt haben:

Image

Wir wissen, dass beim Einfügen eines [Person]-Objekts über unsere [DAO]-Schicht das Feld [id] dieses Objekts vor dem Einfügen den Wert -1 hat und danach einen anderen Wert als -1; dieser Wert ist der Primärschlüssel, der der neuen, in die Tabelle [PERSONNES] eingefügten Zeile zugewiesen wird. Schauen wir uns ein Beispiel an, um zu sehen, wie wir diesen Wert ermitteln können.

Die SQL-Anweisung

SELECT @@IDENTITY

gibt den zuletzt in das ID-Feld der Tabelle eingefügten Wert zurück. Sie sollte nach dem Einfügen ausgeführt werden. Dies unterscheidet sich von den DBMS [Firebird] und [Postgres], bei denen der Primärschlüsselwert des hinzugefügten Datensatzes vor dem Einfügen abgefragt wurde, entspricht jedoch der Primärschlüsselgenerierung im MySQL-DBMS. Wir werden dies in der Datei [people-sqlexpress.xml] verwenden, die die in der Datenbank ausgeführten SQL-Anweisungen enthält.

20.2. Das Eclipse-Projekt für die [DAO]- und [Service]-Schichten

Um die [DAO]- und [Service]-Schichten unserer Anwendung mit der [SQL Server Express]-Datenbank zu entwickeln, verwenden wir das folgende Eclipse-Projekt [mvc-personnes-06]:

Image

Das Projekt ist ein einfaches Java-Projekt, kein Tomcat-Webprojekt.


Ordner [src]


Dieser Ordner enthält den Quellcode für die [dao]- und [service]-Schichten:

Image

Alle Dateien, deren Namen [sqlexpress] enthalten, können im Vergleich zu den Firebird-, Postgres- und MySQL-Versionen geändert worden sein oder auch nicht. Im Folgenden beschreiben wir nur diejenigen, die geändert wurden.


Ordner [database]


Dieser Ordner enthält das Skript zum Erstellen der SQL Express-Datenbank für Benutzer:

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]-Ordner


Dieser Ordner enthält die von der Anwendung benötigten Archive:

Beachten Sie das Vorhandensein des JDBC-Treibers [sqljdbc.jar] für das DBMS [SQL Server Express]. Alle diese Dateien sind Teil des Klassenpfads des Eclipse-Projekts.


20.3. Die [DAO]-Schicht

Die [DAO]-Schicht sieht wie folgt aus:

Image

Wir heben nur die Änderungen gegenüber der [Firebird]-Version hervor.

Die Mapping-Datei [personne-sqlexpress.xml] sieht wie folgt aus:


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

Dies ist derselbe Inhalt wie in [people-firebird.xml] mit den folgenden geringfügigen Unterschieden:

  • Die SQL-Anweisung „Person.insertOne“ wurde in den Zeilen 29–37 geändert:
  • Die SQL-Einsatzanweisung wird vor der SELECT-Anweisung ausgeführt, die den Primärschlüsselwert der eingefügten Zeile abruft
  • Die SQL-Einsatzanweisung gibt keinen Wert für die Spalte „ID“ in der Tabelle [PERSONNES] an

Dies entspricht dem Einfügebeispiel, das wir in Abschnitt 20.1 besprochen haben. Beachten Sie, dass das in Abschnitt 19.3 für MySQL beschriebene Problem gleichzeitiger Einfügungen durch verschiedene Threads auch hier auftritt.

Die Implementierungsklasse [DaoImplCommon] der [dao]-Schicht ist dieselbe wie in den drei vorherigen Versionen.

Die Konfiguration der [dao]-Schicht wurde an das DBMS [SQL Express] angepasst. Daher sieht die Konfigurationsdatei [spring-config-test-dao-sqlexpress.xml] wie folgt aus:


<?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>
  • Zeilen 5–19: Die [dataSource]-Bean verweist nun auf die [SQL Express]-Datenbank [dbpersonnes], deren Administrator [sa] ist und deren Passwort [msde] lautet. Der Leser sollte diese Konfiguration an seine eigene Umgebung anpassen.
  • Zeile 31: Die Klasse [DaoImplCommon] ist die Implementierungsklasse für die [dao]-Schicht

Zeile 11 bedarf einer Erläuterung:


            <value>jdbc:sqlserver://localhost\\SQLEXPRESS:4000;databaseName=dbpersonnes</value>
  • //localhost: gibt an, dass sich der SQL Express-Server auf demselben Rechner wie unsere Java-Anwendung befindet
  • \\SQLEXPRESS: ist der Name einer SQL Server-Instanz. Es scheint, dass mehrere Instanzen gleichzeitig ausgeführt werden können. Daher ist es sinnvoll, die spezifische Instanz zu benennen, auf die zugegriffen wird. Dieser Name kann über den [SQL Server Configuration Manager] ermittelt werden, der in der Regel zusammen mit SQL Express installiert wird:

Image

Image

  • 4000: Der Listening-Port von SQL Express. Dieser hängt von der Serverkonfiguration ab. Standardmäßig werden dynamische Ports verwendet, die im Voraus nicht bekannt sind. In diesem Fall wird in der JDBC-URL kein Port angegeben. Hier haben wir einen festen Port verwendet, nämlich Port 4000. Dieser wird über die Konfiguration festgelegt:
  • Das Attribut „dataBaseName“ gibt die Datenbank an, mit der Sie arbeiten möchten. Dies ist die Datenbank, die mit dem EMS-Client erstellt wurde:

Image

Nachdem diese Änderungen vorgenommen wurden, können wir mit dem Testen fortfahren.

20.4. Tests für die [dao]- und [service]-Schichten

Die Tests für die [dao]- und [service]-Schichten sind dieselben wie für die [Firebird]-Version. Die erzielten Ergebnisse lauten wie folgt:

Wir sehen, dass die Tests mit der [DaoImplCommon]-Implementierung erfolgreich bestanden wurden. Wir müssen diese Klasse nicht ableiten, wie es beim [Firebird]-DBMS erforderlich war.

20.5. [Web]-Anwendungstests

Um die Webanwendung mit dem [SQL Server Express]-DBMS zu testen, erstellen wir ein Eclipse-Projekt [mvc-personnes-06B] auf ähnliche Weise wie bei den vorherigen Webprojekten.

Wir stellen das Webprojekt [mvc-personnes-05B] in Tomcat bereit:

Das DBMS SQL Server Express wird gestartet. Der Inhalt der Tabelle [PERSONNES] sieht dann wie folgt aus:

Image

Anschließend wird Tomcat gestartet. Über einen Browser rufen wir die URL [http://localhost:8080/mvc-personnes-06B] auf:

Image

Wir fügen über den Link [Hinzufügen] eine neue Person hinzu:

Wir überprüfen den Eintrag in der Datenbank:

Image

Der Leser ist eingeladen, weitere Tests durchzuführen [Bearbeiten, Löschen].