Bölüm 11: Veri Tabanı Programlama

Java ile Nesne Yönelimli Programlama
Kalıcı depolamaİlişkisel modelSQLiteSQL komutları JOIN ve GROUP BYJDBCEkle, oku, güncelle, silPreparedStatementİşlemler (transaction)
Sercan KÜLCÜ · Giresun Üniversitesi · Elektrik-Elektronik Mühendisliği

Giriş

Öğrenme Çıktıları

Kavramlar

Veri tabanının neden gerektiğini, dosyalardan farkını ve ilişkisel modeli (tablo, satır, sütun, birincil anahtar) açıklamak.

SQL

CREATE TABLE, INSERT, SELECT, UPDATE, DELETE komutlarını yazmak; WHERE, ORDER BY, LIMIT ile sorguyu daraltmak; JOIN ile tabloları birleştirmek, GROUP BY ile gruplamak.

JDBC

Connection, Statement ve ResultSet ile Java'dan SQLite'a bağlanıp kayıt eklemek, okumak, güncellemek ve silmek; kaynakları try-with-resources ile kapatmak.

Güvenlik ve bütünlük

SQL enjeksiyonunu PreparedStatement ile önlemek; commit/rollback ile birden çok komutu tek bir işlem olarak yürütmek.

Veri Tabanı Kavramı

Veri Tabanı Neden Önemlidir?

  • Programdaki değişkenler ve nesneler RAM'de yaşar: program kapanınca kaybolur.
  • Kalıcı depolama (persistent storage): verinin uzun süre, program kapandıktan sonra da saklanması. Öğrenci notları, banka hesapları, oyun skorları hep kalıcı olmalıdır.
  • Veri tabanı (database): düzenli biçimde saklanan ve kolayca sorgulanabilen veri topluluğu. Onu yöneten yazılıma Veri tabanı Yönetim Sistemi (VTYS, DBMS) denir.
  • Benzetme: veri tabanı, kütüphanenin kataloglu arşivi gibidir; aranan kitap bütün raflar gezilmeden bulunur.
RAM (bellek)ogrencinotlar[]toplam = 87kapanınca silinirDiskokul.dbkalıcıkaydet: INSERT, UPDATEgeri oku: SELECTProgram yeniden açılınca veri diskten okunur.

Veri Tabanı Kavramı

Dosya mı, Veri Tabanı mı?

Dosya sistemleri bazı uygulamalar için yeterli olabilir (ayar dosyası, günlük kaydı, kısa bir liste). Veri büyüyüp karmaşıklaştıkça veri tabanları gerekir: verinin verimli işlenmesi için önemli özellikler sunarlar.

ÖzellikDüz dosya (txt, csv)Veri tabanı
AramaDosya baştan sona okunurİndeksle hızlı; tek satırlık SQL sorgusu
GüncellemeÇoğu zaman tüm dosya yeniden yazılırYalnızca ilgili satır değişir
Eş zamanlı erişimİki program aynı anda yazarsa veri bozulabilirKilitleme ve işlemlerle güvenli
TutarlılıkBiçim denetimi tamamen programcıya kalırVeri tipleri ve kısıtlar (NOT NULL, UNIQUE, CHECK)
GüvenlikDosya izni: ya hepsi ya hiçbiriKullanıcı, tablo ve alan düzeyinde yetki
Hata sonrasıYarım kalan yazma dosyayı bozabilirYarım kalan işlem geri alınır
programnotlar.csvarama, kilitleme, biçim: programcıdaprogramSQLVTYSdosyaindeks, kısıt, işlem, yetki: VTYS'de

Veri Tabanı Kavramı

Temel Özellikler

Verilere hızlı erişim

İndeksler sayesinde milyonlarca satır arasından aranan kayıt kısa sürede bulunur.

Alan düzeyinde güvenlik

Yetki tablo, hatta sütun (alan) düzeyinde verilebilir: maaş sütununu yalnızca muhasebe birimi görebilir.

Platform bağımsız, standart API'ler

SQL bir standarttır; Java'da JDBC aynı arayüzle farklı veri tabanlarına erişir.

Dosya yapısından bağımsızlık

Program verinin diskte nasıl tutulduğunu bilmez. Ne istediğini SQL ile söyler; nasıl bulunacağına VTYS karar verir.

programNE: SQLVTYS (SQLite, MySQL)NASILindeks / plandisk dosyaları

Bunlara ek olarak veri tabanları veri bütünlüğü kısıtları ve işlem (transaction) desteği sunar.

Veri Tabanı Kavramı

İlişkisel Model: Tablo, Satır, Sütun

userstablo (table)idnameemail1Ahmet Yılmazahmet@example.com2Ayşe Demirayse@example.com3Can Kayacan@example.comsütun (alan, column)satır(kayıt, row)birincil anahtar (primary key): her satırda farklı
  • İlişkisel (relational) veri tabanı veriyi tablolarda saklar.
  • Sütun: verinin bir özelliği; adı ve veri tipi vardır (name TEXT).
  • Satır: bir varlığın tüm bilgileri (bir kullanıcı).
  • Birincil anahtar: her satırı benzersiz tanımlar; iki satırın id'si aynı olamaz.
  • Java benzetmesi: tablo ≈ sınıf, sütun ≈ alan, satır ≈ nesne.

Veri Tabanı Kavramı

Veri Tabanı Türleri

İlişkisel

Tablolar ve SQL. Oracle, SQL Server, MySQL, PostgreSQL. Kullanımın büyük bölümü (yaklaşık %77).

NoSQL / arama

Belge, anahtar–değer, arama dizini gibi tablosuz modeller; belirli senaryolarda kullanılır: MongoDB, Elasticsearch (yaklaşık %23).

Gömülü ilişkisel

SQLite: programın içinde çalışan ilişkisel veri tabanı; veri paylaşımının az olduğu durumlarda yaygındır.

İlişkisel%77NoSQL / arama%23yaklaşık kullanım payı

Örneklerde kurulumu en kolay olan SQLite kullanılır. SQL ve JDBC kodu diğer ilişkisel veri tabanlarında da neredeyse aynıdır.

SQLite

SQLite Nedir?

  • Gömülü (embedded) bir ilişkisel veri tabanıdır: programın içinde bir kütüphane olarak çalışır.
  • Sunucuya ihtiyaç duymaz (serverless): ayrı bir program, kurulum ya da yönetici gerekmez.
  • Kendine yeten bir yapıya sahiptir: tüm tablolar tek bir disk dosyasında saklanır (ör. sample.db).
  • Çapraz platform: aynı dosya Windows, Linux, macOS ve Android'de açılır.
  • Kolayca kurulur ve kullanılır; açık kaynaklı ve ücretsizdir.
  • Dünyada en yaygın kullanılan gömülü ilişkisel veri tabanıdır.

Yaygın kullanım alanları

  • Mobil uygulamalar
  • Gömülü sistemler
  • Küçük ölçekli uygulamalar
  • Tarayıcı, e-posta istemcisi gibi masaüstü programları

Uygun olmadığı yer

  • Ağ üzerinden çok sayıda kullanıcının aynı anda yazdığı büyük sistemler

SQLite

Sunucu Tabanlı ve Gömülü Veri Tabanı

Sunucu tabanlı (MySQL, Oracle)Java programı1. süreçağport 3306VT sunucusu2. süreçveri dosyalarıkullanıcı adıve şifre gerekirGömülü (SQLite)tek süreçJava programıSQLite kütüphanesitest.dbtek dosyadosyayı doğrudan okur/yazar
Sunucu tabanlıSQLite
KurulumSunucu kurulur, kullanıcı ve şifre tanımlanırProjeye tek bir jar eklenir
VeriSunucunun yönettiği birçok dosyaTek bir .db dosyası
Eş zamanlı yazmaÇok kullanıcıAynı anda tek yazıcı

SQLite

SQLite Sürücüsü ve DB Browser

SQLite JDBC sürücüsü

Java'nın SQLite ile konuşmasını sağlayan jar dosyası (sqlite-jdbc-3.x.jar): github.com/xerial/sqlite-jdbc

DB Browser for SQLite

SQLite için popüler bir SQL istemcisi: .db dosyasını açar, tabloları görsel gösterir, SQL çalıştırır: sqlitebrowser.org/dl

Eclipse'te sürücüyü projeye eklemek

  1. Jar dosyası indirilip projede bir lib klasörüne konur.
  2. Projeye sağ tık → Build Path → Configure Build Path…
  3. Libraries sekmesi → Classpath → Add JARs… → jar seçilir → Apply and Close.

Komut satırından

javac Baglan.java
java -cp ".;sqlite-jdbc-3.40.0.0.jar" Baglan

Linux ve macOS'ta ayırıcı ; yerine : olur. Derleme için sürücü gerekmez; java.sql paketi JDK ile gelir.

SQL Temelleri

Yaygın SQL Komutları

