JDBC, Veritabanları, Classpath Falan Filan

JDBC Uygulama arabirimi (API) herhangi bir tablosal veriye, özellikle ilişkisel veritabanlarında (RDMS) veriye erişmek için kullanılan Java API'sidir. JDBC aşağıdaki üç programlama etkinliğini yöneten java uygulamaları yazmamızda yardımcı olur:
  1. Bir veritabanı bağlanma
  2. Veritabanına sorgular ve güncelleme cümleleri gönderme
  3. Sorgumuza cevap olarak gelen sonuçları alma ve işleme
Aşağıdaki basit kod parçacığı yukarıdaki üç adımı gerçekleştiren basit bir örneği göstermektedir:

// ilgili veritabanina bir baglanti yaratiyoruz
Connection con = DriverManager.getConnection("jdbc:myDriver:wombat", "myLogin","myPassword");
//sorguyu calistiracagimiz Statement yaratiyoruz 
Statement stmt = con.createStatement();
// sorguyu calistiralim, sonuclar ResultSet tipinde olacaktir
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1"); 
// elde ettigimiz sonuclari dolasip gelen her bir satirin verilerine ulasalim 
while (rs.next()) {
 int x = rs.getInt("a");
 String s = rs.getString("b");
 float f = rs.getFloat("c");
}

Bu basit kod parçacığı DriverManager yardımıyla parametrelerle belirlenmiş olan veritabanına bir bağlantı yaratıyor, bağlantı için giriş yapıyor. SQL sorgumuzu veritabanına taşıyacak olan Statement nesne örneği oluşturuyor. ResultSet nesnesini sorgumuzu çalıştırarak yaratıyor. Basit bir while döngüsü içerisinde sorgu sonuçlarını teker teker alıp işliyor.

JDBC kullanımı yukarıdaki adımlar doğrultusunda oldukça kolaydır. Bu kolaylığı çoğu kişi için zorlu bir hale getiren kısım bağlantı alma kısmıdır. Bağlantıyı sorunsuz bir şekilde aldıktan sonra bu bağlantı üzerinde Statement ve PreparedStatement kullanarak sorgu çalıştırmanın herhangi bir zorluğu yoktur.

Kullanılan veritabanına göre bağlantı alma kısmı çok az değişiklik gösterir. Bağlantısını alacağımız veritabanıyla ilgili olarak o veritabanının hangi sürücüsünü kullanacağımızı, hangi veritabanına bağlanacağımızı ve hangi kullanıcı adı ve şifresini kullanacağımızı belirtmemiz gerekir. Bu bilgileri doğru bir şekilde verdikten sonra bağlantı almada herhangi bir sorun çıkmayacaktır. Bu noktada acemi kullanıcıların en çok zorlandıkları kısım ilgili veritabanının JDBC sürücülerine ulaşmak ve bu sürücüleri kullanmaktır. Aşağıda sık kullanılan veritabanları ve ilgili JDBC sürücülerine erişmek üzere bağlantı adresleri vardır:

MySQL tarafından sunulan ve JDBC gerçekleştirimi sunan doğal Java sürücüsünün adı Connector/J'dir. Şu adresten indirilebilir.

Microsoft SQL Server için kullanılan JDBC sürücüsü adresinden indirilebilir.

Oracle için gereken JDBC sürücüleri adresinden indirilebilir

PostgreSQL için gereken JDBC sürücüleri adresinden indirilebilir.

Java DB, Java ile birlikte dağıtılan ve verileri dosya biçiminde saklayan eski adı Derby olan veritabanı, ayrıntılı bilgi için tıklayın.

Bunların dışında herhangi bir veritabanı kullanılıyorsa google arama motorunda Veritabani Yönetim Sistemi adı ile birlikte JDBC Driver anahtar kelimeleri birlikte kullanılarak ("MySQL JDBC Driver" şeklinde mesela) sürücüleri varsa erişmek mümkündür. Bu yukarıdaki sürücülere ek olarak farklı firmaların ücretli ve ücretsiz sunabildiği farklı JDBC sürücü çözümleri de vardır. Bu çözümlerden uygun olanı seçmek tamamen programcının ve firmanın tercihine kalmıştır.

