Bu makalemizde, pg modülüyle Node üzerinden PostgreSQL ile çalışma konusuna göz gezdireceğiz.

Makaleye başlamadan önce, Kahve Molası bölümündeki "PostgreSQL ve pgAdmin Kurulumu" makalesini okuduğunu ve makinene PostgreSQL'i kurduğunu varsayarak devam ediyorum.

Öncelikle şunu söylemem gerekiyor ki PostgreSQL, RDBMS (İlişkisel veritabanı yönetim sistemleri) arasında olağanüstü bir ürün. Sadece Open Source ve Cross Platform olduğu için söylemiyorum. MySQL, MSSQL ve hatta Oracle'dan beklediklerimizi verdiği gibi beklemediklerimizi de veriyor. Örneğin sütunlara verilebilen dizi tipleri ile bir sütuna birden fazla değer saklayabildiğimiz gibi, JSON veri tipi sayesinde çok daha kompleks nesnelerimizi de tek bir sütuna kaydedebiliyoruz. Hatta bu kadarla da kalmıyor, JSON olarak kaydedilmiş nesnelerimizin alt bileşenlerine göre "WHERE" çekebiliyor, bir de bu alt bileşenler için indeks de ekleyebiliyoruz.

Tüm bu saydığım özellikler sebebiyle PostgreSQL'i Transactional NoSQL olarak kullanan kişiler biliyorum. Uzun lafın kısası, eğer projelerinde henüz PostgreSQL'e şans vermediysen, vermeni şiddetle tavsiye ediyor ve reklamların ardından tekrar makaleye dönüyorum.



pg modülü, NPM'deki diğer pek çok modül tarafından kullanılan ve sadece sorgularımızı PostgreSQL üzerinde çalıştırmamızı sağlayan, çok alt seviye bir modül. Öyle ki, Transaction açma veya commit etmek için bile hazır bir fonksiyonu bulunmuyor. Bunun yerine, sorguları çalıştırmak için kullanılan "query" fonksiyonuna string şekline "COMMIT" kelimesini geçmemiz gerekiyor.

Yani bu modülü, pgAdmin'e yazdığın sorguları çalıştırmak için kullanılan bir ara kütüphane olarak görebilirsin. Hal böyle olunca, modül oldukça hızlı çalışıyor ve PostgreSQL üzerinde dilediğimiz her işlemi yapmamıza imkan tanıyor. Bu yüzden de pek çok diğer kütüphane için baz teşkil ediyor.

Not : Bu notu, "Yıl olmuş 2014, ne sorgusu, ORM nerede?" diyen haklı ama sabırsız kardeşlerim için yazıyorum. ORM konusunu, Node.JS sınıfındaki bir sonraki makalede inceleyeceğiz inşallah.

Örneklere başlamadan önce, makale boyunca kullanabileceğimiz kullanıcıyı ve veritabanını oluşturarak işe başlayalım. İlk olarak pgAdmin arayüzünü açalım ve kurulum sırasında oluşturduğumuz kullanıcı ile local veritabanına bağlanalım. Ardından "postgre" isimli veritabanı seçili iken üst taraftaki araç çubuğundan "SQL" butonuna tıklayalım. Böylece karşımıza, SQL ifadelerini çalıştırabileceğimiz aşağıdaki gibi bir ekran açılacaktır.



Buraya aşağıdaki komutu yazarak üst taraftaki yeşil Play düğmesine tıklayalım.

CREATE ROLE testuser LOGIN PASSWORD 'testpass' SUPERUSER REPLICATION VALID UNTIL 'infinity';

Böylece "testuser" isimli ve şifresi "testpass" olan, superuser yetkilerine sahip bir kullanıcı oluşturmuş olduk. Bu kadar yetkiye normalde elbette gerek yok ama burada tüm örneklerde test amaçlı kullanabileceğimiz bir kullanıcı oluşturduk.

Sıra veritabanına geldi. Veritabanını grafik arabirim üzerinden de kolaylıkla oluşturabiliriz ancak daha hızlı olması açısından Script ile oluşturma yoluna gideceğiz.  Aynı pencerenin içeriğini temizleyip aşağıdaki Script'i çalıştıralım.