KomutGöreviÖrnekCRUD
CREATE TABLEYeni tablo oluştururCREATE TABLE users (id INTEGER, name TEXT)—
INSERT INTOYeni kayıt eklerINSERT INTO users(name) VALUES('Ali')Create
SELECTTabloyu sorgularSELECT * FROM usersRead
UPDATEVar olan kayıtları güncellerUPDATE users SET name = 'Efe' WHERE id = 1Update
DELETEKayıtları silerDELETE FROM users WHERE id = 1Delete
C: INSERTR: SELECTU: UPDATED: DELETEtablo
  • SQL (Structured Query Language, yapılandırılmış sorgu dili): ilişkisel veri tabanlarının ortak dilidir.
  • Anahtar sözcüklerde büyük/küçük harf fark etmez; okunabilirlik için BÜYÜK yazılır. Metin değerler tek tırnak içindedir: 'Ali'.
  • * “tüm sütunlar” demektir. WHERE komutun hangi satırlara uygulanacağını seçer.

SQL Temelleri

CREATE TABLE ve Veri Tipleri

CREATE TABLE IF NOT EXISTS uyeler (
  id    INTEGER PRIMARY KEY,   -- benzersiz
  name  TEXT NOT NULL,         -- boş olamaz
  email TEXT UNIQUE,           -- tekrar edemez
  yas   INTEGER CHECK (yas >= 0)
)
  • IF NOT EXISTS: tablo zaten varsa hata verme.
  • INTEGER PRIMARY KEY: değer verilmezse SQLite en büyük id + 1 atar.
  • Kısıt bozulursa komut reddedilir: NOT NULL constraint failed: uyeler.name, CHECK constraint failed: yas >= 0.
SQLite tipiJava karşılığı
INTEGERint, long · getInt
REALdouble · getDouble
TEXTString · getString
BLOBbyte[] · getBytes
NULLdeğer yok

SQL Temelleri

SQL Komutları Adım Adım

CREATE TABLE users (id INTEGER PRIMARY KEY,
                    name TEXT, email TEXT)
usersidnameemail(tablo boş)

Tablo oluşturuldu: üç sütun var, henüz hiç satır yok.

INSERT INTO users(name, email)
  VALUES('Ahmet Yılmaz', 'ahmet@example.com')
usersidnameemail1Ahmet Yılmazahmet@example.com

id verilmedi; SQLite 1 atadı. 1 satır etkilendi.

INSERT INTO users(name, email)
  VALUES('Ayşe Demir', 'ayse@example.com')
usersidnameemail1Ahmet Yılmazahmet@example.com2Ayşe Demirayse@example.com

İkinci kayıt id = 2 aldı (en büyük id + 1).

UPDATE users SET name = 'Mehmet Akdeniz'
  WHERE id = 1
usersidnameemail1Mehmet Akdenizahmet@example.com2Ayşe Demirayse@example.com

WHERE id = 1 yalnızca ilk satırı seçti; sadece name değişti, email aynı kaldı.

DELETE FROM users WHERE id = 1
usersidnameemail1Mehmet Akdenizahmet@example.com2Ayşe Demirayse@example.com

id = 1 olan satır silindi. 1 satır etkilendi.

SELECT * FROM users
usersidnameemail2Ayşe Demirayse@example.com

Sorgu tabloyu değiştirmez, yalnızca okur: sonuç tek satır (2, Ayşe Demir, ayse@example.com).

SQL Temelleri

SELECT: Süzme, Sıralama, Sınırlama

skorlaroyuncupuanada72can95ece58efe95naz81
SorguSonuç
SELECT oyuncu, puan FROM skorlar
WHERE puan < 90 ORDER BY puan DESC
naz 81, ada 72, ece 58
SELECT oyuncu, puan FROM skorlar
ORDER BY puan DESC, oyuncu LIMIT 3
can 95, efe 95, naz 81
SELECT oyuncu, puan FROM skorlar
WHERE oyuncu LIKE 'e%'
ece 58, efe 95
SELECT COUNT(*), AVG(puan), MAX(puan)
FROM skorlar WHERE puan >= 60
4 · 85.75 · 95
  • WHERE satırları süzer (=, <>, <, >=, AND, OR, LIKE 'e%': “e ile başlayan”).
  • ORDER BY … DESC büyükten küçüğe sıralar; eşitlikte ikinci ölçüt (oyuncu) kullanılır. LIMIT 3 ilk 3 satırı alır.

SQL Temelleri · Adım adım kod izleme

Bir SELECT Sorgusu Nasıl Çalışır?

String sql = "SELECT oyuncu, puan FROM skorlar "
           + "WHERE puan > 70 "
           + "ORDER BY oyuncu DESC LIMIT 2";
try (Statement st = conn.createStatement();
     ResultSet rs = st.executeQuery(sql)) {
  while (rs.next()) {
    System.out.println(rs.getString("oyuncu") + " "
                       + rs.getInt("puan"));
  }
}
FROMWHEREORDER BYLIMIToyuncupuanada72can95ece58efe95naz81

Sorgu tek bir metindir. skorlar tablosunda 5 satır var.

FROMWHEREORDER BYLIMIToyuncupuanada72can95ece58efe95naz815 satır

executeQuery sorguyu gönderir. Veri tabanı önce FROM: tablonun bütün satırları.

FROMWHEREORDER BYLIMIToyuncupuanada72can95ece58efe95naz81ece 58 elendi

WHERE puan > 70: koşulu sağlamayan ece elenir; 4 satır kalır.

FROMWHEREORDER BYLIMIToyuncupuannaz81efe95can95ada72ad, Z → A

ORDER BY oyuncu DESC: adlar ters alfabetik sıraya dizilir (puana göre değil).

FROMWHEREORDER BYLIMIToyuncupuannaz81efe95can95ada72ilk 2 satır

LIMIT 2: sıralanmış listenin ilk iki satırı sonuç kümesidir.

FROMWHEREORDER BYLIMIToyuncupuannaz81efe95imleçsonuç kümesi

İmleç başlangıçta ilk satırın önündedir; next() onu 1. satıra taşır.

FROMWHEREORDER BYLIMIToyuncupuannaz81efe95imleçsonuç kümesi

Sütunlar adlarıyla okunur ve yazdırılır.

FROMWHEREORDER BYLIMIToyuncupuannaz81efe95imleçsonuç kümesi

İmleç 2. satıra geçer.

FROMWHEREORDER BYLIMIToyuncupuannaz81efe95imleçsonuç kümesi

efe 95 yazılır.

FROMWHEREORDER BYLIMIToyuncupuannaz81efe95imleçsonuç kümesi

Başka satır yok: next() false, döngü biter. try bitince rs ve st kapanır.

İlişkiler ve Gruplama

Tekrarı Önlemek: Normalleştirme

