Gereksinimler ve Ön Hazırlık
Bu eğitimde PostgreSQL veya MySQL gibi ilişkisel veritabanı yönetim sistemlerini (RDBMS) temel alacağız. Çalışmalarınızı gerçekleştirmek için aşağıdaki araçlara sahip olduğunuzdan emin olun:
- Güncel bir SQL veritabanı sunucusu (PostgreSQL 16+ veya MySQL 8.4+ önerilir).
- Veritabanı yönetimi için bir arayüz (DBeaver, pgAdmin veya MySQL Workbench).
- Temel düzeyde SQL sözdizimi bilgisi.
Kurulumlarınızı tamamladıktan sonra, veritabanı seviyesinde kısıtlamaların neden uygulama katmanından daha güvenli olduğunu anlamak önemlidir. Uygulama kodunuz değişse bile, veritabanı kısıtlamaları her zaman aktif kalır.
Temel Veri Kısıtlama Türleri ve Kullanımı
SQL veritabanlarında veri bütünlüğünü sağlamak için kullanılan temel kısıtlamalar; NOT NULL, UNIQUE, PRIMARY KEY, FOREIGN KEY, CHECK ve DEFAULT olarak sıralanır. Her biri farklı bir doğrulama senaryosuna hizmet eder.
Aşağıdaki örnekte, bir kullanıcı tablosu oluştururken temel kısıtlamaların nasıl tanımlandığını görebilirsiniz:
CREATE TABLE kullanicilar (
id SERIAL PRIMARY KEY,
kullanici_adi VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
yas INT CHECK (yas >= 18),
kayit_tarihi TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Bu kod bloğunda, NOT NULL ile alanın boş geçilemeyeceğini, UNIQUE ile verinin tekrarlanamayacağını ve CHECK ile yaşın 18'den küçük olamayacağını garanti altına aldık.
CHECK Kısıtlaması ile Mantıksal Veri Doğrulama
CHECK kısıtlaması, belirli bir sütundaki verinin mantıksal bir koşulu karşılamasını sağlar. Örneğin, bir ürünün stok miktarının negatif olmaması veya bir fiyatın sıfırdan büyük olması gibi durumlar için idealdir.
ALTER TABLE urunler
ADD CONSTRAINT fiyat_pozitif_olmalidir
CHECK (fiyat > 0);
Bu komut, urunler tablosuna bir kısıtlama ekler. Eğer bir geliştirici yanlışlıkla negatif bir fiyat girmeye çalışırsa, veritabanı motoru işlemi reddedecektir. Bu, uygulama tarafında oluşabilecek hatalı veri girişlerini anında engeller.
İlişkisel Veri Bütünlüğü İçin Foreign Key (Yabancı Anahtar)
İlişkisel veritabanlarında tablolar arası bağlantıların doğruluğu FOREIGN KEY ile sağlanır. Bu kısıtlama, bir tabloda bulunan verinin, başka bir tablodaki geçerli bir kayda referans vermesini zorunlu kılar.
CREATE TABLE siparisler (
siparis_id SERIAL PRIMARY KEY,
kullanici_id INT,
CONSTRAINT fk_kullanici
FOREIGN KEY(kullanici_id)
REFERENCES kullanicilar(id)
ON DELETE CASCADE
);
Burada ON DELETE CASCADE kullanımı, bir kullanıcı silindiğinde ona bağlı tüm siparişlerin de otomatik olarak silinmesini sağlar. Bu, veritabanında "yetim veri" (orphan data) kalmasını önleyen kritik bir mekanizmadır.
Veritabanı Kısıtlamaları Karşılaştırma Tablosu
| Kısıtlama Türü | Amacı | Avantajı |
|---|---|---|
| NOT NULL | Boş değer girişini engeller | Veri eksikliğini önler |
| UNIQUE | Tekrar eden veriyi engeller | Benzersizlik sağlar |
| CHECK | Mantıksal koşul doğrular | İş kuralı uygular |
| FOREIGN KEY | Tablo ilişkisini korur | Referans bütünlüğü sağlar |
Güvenlik ve Performans İçin Kritik Uyarılar
Dikkat: Veritabanı kısıtlamaları, SQL Injection gibi saldırılara karşı doğrudan bir savunma mekanizması değildir. SQL Injection saldırılarını önlemek için mutlaka parametreli sorgular (prepared statements) kullanmalısınız. Kısıtlamalar, veritabanı seviyesinde veri kalitesini artırır ancak uygulama güvenliği için yeterli değildir.
İleri Seviye Kısıtlama: Trigger (Tetikleyici) Kullanımı
Standart kısıtlamaların yetersiz kaldığı karmaşık iş kurallarında TRIGGER (tetikleyici) yapısını kullanırız. Bir trigger, veritabanında belirli bir işlem (INSERT, UPDATE, DELETE) gerçekleştiğinde otomatik olarak çalışan bir fonksiyon bloğudur.
CREATE OR REPLACE FUNCTION denetle_mesai_saati()
RETURNS TRIGGER AS $$
BEGIN
IF (EXTRACT(HOUR FROM NEW.islem_zamani) NOT BETWEEN 9 AND 18) THEN
RAISE EXCEPTION 'İşlemler sadece mesai saatleri içinde yapılabilir.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER mesai_kontrol
BEFORE INSERT ON islemler
FOR EACH ROW EXECUTE FUNCTION denetle_mesai_saati();
Bu örnekte, veritabanına mesai saatleri dışında kayıt girilmesini engelleyen dinamik bir kısıtlama oluşturduk. Bu yöntem, statik kısıtlamaların çözemediği zaman bazlı veya çoklu tablo etkileşimli kurallar için mükemmeldir.
Sıkça Sorulan Sorular
Veritabanı kısıtlamaları performansı düşürür mü?
Küçük ölçekli kısıtlamalar performansa göz ardı edilebilir düzeyde etki eder. Ancak çok karmaşık trigger yapıları yoğun yazma işlemlerinde performansı etkileyebilir. Doğru indeksleme ile bu etkiler minimize edilir.
Uygulama katmanında doğrulama yapmak yeterli mi?
Hayır, uygulama katmanı atlatılabilir. Veritabanı seviyesindeki kısıtlamalar, verinin kaynaktan (veritabanından) korunmasını sağlayan son ve en güçlü kaledir.
Kısıtlamaları üretim ortamında nasıl değiştiririm?
Üretim ortamında kısıtlamaları değiştirmek için ALTER TABLE komutlarını kullanın. Ancak, mevcut verilerin yeni kısıtlamalarla uyumlu olduğundan emin olmak için önce bir test ortamında deneme yapın.
CHECK kısıtlaması ile Trigger arasındaki fark nedir?
CHECK kısıtlaması sadece tek bir satırın değerini kontrol etmek için tasarlanmıştır. Trigger ise birden fazla tabloyu sorgulayabilir ve çok daha karmaşık mantıksal işlemleri gerçekleştirebilir.
Hatalı veri girişi olduğunda veritabanı ne yapar?
Veritabanı motoru işlemi durdurur ve bir hata kodu (error code) döndürür. Uygulama kodunuzda bu hata kodunu yakalayarak kullanıcıya anlamlı bir mesaj göstermelisiniz.
Veritabanı Kısıtlamalarında Hata Yönetimi ve Exception Handling
Veritabanı kısıtlamaları devreye girdiğinde, veritabanı motoru işlemi atomik olarak durdurur ve bir istisna (exception) fırlatır. Profesyonel bir uygulamada, bu hataları doğrudan kullanıcıya göstermek yerine, uygulama katmanında yakalayarak (try-catch blokları ile) anlamlı bir hata mesajına dönüştürmelisiniz. Örneğin, bir UNIQUE kısıtlaması ihlal edildiğinde, kullanıcıya "Veritabanı hatası 23505" demek yerine "Bu e-posta adresi zaten kayıtlı" mesajını döndürmek kullanıcı deneyimi açısından kritiktir.
Aşağıdaki örnek, Node.js ve PostgreSQL kullanarak bir kısıtlama hatasının nasıl yakalanacağını göstermektedir:
async function createUser(userData) {
try {
await db.query('INSERT INTO users (email) VALUES ($1)', [userData.email]);
} catch (error) {
if (error.code === '23505') {
throw new Error('Kullanıcı zaten mevcut, lütfen başka bir e-posta deneyin.');
}
throw new Error('Beklenmedik bir veritabanı hatası oluştu.');
}
}
Kısıtlamaların Test Edilmesi: Unit Test Stratejileri
Veritabanı kısıtlamalarınızın beklendiği gibi çalışıp çalışmadığını doğrulamak için "negatif test senaryoları" oluşturmalısınız. Sadece başarılı kayıtları değil, kısıtlamaları ihlal eden verilerin veritabanına girmesini engelleyen testler yazmak, sisteminizin dayanıklılığını artırır.
Test Senaryosu Oluşturma Adımları
- Hazırlık: Test veritabanında temiz bir tablo oluşturun.
- İhlal Senaryosu: Kısıtlamayı bilerek ihlal eden bir veri (örneğin
CHECKkısıtlamasına uymayan bir yaş değeri) gönderin. - Doğrulama: Veritabanının işlemi reddettiğini ve uygun hata kodunu döndürdüğünü assert (doğrulama) edin.
- Temizlik: Test sonunda veritabanını eski haline getirin.
Örnek bir SQL test sorgusu ile kısıtlamanın aktifliğini şu şekilde kontrol edebilirsiniz:
-- CHECK kısıtlamasının çalıştığını doğrulamak için test
BEGIN;
-- Bu işlem hata döndürmelidir (Örn: yaş < 18 ise)
INSERT INTO users (username, age) VALUES ('test_user', 15);
-- Eğer hata almadıysanız kısıtlama tanımlanmamış demektir
ROLLBACK;
İpucu: Büyük ölçekli projelerde, veritabanı şema değişikliklerini manuel yapmak yerineFlywayveyaLiquibasegibi migrasyon araçlarını kullanın. Bu araçlar, kısıtlamaların tüm geliştirme ve üretim ortamlarında aynı standartta uygulanmasını sağlar.
Sonuç
Sql & veritabanı ile uygulamalar için veri kısıtlama mekanizması kurmak, profesyonel bir yazılım mimarisinin olmazsa olmazıdır. Bu rehberde öğrendiğiniz NOT NULL, UNIQUE, CHECK, FOREIGN KEY ve TRIGGER yapıları, verilerinizin tutarlılığını korumak için kullanabileceğiniz en temel araçlardır. Bir sonraki adım olarak, veritabanı şema yönetimi (migration) araçlarını kullanarak bu kısıtlamaları kod üzerinden nasıl yöneteceğinizi araştırmanızı öneririm.


Yorumlar (0)
Yorum Yaz