CREATE DATABASE testdb WITH ENCODING='UTF8' OWNER=testuser CONNECTION LIMIT=-1;

Artık "testdb" isimli boş bir veritabanımız da var. Şimdi bu veritabanını seçelim ve seçili durumda iken yine yukarıdaki SQL butonuna basınca açılan pencere vasıtasıyla, aşağıdaki Script'i çalıştıralım ve veritabanına iki adet tablo ekleyelim.

CREATE TABLE public.table1
(
   column1 serial,
   column2 text NOT NULL,

   CONSTRAINT pk_table1 PRIMARY KEY (column1)
)
WITH (
  OIDS = FALSE
);


CREATE TABLE public.table2
(
   column3 serial,
   column1 integer NOT NULL,
   column4 text NOT NULL,

   CONSTRAINT pk_table2 PRIMARY KEY (column3),
   CONSTRAINT fk_table2_column1 FOREIGN KEY (column1) REFERENCES table1 (column1) ON UPDATE RESTRICT ON DELETE RESTRICT
)
WITH (
  OIDS = FALSE
);

Tüm bunlardan sonra, aşağıdaki gibi bir veritabanına sahip olmuş olman gerekiyor.



Eh, JavaScript'ten yeterince ayrı kaldık. Eve dönme vaktidir. Bu makalemizde,  pg modülü üzerinden yukarıda oluşturduğumuz veritabanına bağlanacak ve iki tablomuz üzerinde de çeşitli sorgular çalıştıracağız.

Bu modülü kullanmak için, ilk olarak masaüstüne "PGTest" isimli bir klasör açıp içerisine "package.json" dosyasını aşağıdaki gibi hazırlayalım.

{
    "name": "PGTest",
    "version": "1.0.0",
    "dependencies": {
        "pg": "3.0.3"
    }
}

Ardından, asıl kodumuzu içeren "program.js" dosyamızı aşağıdaki gibi hazırlayalım.

"use strict";

var pg = require("pg");

var client = new pg.Client("postgres://testuser:testpass@localhost/testdb");

client.connect(function (err) {
    if (err) {
        return console.error("Bağlantı sırasında hata oluştu : ", err);
    }

    client.query("INSERT INTO table1(column2) VALUES ('Ali');", function (err, result) {
        if (err) {
            return console.error("Sorgu çalıştırılırken hata oluştu", err);
        }

        console.log("OK");

        client.end();
    });
});

Kodun içeriği oldukça basit.  İlk olarak pg modülüne referans veriyoruz. Ardından bu nesnenin "Client" sınıfına bir ConnectionString geçiyoruz. Geriye dönen nesne, bizim veritabanına bağlanmamız ve sorguları çalıştırmamıza yarıyor.

Öncelikle "client" nesnesinin asenkron çalışan "connect" fonksiyonu vasıtasıyla veritabanına bağlantı açıyoruz. Buradan dönen "err" parametresi boşsa, bağlantımız başarıyla sağlandı demektir. Ardından aynı "client" nesnesinin asenkron çalışan "query" fonksiyonuna istediğimiz SQL sorgusunu geçiyoruz. Buradan cevap olarak fonksiyon içerisinde iki parametre dönüyor. İlk parametremiz, yine işlemin başarılı sonuçlanıp sonuçlanmadığını gösteriyor. İkinci parametre ise, sorgudan dönen veriyi saklıyor ki, bu örneğimizde geriye veri dönmüyoruz, bunu bir sonraki örneğimizde göreceğiz.

Son olarak "client" nesnesinin "end" fonksiyonu ile veritabanı bağlantımızı kapatıyoruz.

Sıra geldi uygulamayı çalıştırmaya. İlk olarak gerekli modülü indirmek için terminal (Linux veya Mac OS) veya command prompt (Windows) penceresinden "PGTest" isimli klasöre girip aşağıdaki komutu çalıştıralım.

npm install

Böylece pg modülü için gerekli olan dosyalar inmiş olacak. Ardından aşağıdaki komutla uygulamamızı ayağa kaldıralım.

node program.js

Eğer her şey yolunda gittiyse, aşağıdaki gibi bir ekranla karşılaşmış olman gerekiyor.