tek tablo: siparis_kayitmusteritelefonurunAli555-11kalemAli555-11defterEce555-22silgiaynı telefoniki kez yazılmışiki tabloya bölünürmusterileridadtelefon1Ali555-112Ece555-22siparisleridmusteri_idurun11kalem21defter32silgimusteri_id → musteriler.id
  • Tek tabloda aynı bilgi (Ali'nin telefonu) her siparişte tekrarlanır: yer israfı ve tutarsızlık riski.
  • Güncelleme anomalisi: telefon değişince her satır ayrı ayrı düzeltilmeli; biri unutulursa Ali'nin iki farklı telefonu olur.
  • Ekleme anomalisi: henüz siparişi olmayan bir müşteri kaydedilemez.
  • Silme anomalisi: Ece'nin tek siparişi silinince Ece'nin bilgisi de kaybolur.
  • Normalleştirme (normalization): her bilgiyi tek bir yerde tutacak biçimde tabloları bölmek. Tablolar bir anahtarla (musteri_id) bağlanır.
  • Birinci normal biçim: her hücrede tek değer. '555-11, 555-33' gibi bir liste ayrı satırlara ya da ayrı bir tabloya taşınır.

İlişkiler ve Gruplama

Yabancı Anahtar ve İlişkiler

CREATE TABLE bolumler (
  id INTEGER PRIMARY KEY,
  ad TEXT NOT NULL);
CREATE TABLE ogrenciler (
  id INTEGER PRIMARY KEY,
  ad TEXT NOT NULL,
  bolum_id INTEGER REFERENCES bolumler(id));
bolumleridad1Bilgisayar2Elektrik3Makineogrencileridadbolum_id1Ada12Bora23Cem14DenizNULLbire çok: bir bölüm,çok öğrenciDeniz'in bölümü yok: bolum_id NULL
  • Yabancı anahtar (foreign key): başka bir tablonun birincil anahtarını gösteren sütun. Bir bölümde çok öğrenci olabilir: bire çok (one-to-many) ilişki.
  • Var olmayan bölüme öğrenci eklemek (bolum_id = 9) ya da öğrencisi olan bölümü silmek reddedilir: FOREIGN KEY constraint failed.
  • SQLite bu denetimi varsayılan olarak yapmaz: her bağlantıda stmt.execute("PRAGMA foreign_keys = ON") gerekir. Kapalıyken bolum_id = 9 sessizce kabul edilir.
  • Çoka çok ilişki (öğrenci–ders) üçüncü bir ara tabloyla kurulur: kayitlar(ogrenci_id, ders_id).
  • Java benzetmesi: Ogrenci nesnesindeki bir Bolum referansı.

İlişkiler ve Gruplama

JOIN ile Tabloları Birleştirme

SELECT o.ad AS ogrenci, b.ad AS bolum
FROM ogrenciler o JOIN bolumler b
  ON o.bolum_id = b.id
ORDER BY o.id
ogrenciJOIN (INNER)LEFT JOIN
AdaBilgisayarBilgisayar
BoraElektrikElektrik
CemBilgisayarBilgisayar
Denizsatır yokNULL
  • JOIN (birleştirme): iki tablonun satırlarını ON koşuluna göre eşleyip tek bir sonuç tablosu üretir.
  • o ve b tablo takma adlarıdır. İki tabloda da ad sütunu olduğu için o.ad, b.ad diye ayrılır; yalnız ad yazılırsa: ambiguous column name: ad.
  • AS sonuç sütununa yeni ad verir: Java'da rs.getString("bolum") ile okunur.
  • INNER JOIN (yalnız JOIN): yalnızca eşi bulunan satırlar. Bölümü NULL olan Deniz sonuçta yoktur.
  • LEFT JOIN: soldaki tablonun tüm satırları; eşi yoksa sağ taraf NULL, Java'da null.
  • ON unutulursa (FROM ogrenciler, bolumler) her satır her satırla eşleşir: 4 × 3 = 12 satırlık Kartezyen çarpım.

İlişkiler ve Gruplama · Adım adım kod izleme

JOIN Sorgusu Adım Adım

String sql = "SELECT o.ad AS ogrenci, b.ad AS bolum "
    + "FROM ogrenciler o JOIN bolumler b "
    + "ON o.bolum_id = b.id ORDER BY o.id";
try (ResultSet rs = stmt.executeQuery(sql)) {
  while (rs.next()) {
    System.out.println(rs.getString("ogrenci") + " - "
        + rs.getString("bolum"));
  }
}
ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3Makinesonuç: henüz satır yok

Sorgu iki tabloyu takma adlarla anar: o = ogrenciler, b = bolumler.

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayar1 = 1 → eşleşti

executeQuery: Ada'nın bolum_id'si 1; bolumler'de id = 1 bulunur → (Ada, Bilgisayar).

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayarBoraElektrik2 = 2 → eşleşti

Bora → 2 → Elektrik.

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayarBoraElektrikCemBilgisayar1 = 1 → eşleşti

Cem → 1: aynı bölüm satırı ikinci kez kullanılır.

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayarBoraElektrikCemBilgisayarNULL = ? → eşi yok

Deniz'in bolum_id'si NULL: hiçbir id'ye eşit sayılmaz; satır sonuca girmez.

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayarBoraElektrikCemBilgisayarimleç 1. satırda

next() ilk satıra geçer; sütunlar AS ile verilen adlarla okunur.

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayarBoraElektrikCemBilgisayarimleç 2. satırda

İkinci satır yazılır.

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayarBoraElektrikCemBilgisayarimleç 3. satırda

Üçüncü satır yazılır.

ogrenciler oadbolum_idAda1Bora2Cem1DenizNULLbolumler bidad1Bilgisayar2Elektrik3MakinesonuçogrencibolumAdaBilgisayarBoraElektrikCemBilgisayarnext() → false

Başka satır yok, döngü biter. LEFT JOIN yazılsaydı dördüncü satır Deniz - null olurdu.

İlişkiler ve Gruplama

Gruplama: GROUP BY ve HAVING

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30GROUP BY musterimusteriadettoplamAli3200Can1200Ece2120grup başına bir satır
  • GROUP BY aynı değere sahip satırları bir grupta toplar; toplama fonksiyonları (COUNT, SUM, AVG, MIN, MAX) her grup için ayrı hesaplanır.
  • WHERE gruplamadan önce satırları, HAVING gruplamadan sonra grupları süzer. WHERE SUM(tutar) >= 150 hatadır: misuse of aggregate: SUM().
  • Mantıksal çalışma sırası: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT.
Sorgu (SELECT musteri, COUNT(*), SUM(tutar) FROM siparisler …)Sonuç
GROUP BY musteriAli 3 200 · Can 1 200 · Ece 2 120
WHERE tutar >= 50 GROUP BY musteriAli 2 170 · Can 1 200 · Ece 1 80
GROUP BY musteri HAVING SUM(tutar) >= 150Ali 3 200 · Can 1 200

JOIN ile birlikte: SELECT b.ad, COUNT(o.id) FROM bolumler b LEFT JOIN ogrenciler o ON o.bolum_id = b.id GROUP BY b.id → Bilgisayar 2 · Elektrik 1 · Makine 0. COUNT(o.id) NULL'ları saymaz; COUNT(*) yazılsaydı öğrencisiz Makine 1 alırdı.

İlişkiler ve Gruplama · Adım adım kod izleme

GROUP BY Sorgusu Adım Adım

String sql = "SELECT musteri, COUNT(*) AS adet, "
    + "SUM(tutar) AS toplam FROM siparisler "
    + "GROUP BY musteri HAVING SUM(tutar) >= 150 "
    + "ORDER BY toplam DESC, musteri";
try (ResultSet rs = stmt.executeQuery(sql)) {
  while (rs.next()) {
    System.out.println(rs.getString("musteri") + ": "
        + rs.getInt("adet") + " sipariş, "
        + rs.getInt("toplam") + " TL");
  }
}
siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30

siparisler tablosunda 6 satır var; sorgu tek bir metindir.

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali303 grup: Ali, Can, Ece

FROM ve GROUP BY musteri: satırlar müşteri adına göre üç gruba ayrılır.

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30gruplarmusteriadettoplamAli3200Can1200Ece2120

Her grup için COUNT(*) ve SUM(tutar): Ali 120 + 50 + 30 = 200, Can 200, Ece 80 + 40 = 120.

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30gruplarmusteriadettoplamAli3200Can1200Ece2120HAVING: 120 < 150

HAVING SUM(tutar) >= 150: Ece'nin grubu elenir (120).

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30gruplarmusteriadettoplamAli3200Can1200Ece2120HAVING: 120 < 150sonuç (sıralı)musteriadettoplamAli3200Can1200eşitlik → ad: Ali, Can

ORDER BY toplam DESC, musteri: Ali ile Can 200'de eşit; ikinci ölçüt ad, önce Ali.

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30gruplarmusteriadettoplamAli3200Can1200Ece2120HAVING: 120 < 150sonuç (sıralı)musteriadettoplamAli3200Can1200

next() ilk satıra geçer; takma adlar (adet, toplam) sütun adı olarak okunur.

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30gruplarmusteriadettoplamAli3200Can1200Ece2120HAVING: 120 < 150sonuç (sıralı)musteriadettoplamAli3200Can1200

İkinci satır yazılır.

siparisleridmusteritutar1Ali1202Ece803Ali504Can2005Ece406Ali30gruplarmusteriadettoplamAli3200Can1200Ece2120HAVING: 120 < 150sonuç (sıralı)musteriadettoplamAli3200Can1200next() → false

Döngü biter. Sonuçta grup başına bir satır vardır; tek tek siparişler görünmez.

İlişkiler ve Gruplama

NULL: Değerin Olmaması

  • NULL “bilinmiyor / yok” demektir; 0 ya da boş metin ('') değildir.
  • NULL ile her karşılaştırma bilinmeyen sonuç verir: WHERE bolum_id = NULL hiçbir satırı seçmez. Doğrusu IS NULL ya da IS NOT NULL.
  • WHERE bolum_id <> 1 de Deniz'i seçmez: NULL'un 1'den farklı olduğu bile bilinmez.
  • NULL + 1 sonucu NULL'dur. COALESCE(bolum_id, 0) NULL yerine 0 koyar.
  • COUNT(*) satırları, COUNT(bolum_id) NULL olmayan değerleri sayar: 4 ve 3. Eşleşen satır yoksa AVG ve SUM NULL döndürür.
String sql = "SELECT ad, bolum_id FROM ogrenciler "
    + "WHERE id >= 3";
try (ResultSet rs = stmt.executeQuery(sql)) {
  while (rs.next()) {
    int b = rs.getInt("bolum_id");
    boolean bos = rs.wasNull();
    System.out.println(rs.getString("ad") + " " + b
        + (bos ? " (NULL)" : ""));
  }
}
Çıktı
Cem 1
Deniz 0 (NULL)
  • getInt NULL için 0 döndürür; 0 mı NULL mı ayırt etmek için hemen ardından wasNull() çağrılır.
  • wasNull() son okunan sütuna bakar: getString("ad")'dan sonra çağrılsaydı adı sorardı ve (NULL) hiç yazılmazdı.
  • getString ve getObject NULL için null döndürür.

İlişkiler ve Gruplama

İndeks: Aramayı Hızlandırmak

indeks yok: SCAN (her satıra bakılır)DenizAdaCemBora4 karşılaştırmaidx_ogr_ad: sıralı + satıra işaretAda→ satır 1Bora→ satır 2Cem→ satır 3Deniz→ satır 4doğrudan Cem'e iner
CREATE INDEX idx_ogr_ad ON ogrenciler(ad);
EXPLAIN QUERY PLAN
  SELECT * FROM ogrenciler WHERE ad = 'Cem';
  • İndeks (index): bir sütunun değerlerini sıralı tutan ek yapı (B-ağacı); kitabın sonundaki dizin gibi. Arama tüm tabloyu taramak yerine ilgili satıra gider.
  • Birincil anahtar ve UNIQUE sütunların indeksi kendiliğinden oluşur.
  • Bedeli: her INSERT, UPDATE, DELETE indeksi de günceller ve indeks diskte yer kaplar. Sık aranan; WHERE, JOIN ve ORDER BY'da kullanılan sütunlara konur.
  • EXPLAIN QUERY PLAN (SQLite) sorgunun nasıl yürütüleceğini gösterir.
DurumEXPLAIN QUERY PLAN çıktısı
İndeks yokkenSCAN ogrenciler
İndeks varkenSEARCH ogrenciler USING INDEX idx_ogr_ad (ad=?)
WHERE id = 3SEARCH … USING INTEGER PRIMARY KEY (rowid=?)
WHERE ad LIKE '%em'SCAN ogrenciler (baştaki joker indeksi kullanamaz)

JDBC

JDBC (Java Database Connectivity)

  • JDBC (Java Database Connectivity): Java'nın veri tabanlarına bağlanmak için sunduğu standart API'dir; java.sql paketindedir ve JDK ile gelir.
  • Program yalnızca JDBC arayüzlerini kullanır; her veri tabanı üreticisi bu arayüzleri uygulayan bir sürücü (driver) sağlar.
  • Veri tabanını değiştirmek için çoğu zaman yalnızca sürücü jar'ı ve bağlantı adresi değişir.
  • Çok biçimlilik iş başında: Connection bir arayüzdür; SQLite'ta gerçek nesnenin sınıfı org.sqlite.jdbc4.JDBC4Connection'dır.
Java programınızJDBC API (java.sql)Connection · Statement · ResultSetDriverManagerSQLite sürücüsüsqlite-jdbc.jarMySQL sürücüsümysql-connector-j.jarsample.dbMySQL sunucusu

JDBC

JDBC'nin Temel Türleri

TürGöreviÖnemli metotlar
DriverManagerUygun sürücüyü bulur, bağlantı açargetConnection(url), getConnection(url, kullanici, sifre)
ConnectionVeri tabanıyla açık bir oturumcreateStatement(), prepareStatement(sql), commit(), rollback(), close()
StatementSabit bir SQL metnini çalıştırırexecute, executeUpdate, executeQuery
PreparedStatement? yer tutuculu, önceden hazırlanan SQLsetInt, setString, executeUpdate(), executeQuery()
ResultSetSorgu sonucundaki satırlar ve bir imleçnext(), getInt("id"), getString("name")
SQLExceptionVeri tabanı hatası; kontrol edilen istisnagetMessage(), getErrorCode()

Hepsi java.sql paketindedir: dosyanın başına import java.sql.*; yazılır. Connection, Statement ve ResultSet kapatılması gereken kaynaklardır (AutoCloseable).

JDBC

Bağlantı Adresi (JDBC URL)

Bağlantı adresi (URL)Anlamı
jdbc:sqlite:sample.dbÇalışma dizinindeki sample.db; yoksa boş olarak oluşturulur
jdbc:sqlite:C:/veriler/okul.dbTam yol ile dosya
jdbc:sqlite::memory:Bellekte geçici veri tabanı; bağlantı kapanınca silinir (denemeler için)
jdbc:mysql://localhost:3306/testdbMySQL sunucusu: makine, port, veri tabanı adı; ayrıca kullanıcı adı ve şifre
jdbcprotokol:sqlitealt protokol → sürücü:sample.dbveri tabanı (dosya)
  • Biçim: jdbc:<alt protokol>:<veri tabanı dosyası ya da adresi>. DriverManager, alt protokole bakarak sürücüyü seçer.
  • JDBC 4.0 (Java 6) ve sonrasında sürücü, classpath'teki jar'dan otomatik yüklenir. Eski kodlardaki Class.forName("org.sqlite.JDBC") satırı artık gerekmez.
  • Sürücü classpath'te yoksa: No suitable driver found for jdbc:sqlite:sample.db.

JDBC

Bir JDBC Programının Adımları

1. BağlangetConnection()2. Komut nesnesicreateStatement()3. ÇalıştırexecuteQuery()4. Sonucu işlers.next()5. Kapatclose()
try (Connection conn = DriverManager.getConnection(url); // 1
     Statement stmt = conn.createStatement();             // 2
     ResultSet rs = stmt.executeQuery(sql)) {             // 3
  while (rs.next()) {                                     // 4
    System.out.println(rs.getInt(1));
  }
} // 5: rs, stmt, conn ters sırada otomatik kapanır
  • Kaynaklar try-with-resources parantezinde açılır; blok bitince (hata olsa bile) ters sırada kapanır: önce rs, sonra stmt, en son conn.
  • Kapatılmayan bağlantılar dosyayı kilitli bırakabilir ve bellek sızıntısına yol açar.
  • getInt(1): sütun numaraları 1'den başlar.

Temel İşlemler

SQLite'a Bağlanma

// Veri tabanı dosyasının yolu (bağlantı adresi)
String url = "jdbc:sqlite:sample.db";
try (Connection conn = DriverManager.getConnection(url)) {
  // Bağlantı başarılıysa mesaj yazdırılır.
  if (conn != null) {
    System.out.println("Bağlantı başarılı!");
  }
} catch (SQLException e) {
  // Bağlantı hatası olursa hata mesajı yazdırılır.
  System.out.println(e.getMessage());
}
Çıktı
Bağlantı başarılı!
  • getConnection dosya yoksa oluşturur (boş veri tabanı).
  • Bağlantı kurulamazsa null dönmez, SQLException fırlatır; if (conn != null) yalnızca ek bir güvencedir.
  • SQLException kontrol edilen istisnadır: yakalanmalı ya da throws ile bildirilmelidir.

Temel İşlemler

Tablo Oluşturma

String url = "jdbc:sqlite:sample.db";
String sql = "CREATE TABLE IF NOT EXISTS users "
           + "(id INTEGER PRIMARY KEY, name TEXT, email TEXT)";
try (Connection conn = DriverManager.getConnection(url);
     Statement stmt = conn.createStatement()) {
  // Tablo oluşturuluyor.
  stmt.execute(sql);
  System.out.println("Tablo oluşturuldu!");
} catch (SQLException e) {
  System.out.println(e.getMessage());
}
Çıktı
Tablo oluşturuldu!
  • execute(sql) her tür SQL'i çalıştırır; sonuç kümesi üretirse true, CREATE için false döner.
  • IF NOT EXISTS sayesinde program ikinci kez çalışınca hata olmaz. Olmasaydı: table users already exists.
  • Birden çok kaynak tek try (…; …) içinde ; ile ayrılır.

Temel İşlemler

Veri Ekleme

String url = "jdbc:sqlite:sample.db";
String sql = "INSERT INTO users(name, email) "
           + "VALUES('Ahmet Yılmaz', 'ahmet@example.com')";
try (Connection conn = DriverManager.getConnection(url);
     Statement stmt = conn.createStatement()) {
  // Veri tabanına veri ekleniyor.
  int sayi = stmt.executeUpdate(sql);
  System.out.println("Veri eklendi! (" + sayi + " satır)");
} catch (SQLException e) {
  System.out.println(e.getMessage());
}
Çıktı
Veri eklendi! (1 satır)
  • executeUpdate etkilenen satır sayısını (int) döndürür.
  • id verilmedi; SQLite 1 atadı.
  • Program her çalıştığında yeni bir satır eklenir (id 2, 3, …).
  • SQL içindeki metin değerler tek tırnak içindedir.

Temel İşlemler

Veri Okuma

String url = "jdbc:sqlite:sample.db";
String sql = "SELECT * FROM users";
try (Connection conn = DriverManager.getConnection(url);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql)) {
  // Satırlar tek tek okunup yazdırılıyor.
  while (rs.next()) {
    System.out.println("ID: " + rs.getInt("id")
        + ", Ad: " + rs.getString("name")
        + ", E-posta: " + rs.getString("email"));
  }
} catch (SQLException e) {
  System.out.println(e.getMessage());
}
Çıktı
ID: 1, Ad: Ahmet Yılmaz, E-posta: ahmet@example.com
  • executeQuery bir ResultSet döndürür.
  • rs.next() imleci bir sonraki satıra taşır; satır varsa true, yoksa false döner.
  • getInt("id"), getString("name"): o anki satırın sütunlarını adıyla okur.