Sürücünün sorunsuz elde edildiğini varsayarak, Eclipse üzerinde MySQL veritabanına bağlanan oldukça basit bir veritabanı kullanan örnek proje anlatmaya çalışayım.

Yaptığım bu örnek projede kullandığımız veritabanında Deneme isminde tek bir tablo ve bu tablonun da Saha isminde tek bir sahası var. Yazacağım oldukça basit örnek bu sahaya o anki zamanı (System.currentTimeMillis() ile alınan) yazacak ve tablodaki tüm verileri çeken bir sorgu çalıştırıp, o verileri ekrana yazacak.

Bu projenin asıl can alıcı kısmı DBConnectionManager sınıfıdır. Bu sınıf veritabanı bağlantıları için bir bağlantı havuzu oluşturmakta ve gerektikçe bu havuzdan bir bağlantıyı kullanıcının isteği doğrultusunda döndürmektedir. Bağlantı havuzu kullanımının temel gerekçesi, her seferinde bağlantı oluşturmak için gereken işlem ve bağlantı (veritabanın ağ üzerinde başka noktada olduğu durumlarda özellikle) süresini en aza indirmek için hali hazırda açık bağlantılar saklamaktır. Bu bahsedilen sınıfı İnternet üzerinden bulup, biraz değiştirirek şu anki haline getirdim. Sınıfın burada bahsedeceğim en önemli kısmı init() metodudur.

/**
* Ozellikleri yukleyip, degerleri kullanarak yeni bir nesne yaratir
*/
private void init() {
 if (dbp == null)
  dbp = DBParametersReader.readParameters();
 loadDriver(dbp);
 createPool(dbp);
}

DBParametersReader sınıfının parametre dosyasından okuduğu parametreleri yerel bir değişken olarak saklamaktadır (dbp). Daha sonra loadDriver yardımıyla kullanılacak olan sürücü yüklenmektedir. Bu sınıfın önemli bir özelliği parametre dosyasında tanımlı olan herhangi bir sürücüyü kullanabilmesidir. Bu sürücünün ilgili kütüphanesinin elbette classpathte olması gereklidir. Evet ilk defa classpath ifadesi geçti. Çoğu acemi kullanıcının bu ifadeden kaynaklı sorunlarla uğraştığını tahmin edebiliyorum. Kütüphanelerin classpathte olmasını özel olarak anlatacağım. Konumuza geri dönelim. Bu sürücü bilgisini ve parametre dosyasında diğer bilgileri kullanarak sürücü sınıfı yüklendikten sonra (loadDriver) bağlantı havuzumuz oluşturulmaktadır (createPool).

private void loadDriver(DBParameters dbp) {
 try {
  Driver driver = (Driver) Class.forName(dbp.dbDriver).newInstance();
  DriverManager.registerDriver(driver);
  log("Registered JDBC driver " + dbp.dbDriver);
 } catch (Exception e) {
  log("Can't register JDBC driver: " + dbp.dbDriver + ", Exception: " + e);
 }
}

Sürücü yükleme kodunun yaptığı işlem bağlantı almada kullanılacak olan sürücü sınıfının bulunabilmesi için sisteme kaydedilmesidir. Kaydetme işlemi tamamlandıktan sonra bağlantılar bu sürücü sınıfı yardımıyla yaratılacaktır. Bizim yazmış olduğumuz DBConnectionManager sınıfının içerisinde her ne kadar havuz oluşturabilmek için oldukça karmaşık yazılmış olsa da aşağıdaki basit kodlar yardımıyla da bağlantı alınabilmektedir. Aşağıdaki kod parçası veritabanı sürücüsünü yükleme, bağlantı yaratma, bağlantı üzerinde sorgu çalıştırma işlemlerinin oldukça kolay olduğunu gösteren bir örnektir. Örneği inceleyelim ve örnekte varolan önemli noktaları değerlendirelim:

