Skip to content

18. 3 katmanlı mimaride MVC web uygulaması – Örnek 4, Postgres

18.1. Postgres veritabanı

Bu sürümde, kişi listesini 8.x [http://www.postgres.org] adlı bir Postgres veritabanı tablosuna kaydedeceğiz. Aşağıdaki ekran görüntüleri, EMS PostgreSQL Manager Lite [http://www.sqlmanager.net/fr/products/postgresql/manager] istemcisinden alınmıştır; bu, SGBD Postgres için ücretsiz bir yönetim istemcisidir.

Veritabanının adı [dbpersonnes]’tir. Bu veritabanında [PERSONNES] adlı bir tablo bulunmaktadır:

Image

[PERSONNES] tablosu, web uygulaması tarafından yönetilen kişilerin listesini içerecektir. Bu tablo, aşağıdaki SQL komutlarıyla oluşturulmuştur:

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;

Daha önce incelenen Firebird türü [PERSONNES] tablosuna benzer olan bu tablo üzerinde fazla durmayacağız. Ancak, sütun ve tablo adlarının tırnak işaretleri içinde yer aldığına dikkat edilmelidir. Ayrıca, bu adlar büyük/küçük harfe duyarlıdır. Postgres 8.x'in bu çalışma şeklinin yapılandırılabilir olması mümkündür. Bu konuyu derinlemesine araştırmadım.

[PERSONNES] tablosu aşağıdaki içeriğe sahip olabilir:

Image

[PERSONNES] tablosunun yanı sıra, [dbpersonnes] veritabanında “sekans” olarak adlandırılan ve [SEQ_ID] adlı bir nesne bulunmaktadır. Bu üretici, [PERSONNES] sınıfının [ID] birincil anahtarına değer atamak için kullanacağımız ardışık tamsayılar üretir. Nasıl çalıştığını açıklamak için bir örnek verelim:

[SEQ_ID] dizisindeki [Next value] değerinin değiştiğini görebiliriz (üzerine çift tıklayın + F5'e tıklayarak yenileyin):

 

SQL emri

SELECT nextval('"SEQ_ID"')

bu sayede [SEQ_ID] dizisinin aşağıdaki değerini elde etmemizi sağlar. Bunu, SGBD üzerinde yayınlanan SQL emirlerini bir araya getiren [personnes-postgres.xml] dosyasında kullanacağız.

18.2. [dao] ve [service] katmanlarının Eclipse projesi

Postgres veritabanı 8.x ile uygulamamızın [dao] ve [service] katmanlarını geliştirmek için aşağıdaki Eclipse projesi [spring-mvc-39]'i kullanacağız:

Image

Bu proje, bir Tomcat web projesi değil, basit bir Java projesidir.


[src] klasörü


Bu klasör, [dao] ve [service] katmanlarının kaynak kodlarını içerir:

Image

Adında [postgres] geçen tüm dosyalar, Firebird sürümüne göre değiştirilmiş olabilir veya olmayabilir. Aşağıda, değiştirilen dosyaları açıklıyoruz.


[database] klasörü


Bu klasör, kişi veritabanını oluşturmak için kullanılan Postgres komut dosyasını içerir:

Image

-- EMS PostgreSQL Manager Lite 3.1.0.1
-- ---------------------------------------
-- Ana B    ilgisayar : localhost
-- Veritabanı: dbpersonnes



--
-- plpgsql_call_handler işlevi için tanım (OID = 17230): 
--
...
--
-- PERSONNES tablosunun yapısı (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 dizisi tanımı (OID = 17261): 
--
CREATE SEQUENCE "SEQ_ID"
    INCREMENT BY 1
    NO MAXVALUE
    NO MINVALUE
    CACHE 1;
--
-- Blob verileri (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 dizini tanımı (OID = 17256): 
--
ALTER TABLE ONLY "PERSONNES"
    ADD CONSTRAINT "PERSONNES_pkey" PRIMARY KEY ("ID");
--
-- "SEQ_ID" adlı dizinin verileri (OID = 17261)
--
SELECT pg_catalog.setval('"SEQ_ID"', 37, true);
--
-- Yorumlar
--
COMMENT ON SCHEMA public IS 'Standard public schema';

[lib] klasörü


Bu klasör, uygulama için gerekli dosyaları içerir:

SGBD Postgres 8.x jdbc sürücüsünün de mevcut olduğu unutulmamalıdır. Tüm bu dosyalar, Eclipse projesinin Classpath paketinin bir parçasıdır.

18.3. [dao] katmanı

[dao] katmanı şu şekildedir:

Image

[Firebird] sürümüne göre sadece değişenleri sunuyoruz.

[personne-postgres.xml] eşleme dosyası şöyledir:


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

<!-- dikkat - PostgreSQL 8, sütun adlarının tam olarak doğru yazılmasını gerektirir
     et des tables ainsi que des guillemets autour de ces noms -->

    <sqlMap>
        <!-- [Personne] sınıfı takma adı -->
        <typeAlias alias="Personne.classe" 
            type="istia.st.mvc.personnes.entites.Personne"/>
        <!-- eşleme tablosu [PERSONNES] - nesne [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>
        <!-- tüm kişilerin listesi -->
        <select id="Personne.getAll" resultMap="Personne.map" > select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES"</select>
        <!-- belirli bir kişiyi bulma -->
        <select id="Personne.getOne" resultMap="Personne.map" >select "ID", 
            "VERSION", "NOM", "PRENOM", "DATENAISSANCE", "MARIE", "NBENFANTS" FROM 
            "PERSONNES" WHERE "ID"=#değer#</select>
        <!-- bir kişi ekle -->
        <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#, 
             #sürüm#, #soyadı#, #adı#, #dateNaissance#, #evli#, #nbEnfants#) 
            </insert>
        <!-- bir kişiyi güncelle -->
        <update id="Personne.updateOne" parameterClass="Personne.classe"> update 
            "PERSONNES" set "VERSION"=#sürüm#+1, "NOM"=#soyadı#, "PRENOM"=#adı#, 
            "DATENAISSANCE"=#dateNaissance#, "MARIE"=#marie#, "NBENFANTS"=#nbEnfants# 
            WHERE "ID"=#id# ve "VERSION"=#version#</update>
        <!-- bir kişiyi sil -->
        <delete id="Personne.deleteOne" parameterClass="int"> delete FROM 
            "PERSONNES" WHERE "ID"=#değer# </sil>
    </sqlMap>

Bu dosya, aşağıdaki ayrıntılar dışında [personnes-firebird.xml] ile aynı içeriğe sahiptir:

  • sütun ve tablo adları tırnak işaretleri içinde yer almaktadır ve bu adlarda büyük/küçük harf duyarlılığı vardır
  • SQL " Personne.insertOne " sırası 34-41. satırlarda değiştirilmiştir. Postgres ile birincil anahtarın oluşturulma şekli, Firebird'de kullanılan yöntemden farklıdır:
    • 36. satır: SQL [SELECT nextval('"SEQ_ID"')] sıralaması birincil anahtarı sağlar. [as value] sözdizimi zorunludur. [value], elde edilen anahtarı temsil eder. Bu değer, [keyProperty] özniteliği (35. satır) ile belirtilen [Personne] nesnesinin alanına atanacaktır; burada bu alan [id]'tir.
    • <insert> etiketindeki SQL komutları, karşılaşıldıkları sırayla yürütülür. Dolayısıyla SELECT, INSERT’ten önce gerçekleştirilir. Ekleme işlemi sırasında, [Personne] nesnesinin [id] alanı, SQL ve SELECT komutları tarafından güncellenmiş olacaktır.
    • 38-40. satırlar: [Personne] nesnesinin eklenmesi

[dao] katmanının [DaoImplCommon] uygulama sınıfı, [Firebird] sürümünde incelenen sınıftır.

[dao] katmanının yapılandırması, SGBD ve [Postgres]'e uyarlanmıştır. Dolayısıyla, [spring-config-test-dao-postgres.xml] yapılandırma dosyası şu şekildedir:


<?xml version="1.0" encoding="ISO_8859-1"?>
<!DOCTYPE beans SYSTEM "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- veri kaynağı 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>
    <!-- katmana erişim sınıfı [dao] -->
    <bean id="dao" class="istia.st.mvc.personnes.dao.DaoImplCommon">
        <property name="sqlMapClient">
            <ref local="sqlMapClient"/>
        </property>
    </bean>
</beans>
  • 5-19. satırlar: [dataSource] bean'i artık, yöneticisi [postgres] olan ve şifresi [postgres] olan [Postgres] ve [dbpersonnes] veritabanlarını işaret etmektedir. Okuyucu, bu yapılandırmayı kendi ortamına göre değiştirecektir.
  • 31. satır: [DaoImplCommon] sınıfı, [dao] katmanının uygulama sınıfıdır

Bu değişiklikler yapıldıktan sonra testlere geçilebilir.

18.4. [dao] ve [service] katmanlarının testleri

[dao] ve [service] katmanlarının testleri, [Firebird] sürümüyle aynıdır. Önce SGBD Postgres'i, ardından Eclipse testlerini çalıştıralım. Elde edilen sonuçlar şunlardır:

[DaoImplCommon] uygulamasında testlerin başarıyla geçtiği görülmektedir. SGBD ve [Firebird] sürümlerinde olduğu gibi bu sınıfı türetmemize gerek kalmayacaktır.

18.5. [web] uygulamasının testleri

SGBD ve [Postgres] ile web uygulamasını test etmek için, Firebird veritabanıyla [mvc-personnes-03B] projesini oluştururken kullandığımız yönteme benzer şekilde bir Eclipse [mvc-personnes-04B] projesi oluşturuyoruz (bkz. paragraf 17.7). Ancak, [personnes-dao.jar] ve [personnes-service.jar] arşivlerini yeniden oluşturmamıza gerek yoktur. Çünkü [mvc-personnes-03B] projesine göre hiçbir sınıfta değişiklik yapmadık. Sadece [personnes-dao.jar] arşivi, artık gereksiz hale gelen [DaoImplFirebird] sınıfını içeriyor.

Image

[mvc-personnes-04B] web projesini Tomcat ortamında devreye alıyoruz:

i testlerine hazırız. [PERSONNES] tablosunun içeriği şu şekildedir:

Image

Tomcat başlatıldı. Bir tarayıcı kullanarak [http://localhost:8080/mvc-personnes-04B] URL'sini çağırıyoruz:

Image

[Ajout] bağlantısıyla [Ajout] bağlantısı üzerinden yeni bir kişi ekliyoruz:

Veritabanında eklenen kişiyi kontrol ediyoruz:

Image

Okuyucunun başka testler yapması istenir: [modification, suppression].