Temel İşlemler

ResultSet ile Okuma: Adım Adım

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: ilk satırdan öncers

Bağlantı açılır, sorgu çalışır. rs iki satırlık sonucu tutar; imleç ilk satırdan önce bekler.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: 1. satırrs

rs.next() imleci 1. satıra taşır ve true döndürür: döngüye girilir.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: 1. satırrs

Sütunlar o anki satırdan okunur.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: 1. satırrs

Satır yazdırılır.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: 2. satırrs

İmleç 2. satıra geçer; yine true.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: 2. satırrs

Bu kez 2. satırın değerleri okunur.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: 2. satırrs

İkinci satır yazdırılır.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: son satırdan sonrars

Başka satır yok: rs.next() false döner, döngü biter.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: son satırdan sonrars

Döngüden sonraki satır çalışır.

ResultSet rsidname1Ahmet Yılmaz2Ayşe Demirimleç: son satırdan sonrars

try bloğu biter: rs, stmt, conn bu sırayla otomatik kapanır.

String url = "jdbc:sqlite:okuma.db";
String sql = "SELECT * FROM users";
try (Connection conn = DriverManager.getConnection(url);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql)) {
  while (rs.next()) {
    int id = rs.getInt("id");
    String ad = rs.getString("name");
    System.out.println(id + " - " + ad);
  }
  System.out.println("Okuma bitti.");
} catch (SQLException e) {
  System.out.println(e.getMessage());
}