try {
 Driver driver = (Driver) Class.forName("com.mysql.jdbc.Driver").newInstance();
 DriverManager.registerDriver(driver);
 Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/vt", "root", "tekrei");
 // veritabanina kayit ekleme
 PreparedStatement ps = con.prepareStatement("INSERT INTO Deneme (Saha) VALUES ("+System.currentTimeMillis()+")");
 ps.execute();
 //Veritabanindan kayit cekme
 ps = con.prepareStatement("SELECT * FROM Deneme");
 // cektigimiz kayitlar ResultSet nesnesi seklinde bize donuyor
 ResultSet rs = ps.executeQuery();  
 // rs nesnesinde kayitlarin hepsini dolasip
 while(rs.next()){
  // sahalari ekrana yaziyoruz
  System.out.println(rs.getString("Saha"));
 }
} catch (Exception e) {
 e.printStackTrace();
}

Sürücü yükleme Class.forName("com.mysql.jdbc.Driver").newInstance() ve DriverManager.registerDriver(driver) satırlarıyla yapılmaktadır. Bu satırların ilkinde MySQL tarafından sunulmuş olan ve sınıf yolu com.mysql.jdbc.Driver olan sürücüyü kullanacağımızı sisteme belirtmiş oluyoruz. Daha sonra sürücü hatasız bir şekilde yüklenirse (Java bildiğinizi varsayarak, hata durumunda istisna olarak yakalanacağını hatırlatmak isterim) DriverManager yardımıyla bir bağlantı yaratıyoruz (Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/vt")). Bu satırda dikkat edilmesi gereken, URL dediğimiz bilginin bağlanılacak olan veritabanına dair bilgiler içeriyor olduğudur. localhost veritabanı sunucunu, 3306 bağlantı portunu ve vt veritabanın ismini belirtmektedir. Bu URL bilgileri kullanılan JDBC sürücüsüne göre değişiklik gösterecektir. JDBC sürücüsünün nasıl belgelerinde veya rehberlerinde bu bilgileri bulmak mümkündür (Bu yazının sonuna önemli olanları eklenmiştir). Ondan sonraki satırlar sırasıyla bir SQL INSERT işlemi ve SQL SELECT işlemi çalıştırılmaktadır.

Şimdiye kadar yazdıklarımla, bazı konuların ayrıntısına girmesem de, basitçe bir MySQL veritabanına bağlanmayı rahatça yapabilirsiniz. Önerim benim aşağıda bağlantısını gönderdiğim örnek projeyi inceleyerek, oradaki, DBConnectionManager, DBParameters ve DBParametersReader sınıflarını kullanarak sadece vt.properties dosyasını değiştirerek farklı veritabanları için bağlantı havuzu destekli bir yapı kullanabilirsiniz. Bütün bu sınıfların nasıl kullanıldığına dair bilgiler yorumlar şeklinde mevcut. Yapmanız gereken projenize bu sınıfları eklemek ve vt.properties dosyasını kullandığınız veritabanı, JDBC sürücüsü bilgilerine göre değiştirmek.

Gelelim eksik kalan konulara, en azından eksik bıraktığımı düşündüğüm konulara. Kolay olandan başlayalım.

Her veritabanı sürücüsü için farklı URL ve Driver bilgilerinin olduğunu söylemiştim. vt.properties dosyasında bu farklı bilgiler kullanılmalıdır. Aşağıdaki her veritabanı türü için en temel sürücü ve URL bilgilerine örnekler göreceksiniz. Bu bilgiler yetersiz gelirse ilgili JDBC sürücüsünün rehberinden veya nasıl kullanıldığını anlatan belgelerden yararlanarak edinebilirsiniz:

MySQL zaten anlatıldı ancak temel olarak sürücü için com.mysql.jdbc.Driver sınıfı ve URL olarak jdbc:mysql://localhost:3306/vt şeklinde kullanmak gerekir. Bu URL'de değiştirmeniz gereken sadece sunucu adresi (localhost), farklı bir port kullandıysanız port (3306) ve veritabanı (vt) ismidir.

(Bununla ilgili olarak MySQL connector belgesinde "If you are going to use the driver with the JDBC DriverManager, you would use "com.mysql.jdbc.Driver" as the class that implements java.sql.Driver." şeklinde bir ifade yer alır, bu sınıfı kullanmamızın nedeni budur :D Ayrıca URL ile ilgili biçimlendirme hakkında connector belgesinden yararlanılmaktadır. Bu bilgilere hangi veritabanı için olursa olsun bir google araması, olmadı ilgili sürücü belgesi yardımıyla ulaşabilirsiniz :) Aramaya inanmalısınız ;) )

