Skip to content

20. تطبيق الويب MVC في بنية ثلاثية الطبقات – المثال 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] على قائمة بالأشخاص الذين تديرهم تطبيق الويب. وقد تم إنشاؤها باستخدام الأوامر التالية 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

نعلم أنه عند إدراج كائن [Personne] بواسطة طبقتنا [dao]، يكون الحقل [id] لهذا الكائن مساوياً لـ -1 قبل الإدراج ويكون له قيمة مختلفة عن -1 بعد ذلك، وتكون هذه القيمة هي المفتاح الأساسي المخصص للسطر الجديد الذي تم إدراجه في الجدول [PERSONNES]. لنرى من خلال مثال كيف يمكننا معرفة هذه القيمة.

الطلب SQL

SELECT @@IDENTITY

معرفة آخر قيمة تم إدخالها في الحقل ID في الجدول. ويجب إصدارها بعد الإدخال. وهذا يختلف عن SGBD و [Firebird] و [Postgres] حيث كنا نطلب قيمة المفتاح الأساسي للشخص المضاف قبل الإدراج، ولكن هذا مشابه لتوليد المفتاح الأساسي في SGBD و MySQL. سنستخدمه في الملف [personnes-sqlexpress.xml] الذي يجمع الأوامر SQL التي تم إصدارها على قاعدة البيانات.

20.2. مشروع Eclipse للطبقات [dao] و [service]

لتطوير الطبقات [dao] و [service] لتطبيقنا باستخدام قاعدة البيانات [SQL Server Express]، سنستخدم مشروع Eclipse [mvc-personnes-06] التالي:

Image

المشروع هو مشروع Java بسيط، وليس مشروع ويب Tomcat.


مجلد [src]


يحتوي هذا الملف على أكواد المصدر للطبقات [dao] و [service]:

Image

قد تكون جميع الملفات التي تحتوي على [sqlexpress] في اسمها قد خضعت أو لم تخضع لتعديل مقارنة بإصدارات Firebird و Postgres و MySQL. فيما يلي، نصف فقط تلك التي تم تعديلها.


المجلد [database]


يحتوي هذا الملف على البرنامج النصي لإنشاء قاعدة بيانات SQL Express للأشخاص:

Image

-- SQL Manager 2005 Lite لخادم SQL (2.2.0.1)
-- ---------------------------------------
-- المضيف     : (محلي)\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]


يحتوي هذا الملف على الأرشيفات اللازمة للتطبيق:

تجدر الإشارة إلى وجود برنامج التشغيل jdbc [sqljdbc.jar] من SGBD [Sql Server Express]. جميع هذه الملفات هي جزء من Classpath لمشروع 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>
    <!-- اسم مستعار لفئة [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=#marie#، NBENFANTS=#nbEnfants# WHERE ID=#id# و 
        VERSION=#version#</update>
    <!-- حذف شخص -->
    <delete id="Personne.deleteOne" parameterClass="int"> delete FROM PERSONNES WHERE 
        ID=#value# </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 ليس له قيمة في العمود ID من الجدول [PERSONNES]

وهذا يعكس مثال الإدراج الذي علقنا عليه في الفقرة 20.1. وتجدر الإشارة إلى أننا نجد هنا مشكلة الإدراجات المتزامنة بواسطة خيوط مختلفة الموصوفة لـ MySQL في الفقرة 19.3.

فئة التنفيذ [DaoImplCommon] للطبقة [dao] هي نفس فئة الإصدارات الثلاثة السابقة.

تم تكييف تكوين الطبقة [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. يبدو أنه يمكن تشغيل عدة مثيلات في نفس الوقت. لذلك يبدو من المنطقي تسمية المثيل الذي نتعامل معه. يمكن الحصول على هذا الاسم من خلال [SQL Server Configuration Manager] الذي يتم تثبيته عادةً في نفس الوقت مع SQL Express:

Image

Image

  • 4000: منفذ الاستماع لـ SQL Express. وهذا يعتمد على تكوين الخادم. بشكل افتراضي، يعمل مع منافذ ديناميكية، وبالتالي غير معروفة مسبقًا. لذلك لا يتم تحديد منفذ في عنوان URL JDBC. هنا، عملنا مع منفذ ثابت، وهو المنفذ 4000. يتم الحصول عليه من خلال التكوين:
  • يحدد السمة dataBaseName قاعدة البيانات التي نريد العمل بها. وهي القاعدة التي تم إنشاؤها باستخدام العميل EMS:

Image

بعد إجراء هذه التعديلات، يمكننا الانتقال إلى الاختبارات.

20.4. اختبارات الطبقات [dao] و [service]

اختبارات الطبقات [dao] و [service] هي نفسها التي أجريت على الإصدار [Firebird]. وفيما يلي النتائج التي تم الحصول عليها:

يلاحظ أن الاختبارات قد نجحت مع تطبيق [DaoImplCommon]. لن نضطر إلى اشتقاق هذه الفئة كما كان ضروريًا مع SGBD و [Firebird].

20.5. اختبارات تطبيق [web]

لاختبار تطبيق الويب باستخدام SGBD و [SQL Server Express]، نقوم بإنشاء مشروع Eclipse [mvc-personnes-06B] بطريقة مماثلة لتلك المستخدمة في إنشاء مشاريع الويب السابقة.

نقوم بنشر مشروع الويب [mvc-personnes-05B] داخل Tomcat:

يتم تشغيل خادم Express SGBD SQL. ويكون محتوى الجدول [PERSONNES] كما يلي:

Image

يتم تشغيل Tomcat بدوره. باستخدام متصفح، نطلب عنوان URL [http://localhost:8080/mvc-personnes-06B]:

Image

نضيف شخصًا جديدًا عبر الرابط [Ajout]:

نتحقق من الإضافة في قاعدة البيانات:

Image

يُطلب من القارئ إجراء اختبارات أخرى [modification, suppression].