Temel İşlemler

Veri Güncelleme

String url = "jdbc:sqlite:sample.db";
String sql = "UPDATE users SET name = 'Mehmet Akdeniz' WHERE id = 1";
try (Connection conn = DriverManager.getConnection(url);
     Statement stmt = conn.createStatement()) {
  // Veri tabanındaki veri güncelleniyor.
  int sayi = stmt.executeUpdate(sql);
  System.out.println("Veri güncellendi! (" + sayi + " satır)");
} catch (SQLException e) {
  System.out.println(e.getMessage());
}
Çıktı
Veri güncellendi! (1 satır)
  • Öncesinde okuma: ID: 1, Ad: Ahmet Yılmaz, …; sonrasında: ID: 1, Ad: Mehmet Akdeniz, …
  • SET yalnızca yazılan sütunu değiştirir; email aynı kalır.
  • WHERE id = 1 güncellenecek satırı seçer. WHERE unutulursa tüm satırlar güncellenir.

Temel İşlemler

Veri Silme

String url = "jdbc:sqlite:sample.db";
String sql = "DELETE FROM users WHERE id = 1";
try (Connection conn = DriverManager.getConnection(url);
     Statement stmt = conn.createStatement()) {
  // Veri tabanındaki veri siliniyor.
  int sayi = stmt.executeUpdate(sql);
  System.out.println("Veri silindi! (" + sayi + " satır)");
} catch (SQLException e) {
  System.out.println(e.getMessage());
}
Çıktı (program iki kez çalıştırılınca)
Veri silindi! (1 satır)
Veri silindi! (0 satır)
  • İkinci çalıştırmada silinecek satır kalmamıştır: hata oluşmaz, executeUpdate 0 döner.
  • Bu yüzden mesajı dönen sayıya göre vermek daha doğrudur (Alıştırma 1).
  • WHERE'siz DELETE FROM users tüm satırları siler; tablonun kendisi kalır.

Temel İşlemler

execute, executeUpdate, executeQuery

MetotDöndürürKullanım
executebooleanCREATE TABLE, DROP
executeUpdateint satır sayısıINSERT, UPDATE, DELETE
executeQueryResultSetSELECT

Yanlış metot hataya yol açar: executeQuery ile INSERT çalıştırmak → query does not return ResultSet.

Programlar sırayla çalıştırılınca
> java Baglan
Bağlantı başarılı!
> java TabloOlustur
Tablo oluşturuldu!
> java VeriEkle
Veri eklendi! (1 satır)
> java VeriOku
ID: 1, Ad: Ahmet Yılmaz, E-posta: ahmet@example.com
> java VeriGuncelle
Veri güncellendi! (1 satır)
> java VeriOku
ID: 1, Ad: Mehmet Akdeniz, E-posta: ahmet@example.com
> java VeriSil
Veri silindi! (1 satır)
> java VeriOku
> java VeriSil
Veri silindi! (0 satır)

Temel İşlemler · Adım adım kod izleme

executeUpdate Ne Döndürür?

String[] adlar = {"zımba", "makas", "bant"};
int[] stok = {40, 0, 15};
PreparedStatement ek = conn.prepareStatement(
    "INSERT INTO malzeme(ad, stok) VALUES(?, ?)");
for (int i = 0; i < adlar.length; i++) {
  ek.setString(1, adlar[i]);
  ek.setInt(2, stok[i]);
  ek.executeUpdate();
}
int g = stmt.executeUpdate(
    "UPDATE malzeme SET stok = stok + 10 WHERE stok < 20");
int s = stmt.executeUpdate("DELETE FROM malzeme WHERE stok > 100");
System.out.println(g + " güncellendi, " + s + " silindi");
malzemeidadstoktablo boş

Ekleme komutu bir kez hazırlanır; döngüde yalnızca değerler değişecek.

malzemeidadstokyer tutucular1. ? = "zımba"2. ? = 40

İlk turda yer tutucular doldurulur.

malzemeidadstok1zımba401 satır eklendi

executeUpdate 1 döndürür; SQLite id = 1 atar.

malzemeidadstok1zımba402makas0yer tutucular1. ? = "makas"2. ? = 01 satır eklendi

Aynı hazır komut yeni değerlerle yeniden çalışır.

malzemeidadstok1zımba402makas03bant15yer tutucular1. ? = "bant"2. ? = 151 satır eklendi

Üçüncü ürün eklenir; i = 3 olunca döngü biter.

malzemeidadstok1zımba402makas103bant25WHERE stok < 20: 2 satır

WHERE stok < 20 makas (0) ve bandı (15) seçer; ikisine 10 eklenir. Dönen değer etkilenen satır sayısı: 2.

malzemeidadstok1zımba402makas103bant25eşleşen yok: 0

Stoku 100'den büyük ürün yok: hata oluşmaz, yalnızca 0 döner.

malzemeidadstok1zımba402makas103bant25

Program bu sayılara bakarak "kayıt bulunamadı" gibi doğru mesajlar verebilir.

PreparedStatement

SQL Enjeksiyonu (SQL Injection)

String girdi = "x' OR '1'='1";   // kullanıcının yazdığı "ad"

// TEHLİKELİ: girdi SQL metnine yapıştırılıyor
String sql = "SELECT ad FROM kisiler WHERE ad = '"
           + girdi + "'";
System.out.println(sql);
ResultSet rs1 = stmt.executeQuery(sql);

// GÜVENLİ: girdi yalnızca değer olarak gönderiliyor
PreparedStatement ps = conn.prepareStatement(
    "SELECT ad FROM kisiler WHERE ad = ?");
ps.setString(1, girdi);
ResultSet rs2 = ps.executeQuery();
Çıktı (tabloda 3 kişi var; satırlar sayıldı)
SELECT ad FROM kisiler WHERE ad = 'x' OR '1'='1'
Birleştirme: 3 satır
PreparedStatement: 0 satır
  • Kullanıcının yazdığı tırnak, SQL metnini değiştirdi: '1'='1' her zaman doğru, tüm satırlar döndü.
  • Giriş ekranında bu, şifresiz oturum açmak demektir.
  • ? ile gönderilen girdi yalnızca bir değerdir; SQL'in parçası olamaz.

PreparedStatement · Adım adım kod izleme

SQL Enjeksiyonu Adım Adım

String girdi = "x' OR '1'='1";
String sql = "SELECT ad FROM kisiler WHERE ad = '"
           + girdi + "'";
int n = 0;
try (ResultSet rs = stmt.executeQuery(sql)) {
  while (rs.next()) n++;
}
System.out.println("Birleştirme: " + n + " satır");
PreparedStatement ps = conn.prepareStatement(
    "SELECT ad FROM kisiler WHERE ad = ?");
ps.setString(1, girdi);
n = 0;
try (ResultSet rs = ps.executeQuery()) { while (rs.next()) n++; }
System.out.println("PreparedStatement: " + n + " satır");
veri tabanına giden SQL:(henüz yok)idad1Ali2Ayşe3Can

Kullanıcı ad yerine tırnak ve OR içeren bir metin yazdı. Tabloda 3 kişi var.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = 'x' OR '1'='1'idad1Ali2Ayşe3Can

Birleştirme sonucu: kullanıcının tırnağı ad değerini kapattı, ardından gelen OR '1'='1' artık SQL'in parçası.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = 'x' OR '1'='1'idad1Ali2Ayşe3Cankoşul'Ali'='x' → yanlış OR '1'='1' → DOĞRU

Veri tabanı koşulu her satırda değerlendirir: Ali için OR'un sağ tarafı her zaman doğru → satır seçilir.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = 'x' OR '1'='1'idad1Ali2Ayşe3Cankoşul'Ali'='x' → yanlış OR '1'='1' → DOĞRU'Ayşe'='x' → yanlış OR doğru → DOĞRU'Can'='x' → yanlış OR doğru → DOĞRU

Ayşe ve Can da seçilir: koşul hiçbir satırı elemez.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = 'x' OR '1'='1'idad1Ali2Ayşe3Cankoşul'Ali'='x' → yanlış OR '1'='1' → DOĞRU'Ayşe'='x' → yanlış OR doğru → DOĞRU'Can'='x' → yanlış OR doğru → DOĞRU3 satır döndü

