Skip to content

20. Webapplicatie MVC in een 3-tier-architectuur – Voorbeeld 6, SQL Server Express

20.1. De database SQL Server Express

In deze versie gaan we de lijst met personen opslaan in een databasetabel van SQL Server Express 2005, die beschikbaar is via de URL [http://msdn.microsoft.com/vstudio/express/sql/]. De onderstaande schermafbeeldingen zijn afkomstig uit de client EMS Manager Lite voor SQL Server Express [http://www.sqlmanager.net/fr/products/mssql/manager], een gratis beheerclient voor de SGBD SQL Server Express.

De database heet [dbpersonnes]. Deze bevat een tabel [PERSONNES]:

Image

De tabel [PERSONNES] bevat de lijst met personen die door de webapplicatie worden beheerd. Deze is opgebouwd met de volgende SQL-opdrachten:

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
  • regel 2: de primaire sleutel [ID] is van het type geheel getal. Het attribuut IDENTITY geeft aan dat als er een rij wordt ingevoegd zonder waarde voor de kolom ID van de tabel, SQL Express zelf een geheel getal voor deze kolom zal genereren. In IDENTITY(1, 1) is de eerste parameter de eerste mogelijke waarde voor de primaire sleutel, de tweede het increment dat wordt gebruikt bij het genereren van de getallen.

De tabel [PERSONNES] zou de volgende inhoud kunnen hebben:

Image

We weten dat bij het invoegen van een object [Personne] door onze laag [dao], het veld [id] van dit object vóór het invoegen gelijk is aan -1 en daarna een andere waarde dan -1 heeft, waarbij deze waarde de primaire sleutel is die is toegewezen aan de nieuwe rij die in de tabel [PERSONNES] is ingevoegd. Laten we aan de hand van een voorbeeld bekijken hoe we deze waarde kunnen achterhalen.

De opdracht SQL

SELECT @@IDENTITY

geeft de laatste waarde weer die in het veld ID van de tabel is ingevoerd. Deze moet na het invoegen worden verzonden. Dit verschilt van de opdrachten SGBD, [Firebird] en [Postgres], waarbij vóór het invoegen de waarde van de primaire sleutel van de toegevoegde persoon werd opgevraagd, maar het is vergelijkbaar met het genereren van de primaire sleutel in de SGBD en MySQL. We zullen dit gebruiken in het bestand [personnes-sqlexpress.xml], dat de opdrachten SQL verzamelt die naar de database zijn verzonden.

20.2. Het Eclipse-project van de lagen [dao] en [service]

Om de lagen [dao] en [service] van onze applicatie met de database [SQL Server Express] te ontwikkelen, gebruiken we het volgende Eclipse-project [mvc-personnes-06]:

Image

Het project is een eenvoudig Java-project, geen Tomcat-webproject.


Map [src]


Deze map bevat de broncode van de lagen [dao] en [service]:

Image

Alle bestanden met [sqlexpress] in hun naam kunnen al dan niet gewijzigd zijn ten opzichte van de versies voor Firebird, Postgres en MySQL. Hieronder beschrijven we alleen de bestanden die gewijzigd zijn.


Map [database]


Deze map bevat het script voor het aanmaken van de SQL Express-database met personen:

Image

-- SQL Manager 2005 Lite voor SQL Server (2.2.0.1)
-- ---------------------------------------
-- Host      : (lokaal)\SQLEXPRESS
-- Database: dbpersonnes


--
-- Structuur voor tabel 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

--
-- Gegevens voor tabel PERSO NNES (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

--
-- Definitie voor indexen: 
--

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

Map [lib]


Deze map bevat de bestanden die nodig zijn voor de toepassing:

Let op de aanwezigheid van de JDBC-driver [sqljdbc.jar] van SGBD en [Sql Server Express]. Al deze bestanden maken deel uit van Classpath van het Eclipse-project.


20.3. De laag [dao]

De laag [dao] is als volgt:

Image

We vermelden alleen wat er is veranderd ten opzichte van de versie [Firebird].

Het mappingbestand [personne-sqlexpress.xml] is als volgt:


<?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>
    <!-- aliasklasse [Personne] -->
    <typeAlias alias="Personne.classe" 
        type="istia.st.mvc.personnes.entites.Personne"/>
    <!-- toewijzingstabel [PERSONNES] - object [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>
    <!-- lijst van alle personen -->
    <select id="Personne.getAll" resultMap="Personne.map" > select ID, VERSION, NOM, 
        PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES</select>
    <!-- een specifieke persoon ophalen -->
        <select id="Personne.getOne" resultMap="Personne.map" >select ID, VERSION, NOM, 
        PRENOM, DATENAISSANCE, MARIE, NBENFANTS FROM PERSONNES WHERE ID=#waarde#</select>
    <!-- een persoon toevoegen -->
    <insert id="Personne.insertOne" parameterClass="Personne.classe">
        insert into 
        PERSONNES(VERSION, NOM, PRENOM, DATENAISSANCE, MARIE, NBENFANTS) 
        VALUES(#versie#, #achternaam#, #voornaam#, #dateNaissance#, #getrouwd met#, 
         #nbEnfants#) 
        <selectKey keyProperty="id">
            select @@IDENTITY as value
        </selectKey>         
    </insert>
    <!-- een persoon bijwerken -->
    <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
        PERSONNES set VERSION=#versie#+1, NOM=#achternaam#, PRENOM=#voornaam#, DATENAISSANCE=#dateNaissance#, 
        MARIE=#marie#, NBENFANTS=#nbEnfants# WHERE ID=#id# en 
        VERSION=#versie#</update>
    <!-- een persoon verwijderen -->
    <delete id="Personne.deleteOne" parameterClass="int"> delete FROM PERSONNES WHERE 
        ID=#waarde# </verwijderen>
    <!-- de waarde van de primaire sleutel [id] van de laatst toegevoegde persoon ophalen -->
    <select id="Personne.getNextId" resultClass="int">select 
        LAST_INSERT_ID()</select>
</sqlMap>

De inhoud is identiek aan die van [personnes-firebird.xml], op de volgende details na:

  • de opdracht SQL " Personne.insertOne " is gewijzigd in de regels 29-37:
  • de invoegopdracht SQL wordt uitgevoerd vóór de opdracht SELECT, waardoor de waarde van de primaire sleutel van de ingevoegde regel kan worden opgehaald
  • de invoegopdracht SQL heeft geen waarde voor de kolom ID van de tabel [PERSONNES]

Dit komt overeen met het invoegvoorbeeld dat we in paragraaf 20.1 hebben besproken. Merk op dat hier het probleem van gelijktijdige invoegingen door verschillende threads terugkomt, zoals beschreven voor MySQL in paragraaf 19.3.

De implementatieklasse [DaoImplCommon] van de laag [dao] is dezelfde als die van de drie voorgaande versies.

De configuratie van de laag [dao] is aangepast aan die van SGBD en [SQL Express]. Het configuratiebestand [spring-config-test-dao-sqlexpress.xml] ziet er dan ook als volgt uit:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- de gegevensbron 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>
    <!-- de toegangsklasse tot de laag [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • regels 5-19: de bean [dataSource] verwijst nu naar de database [SQL Express] [dbpersonnes], waarvan de beheerder [sa] is met het wachtwoord [msde]. De lezer past deze configuratie aan zijn eigen omgeving aan.
  • regel 31: de klasse [DaoImplCommon] is de implementatieklasse van de laag [dao]

Regel 11 verdient enige uitleg:

            <value>jdbc:sqlserver://localhost\\SQLEXPRESS:4000;databaseName=dbpersonnes</value>
  • //localhost: geeft aan dat de SQL Express-server zich op dezelfde machine bevindt als onze Java-toepassing
  • \\SQLEXPRESS: is de naam van een instantie van de SQL-server. Het lijkt erop dat er meerdere instanties tegelijkertijd kunnen draaien. Het lijkt dus logisch om de instantie waaraan we ons richten een naam te geven. Deze naam kan worden verkregen via [SQL Server Configuration Manager], dat normaal gesproken tegelijk met SQL Express wordt geïnstalleerd:

Image

Image

  • 4000: luisterpoort van SQL Express. Dit is afhankelijk van de configuratie van de server. Standaard werkt het met dynamische poorten, die dus niet van tevoren bekend zijn. Er wordt dan geen poort opgegeven in de URL JDBC. Hier hebben we gewerkt met een vaste poort, namelijk poort 4000. Dit wordt ingesteld via de configuratie:
  • Het attribuut dataBaseName bepaalt de database waarmee we willen werken. Dit is de database die is aangemaakt met de client EMS:

Image

Zodra deze wijzigingen zijn doorgevoerd, kunnen we overgaan tot het testen.

20.4. De tests van de lagen [dao] en [service]

De tests van de lagen [dao] en [service] zijn dezelfde als voor de versie [Firebird]. De verkregen resultaten zijn als volgt:

We zien dat de tests met de implementatie [DaoImplCommon] met succes zijn doorlopen. We hoeven deze klasse niet af te leiden, zoals wel nodig was bij de SGBD en [Firebird].

20.5. Testen van de applicatie [web]

Om de webapplicatie met SGBD en [SQL Server Express] te testen, bouwen we een Eclipse-project [mvc-personnes-06B] op, op dezelfde manier als bij de eerdere webprojecten.

We implementeren het webproject [mvc-personnes-05B] in Tomcat:

De SGBD SQL Express-server wordt gestart. De inhoud van de tabel [PERSONNES] is dan als volgt:

Image

Vervolgens wordt Tomcat gestart. Met een browser roepen we de URL [http://localhost:8080/mvc-personnes-06B] op:

Image

We voegen een nieuwe persoon toe via de link [Ajout]:

We controleren of de toevoeging in de database is gelukt:

Image

De lezer wordt uitgenodigd om nog meer tests uit te voeren: [modification, suppression].