Ekranda yazdığına göre INSERT işlemimiz başarılı olmuş. Bir de veritabanından kontrol edelim bakalım.  Bunun için pgAdmin uygulamasından testdb > schemas > public > tables > table1 üzerine sağ tuşa basıp "View Top 100" seçeneğini seçebiliriz.  Bu durumda aşağıdaki gibi bir ekranla karşılaşıyor olman gerekiyor. 


Böylece ilk INSERT işlemimizi tamamlamış olduk. Bir de SELECT işlemine bakalım. Bunun için "program.js" dosyasının içeriğini aşağıdaki gibi değiştirelim.

"use strict";

var pg = require("pg");

var client = new pg.Client("postgres://testuser:testpass@localhost/testdb");

client.connect(function (err) {
    if (err) {
        return console.error("Bağlantı sırasında hata oluştu : ", err);
    }

    client.query("SELECT * FROM table1;", function (err, result) {
        if (err) {
            return console.error("Sorgu çalıştırılırken hata oluştu", err);
        }

        console.log(result.rows[0].column1);
        console.log(result.rows[0].column2);

        client.end();
    });
});


Programı çalıştırdığın takdirde, aşağıdaki gibi bir ekranla karşılaşmış olman gerekiyor.


Koda baktığımızda çok bir değişiklik gözükmüyor. Sadece SQL ifadesi olarak INSERT yerine SELECT sorgusu yazdık. Bir de bu kez, dönen "result" parametresinin içerisindeki "rows" dizisinden faydalandık. "result.rows" dizisini, .Net'de eskiden kullanılan Typed DataSet gibi düşünebiliriz. Burada "result.rows" dizisinin içerisinde, sorgudan dönen tüm satırlar bulunuyor. Biz burada, 1. satırdaki "column1" ve "column2" sütunlarını ekrana yazdırdık.

Bir de Transaction Scope içerisinde, birden fazla işlem yapmaya göz atalım. "program.js" dosyasının içeriğini aşağıdaki gibi değiştirelim.

"use strict";

var pg = require("pg");

var client = new pg.Client("postgres://testuser:testpass@localhost/testdb");

client.connect(function (err) {
    if (err) {
        return console.error("Bağlantı sırasında hata oluştu : ", err);
    }

    client.query("BEGIN TRANSACTION", function (err, result) {
        if (err) {
            return console.error("Sorgu çalıştırılırken hata oluştu", err);
        }

        client.query("INSERT INTO table1(column2) VALUES ('Veli');", function (err, result) {
            if (err) {
                return console.error("Sorgu çalıştırılırken hata oluştu", err);
            }

            client.query("INSERT INTO table2(column1, column4) VALUES (2, 'Ayse');", function (err, result) {
                if (err) {
                    return console.error("Sorgu çalıştırılırken hata oluştu", err);
                }

                client.query("COMMIT;", function (err, result) {
                    if (err) {
                        return console.error("Sorgu çalıştırılırken hata oluştu", err);
                    }

                    console.log("OK");

                    client.end();
                });
            });
        });
    });
});


Not : Bu notu yukarıdaki, tutmasak sağa doğru gidecekmiş gibi gözüken koddan rahatsızlık duyanlar için yazıyorum (Rahatsızlık duymayanlar da kendisini gözden geçirsin). Bu tarz rahatsız edici durumlarla karşılaşmamak için Promise denen yöntemi kullanmak gerekiyor. Bu konu hakkında inşallah yakında Object Oriented JavaScript kategorisinde bir makale yazacağım. Ancak bunun öncesinde Promise içeren bir kod yazmak istemedim.


Programı çalıştırdığın takdirde, aşağıdaki gibi bir ekranla karşılaşmış olman gerekiyor.


Ne güzel, "OK" diyor. Bir de veritabanından tabloları kontrol edelim bakalım. An itibariyle tabloların aşağıdaki gibi olması gerekiyor.

table1


table2


Gelelim kodun içeriğine. Veritabanına bağlantı açan kısma kadar bir farklılık yok. Ardından ilk "query" çağrımızda "BEGIN TRANSACTION" sorgusunu gönderiyoruz. Bu sorgu, kullandığımız "client" nesnesinin bağlantısı kapanana ya da biz iptal edene kadar geçerli olacak bir Transaction açıyor. Böylece, bundan sonra göndereceğimiz sorgular aynı Transaction Scope içerisinde çalışacaklar.