Döngü 3 kez döner. Bir giriş ekranında bu, şifresiz oturum açmak demektir.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = 'x' OR '1'='1'idad1Ali2Ayşe3Cankoşul'Ali'='x' → yanlış OR '1'='1' → DOĞRU'Ayşe'='x' → yanlış OR doğru → DOĞRU'Can'='x' → yanlış OR doğru → DOĞRU3 satır döndü

Sonuç yazılır.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = ?idad1Ali2Ayşe3Can

Bu kez SQL önce derlenir; ? yalnızca bir değerin yeri. Komutun yapısı artık değişemez.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = [x' OR '1'='1]idad1Ali2Ayşe3Can

Girdi bütünüyle tek bir metin değeri olarak bağlanır; içindeki tırnaklar sıradan karakterdir.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = [x' OR '1'='1]idad1Ali2Ayşe3Cankoşulad = "x' OR '1'='1" → yanlışad = "x' OR '1'='1" → yanlışad = "x' OR '1'='1" → yanlış0 satır

Hiçbir kişinin adı bu 12 karakterlik metne eşit değil: satır yok.

veri tabanına giden SQL:SELECT ad FROM kisilerWHERE ad = [x' OR '1'='1]idad1Ali2Ayşe3Cankoşulad = "x' OR '1'='1" → yanlışad = "x' OR '1'='1" → yanlışad = "x' OR '1'='1" → yanlış0 satır

Aynı girdi, iki farklı sonuç. Kullanıcıdan gelen değer SQL'e her zaman ? ile verilir.

PreparedStatement

PreparedStatement ve Yer Tutucular

static void ekle(Connection conn, String ad, String soyad,
                 int yas) throws SQLException {
  String sql = "INSERT INTO kisiler(ad, soyad, yas) "
             + "VALUES(?, ?, ?)";
  try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, ad);      // 1. soru işareti
    ps.setString(2, soyad);   // 2. soru işareti
    ps.setInt(3, yas);        // 3. soru işareti
    ps.executeUpdate();
  }
}

Neden?

  • Güvenli: SQL enjeksiyonunu önler.
  • Tırnak sorunu yok: "Ali'nin" sorunsuz kaydedilir.
  • Aynı komut farklı değerlerle tekrar kullanılır.
INSERT INTO kisiler(ad, soyad, yas) VALUES( ?, ?, ? )?1. yer tutucu?2. yer tutucu?3. yer tutucusetString(1, ad)"Ahmet"setString(2, soyad)"Yılmaz"setInt(3, yas)30

setXxx(sıra, değer) her ?'i doldurur; sıra 1'den başlar. Sonra executeUpdate() parametresiz çağrılır.

PreparedStatement

Kişi Programı: Güncelleme ve Silme Metotları

static void guncelle(Connection conn, String ad, String soyad,
                     int yas) throws SQLException {
  String sql = "UPDATE kisiler SET soyad = ?, yas = ? "
             + "WHERE ad = ?";
  try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, soyad);
    ps.setInt(2, yas);
    ps.setString(3, ad);
    ps.executeUpdate();
  }
}

static void sil(Connection conn, String ad) throws SQLException {
  String sql = "DELETE FROM kisiler WHERE ad = ?";
  try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, ad);
    ps.executeUpdate();
  }
}
  • Her metot Connection'ı parametre olarak alır: bağlantı bir kez açılır, birçok işte kullanılır.
  • ?'ler SQL'deki sıraya göre numaralanır: guncelle'de ad, 3. yer tutucudur.
  • throws SQLException: hata çağırana iletilir; main de throws ile bildirdiğinden hata olursa program yığın iziyle durur.

PreparedStatement

Kişi Programı: Listeleme Metodu

static void listele(Connection conn) throws SQLException {
  String sql = "SELECT id, ad, soyad, yas FROM kisiler";
  try (Statement st = conn.createStatement();
       ResultSet rs = st.executeQuery(sql)) {
    while (rs.next()) {
      System.out.println(rs.getInt("id") + " " + rs.getString("ad")
          + " " + rs.getString("soyad") + " " + rs.getInt("yas"));
    }
    System.out.println("---");
  }
}
Çıktı (ilk listele çağrısı)
1 Ahmet Akdeniz 35
2 Ayşe Demir 40
---
  • Sorguda değişken değer olmadığından düz Statement yeterlidir.
  • Her listele çağrısı tablonun o anki hâlini okur; sonuna --- yazar.

PreparedStatement

Kişi Programı: Adım Adım

kisileridadsoyadyas(tablo boş)

kisiler.db yoksa oluşturulur; bağlantı ve stmt açılır.

kisileridadsoyadyas(tablo boş)

Tablo oluşturulur; henüz boştur.

kisileridadsoyadyas1AhmetYılmaz30

ekle: ?'ler doldurulur, satır eklenir, id = 1.

kisileridadsoyadyas1AhmetYılmaz302AyşeDemir40

İkinci kişi id = 2 ile eklenir.

kisileridadsoyadyas1AhmetAkdeniz352AyşeDemir40

WHERE ad = ?: Ahmet'in soyadı ve yaşı değişir.

kisileridadsoyadyas1AhmetAkdeniz352AyşeDemir40

listele tüm satırları ve --- yazar.

kisileridadsoyadyas1AhmetAkdeniz352AyşeDemir40

Ahmet'in satırı silinir.

kisileridadsoyadyas2AyşeDemir40

Tabloda yalnızca Ayşe kaldı.

kisileridadsoyadyas2AyşeDemir40

Bağlantı kapanır; veri kisiler.db'de kalıcıdır.

public static void main(String[] args) throws SQLException {
  String url = "jdbc:sqlite:kisiler.db";
  try (Connection conn = DriverManager.getConnection(url);
       Statement stmt = conn.createStatement()) {
    stmt.execute("CREATE TABLE IF NOT EXISTS kisiler ("
        + "id INTEGER PRIMARY KEY, ad TEXT NOT NULL, "
        + "soyad TEXT NOT NULL, yas INTEGER NOT NULL)");
    ekle(conn, "Ahmet", "Yılmaz", 30);
    ekle(conn, "Ayşe", "Demir", 40);
    guncelle(conn, "Ahmet", "Akdeniz", 35);
    listele(conn);
    sil(conn, "Ahmet");
    listele(conn);
  }
}

İleri JDBC

Otomatik Anahtar: getGeneratedKeys

String sql = "INSERT INTO ogrenciler(ad, bolum_id) "
    + "VALUES(?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql,
        Statement.RETURN_GENERATED_KEYS)) {
  ps.setString(1, "Ece");
  ps.setInt(2, 3);
  ps.executeUpdate();
  try (ResultSet k = ps.getGeneratedKeys()) {
    if (k.next()) {
      System.out.println("Yeni id: " + k.getInt(1));
    }
  }
}
Çıktı
Yeni id: 5
  • Yeni satırın id'sini veri tabanı belirler; program onu getGeneratedKeys() ile öğrenir. Örnek: yeni siparişin kalemlerini eklemek için siparişin id'si gerekir.
  • prepareStatement'a Statement.RETURN_GENERATED_KEYS verilir; dönen ResultSet'in tek sütunu yeni anahtardır.
  • Tabloda 4 öğrenci vardı; Ece id = 5 aldı.
  • Silinen kaydın numarası başka kayda geçmemeliyse (fatura, sipariş no) AUTOINCREMENT kullanılır. MySQL'deki karşılığı AUTO_INCREMENT.
1, 2, 3 eklendi; 3 silindi; yeni satır →id
id INTEGER PRIMARY KEY3 (en büyük id + 1)
id INTEGER PRIMARY KEY AUTOINCREMENT4 (kullanılmış numara verilmez)

İleri JDBC

Satırdan Nesneye: DAO Deseni

class Ogrenci {
  private final int id;
  private final String ad;
  Ogrenci(int id, String ad) {
    this.id = id;
    this.ad = ad;
  }
  public String toString() {
    return "#" + id + " " + ad;
  }
}
OgrenciDao metoduSQLDöndürür
ekle(ad, bolum)INSERTyeni id
bul(id)SELECT … WHERE id = ?Ogrenci ya da null
bolumdekiler(bolum)SELECT … WHERE bolum_id = ?List<Ogrenci>
sil(id)DELETEsilindi mi (boolean)
main, arayüzList<Ogrenci> ile çalışırOgrenciDaoSQL yalnız buradaJDBC + sürücüConnection, PreparedStatementokul.dbtablolaristek ↓↑ nesneler
  • Nesne yönelimli programda her satır bir nesneye dönüştürülür: tablo ≈ sınıf, satır ≈ nesne.
  • DAO (Data Access Object, veri erişim nesnesi): bir tablonun bütün SQL'ini tek sınıfta toplar. Programın geri kalanı SQL görmez.
  • Veri tabanı değişirse yalnızca DAO değişir. Bağlantı DAO'ya yapıcıyla verilir.
  • Bu eşlemeyi JPA, Hibernate gibi ORM (nesne-ilişkisel eşleme) kütüphaneleri otomatik yapar.

İleri JDBC · Adım adım kod izleme