Oracle:
 Sürücü = oracle.jdbc.driver.OracleDriver
 URL = jdbc:oracle:thin:@oracle:1521:orcl

(Oracle tarafından sunulan oldukça farklı seçenekler var, bu yüzden oracle'ın bu konularla ilgili belgelerini okumak ve farklı kaynakları incelemek şarttır :D )

PostgreSQL:
 Sürücü = org.postgresql.Driver
 URL = jdbc:postgresql://host:port/database

SQL Server:
 Sürücü = com.microsoft.sqlserver.jdbc.SQLServerDriver
 URL = jdbc:sqlserver://[serverName[\instanceName][:portNumber]][;property=value[;property=value]]

Java DB:
 Sürücü: org.apache.derby.jdbc.EmbeddedDriver
 URL: jdbc:derby:[propertyList]


Bu örnekler temel olarak ilgili veritabanlarına bağlanmanıza yetecektir. Ama önerim yukarıdaki çözümlere ek olarak internet üzerinde basit aramalarla ayrıntılı yazıları incelemenizdir (Örnek arama sorguları "Oracle JDBC Example" "MySQL how to use JDBC" vb.)

Bir başka önemli konu, herkesin kafasını yeterince karıştıran bir konu. CLASSPATH nedir, nasıl ayarlanır.

CLASSPATH kavramı şu noktadan çıkmaktadır. Java ile standart olarak dağıtılmayan, Java SDK'nın bir parçası olmayan kütüphanelerin sunduğu hizmetlere ve sınıflara erişebilmek için bu kütüphaneleri Java'nın çalışma ve derleme zamanında erişilebilir kılmak gerekiyor. Bunu da bu kütüphane dosyalarını (genellikle JAR oluyor bunlar) classpath içerisine yerleştirmek veya classpath ayarlarına bu kütüphanelerin bulunduğu konumu eklemek gerekiyor.

Bunu Eclipse projesi veya Netbeans projesinde yapmak kolaydır. Örneğin proje dizininiz içerisinde bir mysql.jar dosyasını projenizin kullanabilmesi için yapmanız gereken sadece Project Properties -> Java Build Path -> Libraries sekmesinde Add Jars seçeneğiyle ilgili jarı eklemektir. Netbeans içinde böyle bir menü yardımıyla rahatça yapılacaktır.


Kullanıcıları zorlayan kısmı, herhangi bir IDE kullanmadan bu kütüphanelerin "classpath"e nasıl eklendiğidir. Bunun iki yolu vardır. Birinci yol, pek tavsiye etmediğim ama işleri oldukça kolaylaştırdığı söylenen sistem classpathine bu kütüphanelerin eklenmesidir. Windowsta mesela ortam değişkenleri arasında CLASSPATH adında bir değişken ekleyip, bu değişkenin değerini ".;C:\SurucuDizini\SurucuJari.jar" şeklinde değiştirebiliriz. Böylece classpath tanımlamış oluruz. Ancak benim önerdiğim yöntem derleme ve çalıştırma yaparken bu kendimize özel classpath bilgilerini argüman olarak komutlara eklemektir.

javac -classpath c:\SurucuDizini\SurucuJari.jar AnaProgram.java

java -classpath c:\SurucuDizini\SurucuJari.jar AnaProgram

şeklinde kullanmanızdır. Elbette bu şekilde kullandığınız zaman projelerinizi ileride dağıttığınızda bu kütüphane jarlarını da beraber dağıtmanız ve kullanıcıları classpath ayarlarından kurtarmak için ilgili betik dosyalarını (linux için sh, windows için bat dosyalarını) kullanıcılara sunmanız gereklidir. Aşağıda örnek iki betik dosyasını görebilirsiniz:

calistir.sh

#!/bin/bash

java -classpath .:kumanifest.jar:./lib/derby.jar:./lib/toplink-essentials.jar:./lib/swingx.jar:./lib/nimrodlf.jar:./lib/commons-beanutils.jar:./lib/commons-collections.jar:./lib/commons-digester.jar:./lib/commons-javaflow.jar:./lib/commons-logging.jar:./lib/GUIAraclar.jar:./lib/itext.jar:./lib/jasperreports.jar:./lib/jr-bsh-compiler.jar:./lib/mysql.jar:./lib/poi.jar net.kodveus.kumanifest.MainFrame

calistir.bat

java -classpath .;kumanifest.jar;./lib/derby.jar;./lib/toplink-essentials.jar;./lib/swingx.jar;./lib/nimrodlf.jar;./lib/commons-beanutils.jar;./lib/commons-collections.jar;./lib/commons-digester.jar;./lib/commons-javaflow.jar;./lib/commons-logging.jar;./lib/GUIAraclar.jar;./lib/itext.jar;./lib/jasperreports.jar;./lib/jr-bsh-compiler.jar;./lib/mysql.jar;./lib/poi.jar net.kodveus.kumanifest.MainFrame

Gördüğünüz gibi -classpath argümanı yardımıyla kullandığım tüm kütüphaneleri, lib dizini altında kullanıcıya sunduğum kütüphaneler bunlar, classpath içerisine eklemiş oluyorum. Ve son olarak paket bilgisiyle birlikte çalıştırılacak ana sınıfı veriyorum. Ve kullanıcının classpath ayarlamasına gerek kalmadan uygulamayı çalıştırmasını sağlamış oluyorum.

Umarım buraya kadar yazdıklarım anlaşılmaktadır. 2-3 saat sürecinde ortaya çıkarılan oldukça uzun bir yazı oldu. Anlaşılmayan, hatalı olduğunu düşündüğünüz noktaları belirtirseniz, düzeltmeleri ve açıklamaları yapmaya çalışırım.

(Biterken Radiohead - Creep akustik sürüm çalıyordu)

Örnek Eclipse Projesi

(Projeyi kullanabilmek için veritabanı yaratmanız ve içerisinde projede kullanılan bir sahalı bir tabloyu eklemeniz gereklidir. Yazı kapsamında olmadığı için nasıl yapıldığı anlatılmamıştır. MySQL Workbench veya SquirrelSQL kullanarak veritabanınıza erişip bu işlemleri kolayca yapabilirsiniz.)
Kaynaklar:

Trail: JDBC(TM) Database Access
Using JDBC with MySQL, Getting Started
SQL Tutorial
The PostgreSQL JDBC Interface
SQL Server 2005 JDBC Driver Documentation
Oracle JDBC Drivers release 11.1.0.7.0 - Production README

Using Java Database Connectivity (JDBC) with Oracle
Managing the Java classpath (Windows)
How Classes are Found
Using Java DB in Desktop Applications

1 yorum:

Adsız dedi ki...

Çok faydalı oldu, çok teşekkürler.