Skip to content

18. تطبيق الويب MVC في بنية ثلاثية الطبقات – المثال 4، Postgres

18.1. قاعدة بيانات Postgres

في هذا الإصدار، سنقوم بتثبيت قائمة الأشخاص في جدول قاعدة بيانات Postgres 8.x [http://www.postgres.org]. فيما يلي، تأتي لقطات الشاشة من عميل EMS PostgreSQL Manager Lite [http://www.sqlmanager.net/fr/products/postgresql/manager]، وهو عميل إدارة مجاني لـ SGBD Postgres.

تسمى قاعدة البيانات [dbpersonnes]. وهي تحتوي على جدول [PERSONNES]:

Image

ستحتوي الجدولة [PERSONNES] على قائمة بالأشخاص الذين تديرهم تطبيق الويب. وقد تم إنشاؤها باستخدام الأوامر التالية:

CREATE TABLE "public"."PERSONNES" (
  "ID" INTEGER NOT NULL, 
  "VERSION" INTEGER NOT NULL, 
  "NOM" VARCHAR(30) NOT NULL, 
  "PRENOM" VARCHAR(30) NOT NULL, 
  "DATENAISSANCE" DATE NOT NULL, 
  "MARIE" BOOLEAN NOT NULL, 
  "NBENFANTS" INTEGER NOT NULL, 
  CONSTRAINT "PERSONNES_pkey" PRIMARY KEY("ID"), 
  CONSTRAINT "PERSONNES_chk_NBENFANTS" CHECK ("NBENFANTS" >= 0), 
  CONSTRAINT "PERSONNES_chk_NOM" CHECK (("NOM")::text <> ''::text), 
  CONSTRAINT "PERSONNES_chk_PRENOM" CHECK (("PRENOM")::text <> ''::text)
) WITH OIDS;

لن نركز على هذه الجدولة التي تشبه الجدولة [PERSONNES] من نوع Firebird التي تمت دراستها سابقًا. ومع ذلك، تجدر الإشارة إلى أن أسماء الأعمدة والجداول موضوعة بين علامتي اقتباس. علاوة على ذلك، فإن هذه الأسماء حساسة لحالة الأحرف. من الممكن أن يكون هذا النمط من عمل Postgres 8.x قابلاً للتكوين. لم أتعمق في هذه المسألة.

قد تحتوي الجدولة [PERSONNES] على المحتوى التالي:

Image

بالإضافة إلى الجدول [PERSONNES]، تحتوي قاعدة البيانات [dbpersonnes] على كائن يسمى تسلسل ويحمل الاسم [SEQ_ID]. يُصدر هذا المولد أرقامًا صحيحة متتالية سنستخدمها لإعطاء قيمة للمفتاح الأساسي [ID] للفئة [PERSONNES]. لنأخذ مثالاً لتوضيح كيفية عمله:

يمكن ملاحظة أن القيمة [Next value] للتسلسل [SEQ_ID] قد تغيرت (انقر عليها مرتين + F5 للتحديث):

 

الطلب SQL

SELECT nextval('"SEQ_ID"')

يسمح لنا بالحصول على القيمة التالية من التسلسل [SEQ_ID]. سنستخدمها في الملف [personnes-postgres.xml] الذي يجمع الأوامر SQL الصادرة على SGBD.

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

لتطوير الطبقات [dao] و [service] لتطبيقنا باستخدام قاعدة بيانات Postgres 8.x، سنستخدم مشروع Eclipse [spring-mvc-39] التالي:

Image

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


المجلد [src]


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

Image

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


مجلد [database]


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

Image

-- EMS PostgreSQL Manager Lite 3.1.0.1
-- ---------------------------------------
-- المضيف     : localhost
-- قاعدة البيانات: dbpersonnes



--
-- تعريف الدالة plpgsql_call_handler (OID = 17230) : 
--
...
--
-- هيكل الجدول PERSONNES (OID = 17254): 
--
CREATE TABLE "PERSONNES" (
    "ID" integer NOT NULL,
    "VERSION" integer NOT NULL,
    "NOM" varchar(30) NOT NULL,
    "PRENOM" varchar(30) NOT NULL,
    "DATENAISSANCE" date NOT NULL,
    "MARIE" boolean NOT NULL,
    "NBENFANTS" integer NOT NULL,
    CONSTRAINT "PERSONNES_chk_NBENFANTS" CHECK (("NBENFANTS" >= 0)),
    CONSTRAINT "PERSONNES_chk_NOM" CHECK ((("NOM")::text <> ''::text)),
    CONSTRAINT "PERSONNES_chk_PRENOM" CHECK ((("PRENOM")::text <> ''::text))
);
--
-- تعريف التسلسل SEQ_ID (OID = 17261): 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- بيانات الكتل (OID = 17254) (LIMIT 0,3)
--
INSERT INTO "PERSONNES" ("ID", "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES (1, 1, 'Major', 'Joachim', '1984-11-13', true, 2);
INSERT INTO "PERSONNES" ("ID", "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES (2, 1, 'Humbort', 'Mélanie', '1985-01-12', false, 1);
INSERT INTO "PERSONNES" ("ID", "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES (3, 1, 'Lemarchand', 'Charles', '1986-01-01', false, 0);
--
-- تعريف الفهرس PERSONNES_pkey (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- بيانات لتسلسل عام "SEQ_ID" (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- تعليقات
--
COMMENT ON SCHEMA public IS 'Standard public schema';

المجلد [lib]


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

تجدر الإشارة إلى وجود برنامج التشغيل jdbc الخاص بـ SGBD Postgres 8.x. جميع هذه الملفات هي جزء من Classpath لمشروع Eclipse.

18.3. الطبقة [dao]

الطبقة [dao] هي التالية:

Image

نقدم فقط التغييرات التي طرأت مقارنة بالإصدار [Firebird].

ملف التعيين [personne-postgres.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">

<!-- تنبيه - يتطلب Postgresql 8 التهجئة الدقيقة لأسماء الأعمدة
     et des tables ainsi que des guillemets autour de ces noms -->

    <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"> 
            <selectKey keyProperty="id">
                SELECT nextval('"SEQ_ID"') as value
            </selectKey> 
            insert into "PERSONNES"("ID", "VERSION", 
            "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS") VALUES(#id#, 
            #الإصدار#، #اللقب#، #الاسم الأول#، #dateNaissance#، #الزوجة#، #nbEnfants#) 
            </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>
    </sqlMap>

وهو يحتوي على نفس محتوى [personnes-firebird.xml] مع التفاصيل التالية:

  • أسماء الأعمدة والجداول موضوعة بين علامتي اقتباس، وهذه الأسماء حساسة لحالة الأحرف
  • تغير الترتيب SQL " Personne.insertOne " في الأسطر 34-41. تختلف طريقة إنشاء المفتاح الأساسي باستخدام Postgres عن تلك المستخدمة مع Firebird:
    • السطر 36: يوفر الترتيب SQL [SELECT nextval('"SEQ_ID"')] المفتاح الأساسي. صيغة [as value] إلزامية. يمثل [value] المفتاح الذي تم الحصول عليه. سيتم تعيين هذه القيمة إلى حقل الكائن [Personne] المحدد بواسطة السمة [keyProperty] (السطر 35)، وهو هنا الحقل [id].
    • يتم تنفيذ الأوامر SQL الخاصة بعلامة <insert> بالترتيب الذي تظهر به. لذلك يتم تنفيذ SELECT قبل INSERT. لذلك، عند إجراء عملية الإدراج، سيتم تحديث الحقل [id] الخاص بالكائن [Personne] بواسطة الأمر SQL SELECT.
    • السطور 38-40: إدراج الكائن [Personne]

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

تم تعديل تكوين الطبقة [dao] لتتوافق مع SGBD و [Postgres]. وبالتالي، فإن ملف التكوين [spring-config-test-dao-postgres.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>org.postgresql.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:postgresql:dbpersonnes</value>
        </property>
        <property name="username">
            <value>postgres</value>
        </property>
        <property name="password">
            <value>postgres</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-postgres.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] الآن إلى قاعدة البيانات [Postgres] [dbpersonnes] التي يكون مسؤولها هو [postgres] بكلمة مرور [postgres]. سيقوم القارئ بتعديل هذا التكوين وفقًا لبيئته الخاصة.
  • السطر 31: الفئة [DaoImplCommon] هي فئة تنفيذ الطبقة [dao]

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

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

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

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

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

لاختبار تطبيق الويب باستخدام SGBD و [Postgres]، نقوم بإنشاء مشروع Eclipse [mvc-personnes-04B] بطريقة مشابهة لتلك المستخدمة لإنشاء مشروع [mvc-personnes-03B] باستخدام قاعدة بيانات Firebird (انظر الفقرة 17.7). ومع ذلك، لا يتعين علينا إعادة إنشاء الأرشيفين [personnes-dao.jar] و [personnes-service.jar]. في الواقع، لم نقم بتعديل أي فئة فيما يتعلق بمشروع [mvc-personnes-03B]. ببساطة، يحتوي الأرشيف [personnes-dao.jar] على الفئة [DaoImplFirebird] التي أصبحت عديمة الفائدة.

Image

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

نحن جاهزون لإجراء اختبارات . يصبح محتوى الجدول [PERSONNES] كما يلي:

Image

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

Image

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

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

Image

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