Satırdan Nesneye Adım Adım

static List<Ogrenci> bolumdekiler(Connection conn,
    int bolum) throws SQLException {
  List<Ogrenci> liste = new ArrayList<>();
  String sql = "SELECT id, ad FROM ogrenciler "
      + "WHERE bolum_id = ? ORDER BY id";
  try (PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setInt(1, bolum);
    try (ResultSet rs = ps.executeQuery()) {
      while (rs.next()) {
        liste.add(new Ogrenci(rs.getInt("id"),
                              rs.getString("ad")));
      }
    }
  }
  return liste;
}
// main içinde:
List<Ogrenci> liste = bolumdekiler(conn, 1);
for (Ogrenci o : liste) System.out.println(o);
liste (List<Ogrenci>)boş: []

main metodu çağırır: bolum = 1. Bağlantı main'de açıktır.

liste (List<Ogrenci>)boş: []

Boş bir liste oluşturulur; sorgu ? ile hazırlanır.

rs (bölüm 1)idad1Ada3Cemliste (List<Ogrenci>)boş: []imleç ilk satırın önünde

? = 1 bağlanır, sorgu çalışır: bölüm 1'de Ada ve Cem var.

rs (bölüm 1)idad1Ada3Cemimleçliste (List<Ogrenci>)Ogrenci #1 Ada

İlk satır: id = 1, ad = Ada okunur; new Ogrenci(1, "Ada") listeye eklenir.

rs (bölüm 1)idad1Ada3Cemimleçliste (List<Ogrenci>)Ogrenci #1 AdaOgrenci #3 Cem

İkinci satırdan ikinci nesne: Ogrenci #3 Cem.

rs (bölüm 1)idad1Ada3Cemliste (List<Ogrenci>)Ogrenci #1 AdaOgrenci #3 Cemrs ve ps kapandı

next() false: try blokları biter, rs ve ps kapanır. Bağlantıyı onu açan main kapatır.

liste (List<Ogrenci>)Ogrenci #1 AdaOgrenci #3 Cemsatır değil nesne döner

Metot List<Ogrenci> döndürür (2 öğe); çerçevesi yığından kalkar.

liste (List<Ogrenci>)Ogrenci #1 AdaOgrenci #3 Cem

Her nesne toString() ile yazılır. main SQL'den habersizdir.

MySQL

Aynı Kod, Başka Veri Tabanı: MySQL

String url = "jdbc:mysql://localhost:3306/testdb";
String kullanici = "root";
String sifre = "gizli";
String sql = "SELECT id, name, email FROM users";
try (Connection conn =
         DriverManager.getConnection(url, kullanici, sifre);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql)) {
  while (rs.next()) {
    System.out.println(rs.getInt("id") + " "
        + rs.getString("name"));
  }
} catch (SQLException e) {
  System.out.println(e.getMessage());
}
  • JDBC kodu aynıdır; değişenler: adres, kullanıcı adı, şifre ve sürücü jar'ı (mysql-connector-j).
  • MySQL bir sunucudur: önce kurulup çalıştırılmalı, testdb veri tabanı oluşturulmalıdır.
  • Sürücü sınıfı com.mysql.cj.jdbc.Driver'dır; eski com.mysql.jdbc.Driver adı kullanımdan kalkmıştır.
  • Gerçek projelerde şifre koda yazılmaz; ayar dosyasından veya ortam değişkeninden okunur.

İşlemler

İşlem (Transaction) ve ACID

  • Havale: Ece'nin hesabına 300 TL ekle ve Ali'nin hesabından 300 TL düş. İki UPDATE'ten biri olur diğeri olmazsa para yoktan var olur ya da kaybolur.
  • İşlem (transaction): tek bir iş birimi olarak yürütülen bir veya daha fazla SQL komutu: ya hepsi ya hiçbiri.
  • JDBC'de varsayılan auto-commit açıktır: her komut hemen kalıcı olur.
  • conn.setAutoCommit(false) → komutlar → conn.commit() (kalıcı yap) veya conn.rollback() (geri al).
Atomicity

Bölünmezlik: işlemin ya tamamı ya hiçbiri uygulanır.

Consistency

Tutarlılık: işlem sonunda tüm kısıtlar (ör. bakiye ≥ 0) sağlanır.

Isolation

Yalıtım: eş zamanlı işlemler birbirinin yarım hâlini görmez.

Durability

Kalıcılık: commit edilen veri, elektrik kesilse de kaybolmaz.

setAutoCommit(false)UPDATE (+300)UPDATE (−300)commit(): kalıcırollback(): hiçbiri

İşlemler

Havale Örneği: Kurulum ve Sonuç

static final String YATIR =
    "UPDATE hesaplar SET bakiye = bakiye + ? WHERE id = ?";
static final String CEK =
    "UPDATE hesaplar SET bakiye = bakiye - ? WHERE id = ?";

public static void main(String[] args) throws SQLException {
  String url = "jdbc:sqlite::memory:";  // bellekte, geçici
  try (Connection conn = DriverManager.getConnection(url);
       Statement st = conn.createStatement()) {
    st.execute("CREATE TABLE hesaplar (id INTEGER PRIMARY KEY, "
        + "sahip TEXT, bakiye INTEGER CHECK (bakiye >= 0))");
    st.execute("INSERT INTO hesaplar(sahip, bakiye) "
        + "VALUES ('Ali', 1000), ('Ece', 500)");
    havale(conn, 1, 2, 300);    // Ali → Ece 300 TL
    havale(conn, 1, 2, 5000);   // Ali'nin parası yetmez
    yazdir(conn);
  }
}
Çıktı
300 TL aktarıldı.
Geri alındı: 5000 TL
Ali: 700
Ece: 800
  • CHECK (bakiye >= 0): bakiyeyi eksiye düşüren komut SQLException fırlatır.
  • İkinci havalede Ece'ye 5000 TL yatırılmıştı; rollback bunu da geri aldı.
  • yazdir, listele gibi bir SELECT döngüsüdür.

İşlemler

commit ve rollback: Adım Adım

işlem başladıidsahipbakiye1Ali10002Ece500

Auto-commit kapatılır: bundan sonraki değişiklikler commit'e kadar geçicidir.

geçici (commit yok)idsahipbakiye1Ali10002Ece800

Ece'ye 300 TL yatırılır: 500 → 800.

geçici (commit yok)idsahipbakiye1Ali7002Ece800

Ali'den 300 TL çekilir: 1000 → 700. Kısıt sağlanıyor.

kalıcı (commit edildi)idsahipbakiye1Ali7002Ece800

commit(): iki değişiklik birlikte kalıcı olur.

kalıcı durumidsahipbakiye1Ali7002Ece800

finally: auto-commit yeniden açılır; metot biter.

geçici (commit yok)idsahipbakiye1Ali7002Ece5800

İkinci çağrı: Ece'ye 5000 TL yatırılır (geçici): 800 → 5800.

geçici (commit yok)idsahipbakiye1Ali7002Ece5800

700 − 5000 < 0 olurdu: CHECK bozulur, SQLException fırlar.

rollback: geri alındıidsahipbakiye1Ali7002Ece800

rollback(): geçici yatırma da geri alınır. Para ne kayboldu ne çoğaldı.

static void havale(Connection conn, int kimden, int kime,
                   int tutar) throws SQLException {
  conn.setAutoCommit(false);         // işlem başlar
  try (PreparedStatement p1 = conn.prepareStatement(YATIR);
       PreparedStatement p2 = conn.prepareStatement(CEK)) {
    p1.setInt(1, tutar);  p1.setInt(2, kime);
    p1.executeUpdate();              // alıcıya yatır
    p2.setInt(1, tutar);  p2.setInt(2, kimden);
    p2.executeUpdate();              // göndericiden çek
    conn.commit();                   // kalıcı yap
    System.out.println(tutar + " TL aktarıldı.");
  } catch (SQLException e) {
    conn.rollback();                 // geri al
    System.out.println("Geri alındı: " + tutar + " TL");
  } finally {
    conn.setAutoCommit(true);
  }
}

İşlemler

Toplu İşlem: addBatch ve executeBatch