Ardından ikinci bir "query" fonksiyonu çağırımı ile ilk INSERT sorgumuzu gönderiyoruz. Problem olmaması durumunda da ikinci "query" fonksiyonu çağırımı ile ikinci INSERT sorgumuzu gönderiyoruz. Şimdiye kadar yapılan INSERT işlemlerimiz başarılı oldu ancak henüz Transaction'ı Commit etmediğimiz için, veritabanını açtığımızda bu işlemleri göremeyiz.

Sıra geldi Commit işlemine. Bunun için tek yapmamız gereken, "query" fonksiyonuna "COMMIT" SQL sorgusunu geçmek. Böylece "BEGIN TRANSACTION" komutu ile başlamış olan Transaction Scope boyunca gerçekleştirilen işlemler veritabanına Commit edilmiş oluyor.

İşin aslı bundan sonrası, standart SQL ifadelerinin "query" fonksiyonu ile işletilmesinden ibaret ama son örneğimizi de JOIN üzerine yapalım. "program.js" dosyasının içeriğini aşağıdaki gibi değiştirelim.

"use strict";

var pg = require("pg");

var client = new pg.Client("postgres://testuser:testpass@localhost/testdb");

client.connect(function (err) {
    if (err) {
        return console.error("Bağlantı sırasında hata oluştu : ", err);
    }

    client.query("SELECT * FROM table1 JOIN table2 ON table1.column1 = table2.column1 WHERE table1.column1 = 2;", function (err, result) {
        if (err) {
            return console.error("Sorgu çalıştırılırken hata oluştu", err);
        }

        console.log(result.rows[0].column1);
        console.log(result.rows[0].column2);
        console.log(result.rows[0].column3);
        console.log(result.rows[0].column4);

        client.end();
    });
});

Programı çalıştırdığın takdirde, aşağıdaki gibi bir ekranla karşılaşmış olman gerekiyor.


Bu örneğimizde de farklı bir durum yok. Diğer örneklerden farklı olarak, bu kez JOIN içeren bir SQL ifadesini çalıştırdık ve her iki tablodan da gelen sonuçları ekrana yazdırdık.

Açıkçası bundan sonrasında söylenebilecek çok da fazla bir şey yok. Bilinen SQL ifadelerini "query" fonksiyonu vasıtasıyla çalıştırarak, ihtiyaca göre yeni tablo, index vs. de oluşturabiliriz, standart CRUD işlemlerini de gerçekleştirebiliriz, maintenance için gerekli olan adımları da işletebiliriz. Sonuçta "pg" modülünün tek yaptığı, veritabanına bağlanarak bize sorguları işletebileceğimiz bir uçbirim sağlamak. Sonrası bize kalmış.

Makalemizin sonuna gelmişken, öğrendiklerimizi özetleyelim.
  • pg modülü sayesinde, Node üzerinden PostgreSQL veritabanına bağlanarak istediğimiz SQL sorgularını çalıştırabiliriz.
  • pg modülü çok alt seviye bir modüldür. ORM türevi herhangi bir özelliği yoktur. Sadece verdiğimiz sorguları işletir. Bu yüzden oldukça hızlı çalışır ve diğer pek çok modül için baz teşkil eder.
  • pg modülü ile öncelikle ConnectionString geçtiğimiz bir "Client" sınıfının örneği üzerinden "connect" fonksiyonu ile veritabanına bağlanır ve sonrasında ihtiyacımız kadarınca "query" fonksiyonu vasıtasıyla SQL sorgularımızı çalıştırırız.
  • Çalıştırdığımız SQL sorgusunda geriye bir değer dönüyorsa, bu değer "result" parametresi içerisinde bize ulaşır.
  • Veritabanı ile bağlantımızı kesmek için "client" nesnesinin "end" fonksiyonunu çağırırız.
  • Node'daki pek çok modül gibi, pg modülü fonksiyonları da asenkron olarak çalışır.


Yorum Gönder

 
Top