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] — безкоштовного клієнта для адміністрування Postgres SGBD.

База даних має назву [dbpersonnes]. Вона містить таблицю [PERSONNES]:

Image

Таблиця [PERSONNES] міститиме список осіб, якими керує веб-додаток. Вона була створена за допомогою таких команд SQL:

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");
--
-- Дані для послідовності public «SEQ_ID» (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Коментарі
--
COMMENT ON SCHEMA public IS 'Standard public schema';

Папка [lib]


Ця папка містить архіви, необхідні для роботи програми:

Слід звернути увагу на наявність драйвера JDBC для Postgres 8.x (SGBD). Усі ці архіви входять до складу проекту Eclipse (Classpath).

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"=#значення# </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: біан [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].