conn.setAutoCommit(false);
try (PreparedStatement ps = conn.prepareStatement(
        "INSERT INTO olcumler(deger) VALUES(?)")) {
  for (int i = 1; i <= 1000; i++) {
    ps.setInt(1, i);
    ps.addBatch();
  }
  int[] sonuc = ps.executeBatch();
  conn.commit();
  System.out.println(sonuc.length + " komut, ilki "
      + sonuc[0] + " satır etkiledi");
} catch (SQLException e) {
  conn.rollback();
}
conn.setAutoCommit(true);
Çıktı
1000 komut, ilki 1 satır etkiledi
  • addBatch() doldurulmuş komutu bir kuyruğa ekler; executeBatch() kuyruktakilerin hepsini gönderir ve her komutun etkilediği satır sayısını int[] olarak döndürür.
  • Auto-commit açıkken her INSERT ayrı bir işlemdir ve diske ayrı ayrı yazılır. Disk üzerindeki bir .db dosyasıyla yapılan bir denemede 1000 satır: tek tek yaklaşık 2,6 s, toplu ve tek işlemde yaklaşık 0,01 s (makineye göre değişir).
  • Hepsi tek işlem olduğu için biri başarısız olursa rollback() tüm kuyruğun etkisini geri alır: ya hepsi ya hiçbiri.
  • Çok büyük yüklemelerde kuyruk parça parça gönderilir (ör. her 500 addBatch'te bir executeBatch); bellek şişmez.

Özet

Özet

KonuÖzü
Veri tabanıKalıcı, düzenli, sorgulanabilir veri; VTYS yönetir. Tablo, satır, sütun, birincil anahtar.
SQLiteSunucusuz, gömülü, tek dosya. Adres: jdbc:sqlite:sample.db; sürücü jar'ı classpath'te olmalı.
SQLCREATE TABLE, INSERT, SELECT, UPDATE, DELETE; WHERE, ORDER BY, LIMIT.
İlişkilerYabancı anahtar; JOIN … ON; GROUP BY / HAVING; NULL için IS NULL.
JDBCDriverManager → Connection → Statement / PreparedStatement → ResultSet.
Çalıştırmaexecute (tablo), executeUpdate → int, executeQuery → ResultSet; while (rs.next()).
GüvenlikKullanıcı girdisi her zaman ? ve setXxx ile; SQL metnine eklenmez.
İşlemsetAutoCommit(false), commit(), rollback(): ya hepsi ya hiçbiri (ACID).
Kaynaklartry-with-resources: rs, stmt, conn ters sırada kapanır; SQLException kontrol edilen istisnadır.

Özet

Sık Yapılan Hatalar

Sürücü classpath'te değilJar eklenmemiş:No suitable driver found for jdbc:sqlite:sample.db
SQLException'ı yakalamamaktry veya throws yok (derleme hatası):unreported exception SQLException; must be caught or declared to be thrown
Tablo yok veya adı yanlışTablo oluşturulmadan sorgu:no such table: users
Metni tırnaksız yazmakVALUES(Ahmet, …):no such column: Ahmet
Kesme işaretli metni birleştirmekVALUES('Ali'nin'):near "nin": syntax error
Yanlış execute metoduexecuteQuery ile INSERT:query does not return ResultSet
İndisi 0'dan başlatmakrs.getString(0); sıra 1'den başlar:column 0 out of bounds [1,3]
WHERE'siz UPDATE / DELETEHata vermez; tüm satırları değiştirir ya da siler.

Özet

Sık Yapılan Hatalar: İlişkiler, Gruplama, JDBC

= NULL ile karşılaştırmakWHERE bolum_id = NULL hata vermez, 0 satır döner. Doğrusu IS NULL.
ON'suz birleştirmeFROM ogrenciler, bolumler: her satır her satırla eşleşir, 4 × 3 = 12 satır.
Belirsiz sütun adıİki tabloda da ad var; tablo adı yazılmamış:ambiguous column name: ad
WHERE'de toplama fonksiyonuGrup koşulu HAVING'e yazılır:misuse of aggregate: SUM()
Yabancı anahtar denetimini açmamakSQLite'ta PRAGMA foreign_keys = ON olmadan bolum_id = 9 sessizce kabul edilir.
wasNull()'u geç çağırmakSon okunan sütuna bakar; ilgili getInt'in hemen ardından çağrılmalıdır.
Gruplanmayan sütunu seçmekSELECT musteri, tutar … GROUP BY musteri: SQLite gruptaki herhangi bir satırın tutarını getirir; birçok veri tabanında hatadır.
Auto-commit açıkken binlerce INSERTHer satır ayrı işlem: çok yavaş. setAutoCommit(false), addBatch, commit.

Özet

Örnek Problemler 1

Problem 1: SELECT hangi satırları döndürür?

CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO users(name) VALUES ('Ali');
INSERT INTO users(name) VALUES ('Can');
DELETE FROM users WHERE id = 2;
INSERT INTO users(name) VALUES ('Ece');
UPDATE users SET name = 'Efe' WHERE id = 1;
SELECT * FROM users;

Problem 2

Kişi programı (kisiler.db ile) ikinci kez çalıştırılıyor. Çıktının ilk bloğu (ilk ---'e kadar) ve son bloğu nedir?

Cevapları göster
  1. (1, Efe) ve (2, Ece). Can silinince en büyük id 1 kaldı; Ece en büyük id + 1 = 2 aldı (id yeniden kullanıldı). UPDATE yalnızca 1 numaralı satırı değiştirdi.
  2. Dosya kalıcıdır; ilk çalıştırmadan Ayşe (id 2) kalmıştı ve IF NOT EXISTS tabloyu silmez. Yeni kayıtlar id 3 (Ahmet) ve 4 (Ayşe) alır. İlk blok: 2 Ayşe Demir 40, 3 Ahmet Akdeniz 35, 4 Ayşe Demir 40. Son blok: 2 Ayşe Demir 40, 4 Ayşe Demir 40 (aynı kişi iki kez kayıtlı).

Özet

Örnek Problemler 2

Problem 3: Giriş kontrolü

String sql = "SELECT * FROM kisiler WHERE ad = '" + ad
           + "' AND sifre = '" + sifre + "'";
ResultSet rs = stmt.executeQuery(sql);
boolean girisBasarili = rs.next();

Tabloda 3 kişi var. Kullanıcı ad olarak ahmet, şifre olarak ' OR '1'='1 yazarsa oluşan SQL nedir, kaç satır döner, giriş başarılı olur mu?

Problem 4

havale metodundan setAutoCommit(false), commit() ve rollback() satırları çıkarılsaydı (her komut kendi başına kalıcı) program sonunda Ali ve Ece'nin bakiyesi ne olurdu?

Cevapları göster
  1. SELECT * FROM kisiler WHERE ad = 'ahmet' AND sifre = '' OR '1'='1'. AND, OR'dan önce uygulanır: (… AND …) OR '1'='1' her satırda doğru → 3 satır, rs.next() true: şifresiz giriş! Çözüm: WHERE ad = ? AND sifre = ? ile PreparedStatement → 0 satır.
  2. Ali: 700, Ece: 5800. İkinci havalede yatırma hemen kalıcı oldu, çekme CHECK yüzünden başarısız oldu ve geri alacak işlem yoktu: 5000 TL yoktan var oldu (program yine de “Geri alındı” yazar).

Özet

Alıştırmalar

  1. Silme programını değiştirin: executeUpdate 0 döndürürse Kayıt bulunamadı. yazsın.
  2. ogrenciler(id, ad, vize, final) tablosunu oluşturun; Ada (60, 70), Bora (30, 50), Cem (80, 40) öğrencilerini PreparedStatement ile bir döngüde ekleyin. Ortalaması (vize %40, final %60) 50 ve üzeri olanları büyükten küçüğe listeleyin.
  3. Menülü bir program yazın: 1-Ekle, 2-Güncelle, 3-Sil, 4-Listele. Scanner ve switch kullanın; güncellenecek/silinecek id yoksa Kullanıcı bulunamadı. yazsın.
  4. Bir oyunun skorlarını skorlar(oyuncu, puan) tablosuna kaydedin ve en yüksek 10 skoru listeleyin.
Cevaplar / ipuçları
  1. int sayi = stmt.executeUpdate(sql); if (sayi > 0) System.out.println("Veri silindi!"); else System.out.println("Kayıt bulunamadı.");
  2. SELECT ad, vize * 0.4 + final * 0.6 AS ort FROM ogrenciler WHERE vize * 0.4 + final * 0.6 >= 50 ORDER BY ort DESC · Çıktı: Ada 66.0, Cem 56.0 (Bora 42.0 elenir). getDouble("ort") ile okunur.
  3. nextInt()'ten sonra nextLine() ile satır sonu temizlenir. UPDATE … WHERE id = ? ve DELETE … WHERE id = ? için executeUpdate'in döndürdüğü sayı 0 ise kullanıcı yoktur.
  4. SELECT oyuncu, puan FROM skorlar ORDER BY puan DESC LIMIT ? ve ps.setInt(1, 10). Eklemede INSERT INTO skorlar(oyuncu, puan) VALUES(?, ?).

Özet

Kaynaklar

  • Oracle, The Java Tutorials: JDBC Database Access (Establishing a Connection; Processing SQL Statements with JDBC; Using Prepared Statements; Using Transactions).
  • Oracle, Java SE 21 API Documentation: java.sql paketi (DriverManager, Connection, PreparedStatement, ResultSet).
  • SQLite belgeleri, sqlite.org: SQL As Understood By SQLite; Datatypes In SQLite; Appropriate Uses For SQLite.
  • Xerial, SQLite JDBC Driver, github.com/xerial/sqlite-jdbc.
  • P. Deitel ve H. Deitel, Java: How to Program, 11. baskı, Pearson (Accessing Databases with JDBC).
  • C. S. Horstmann, Core Java, Volume II: Advanced Features, 12. baskı, Pearson (Database Programming).
  • OWASP, SQL Injection Prevention Cheat Sheet.

Son

Bölüm 11: Veri Tabanı Programlama
Dersin sonu · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.