3 Aralık 2007 Pazartesi

JDBC

JDK 1.1 'den bu yana JDBC, Java 'nın temel taşı olarak standart paketle beraber geliyor. Anlamı ise Java Database Connectivity. Mevcut veritabanlarına kolay erişim için bir mekanizma sağlıyor. Java programları ile veritabanları arasında sürücüler (drivers) köprü oluşturmaktadır. Aynı kaynak kod ile birçok veritabanına ulaşabilirsiniz. Tabii ki bağlantı kurmak istediğiniz veritabanına ait sürücümüz mevcut olduğu sürece bu geçerlidir.

JDBC belirtimine uygun olarak farklı veritabanları için geliştirilen sürücüler ileri derecede bir esneklik sağlarlar. Ana kod paketi,” java.sql adı altında toplanmıştır. Ama J2EE uygulamaları daha karmaşık teknikler gerektirdiği için “javax.sql adındaki kod paketi de seçimlik bir kod paketi olarak eklenmiştir.

Piyasadaki JDBC sürücüleri 4 farklı çeşittedir:

1. Tip - JDBC-ODBC köprüsü: Windows platformunda çok popüler olan ve Microsoft tarafından geliştirilmiş olan ODBC (Open Database Connectivity) standart SQL işlemleri için kullanılan bir API'dır. ODBC daha sonra Visigenic ve Intersol tarafından Windows dışındaki bazı platformlara da taşınmıştır. Bu tip sürücüler, gelen JDBC istemlerini ODBC komutlarına çevirirler ve geri gelen cevapları tekrar JDBC ve Java nesnelerine çevirerek istemciye sunarlar. Bu tip sürücüler, istemci ve veritabanı arasında kullanılan arayüz sayısının fazla olması nedeniyle, ticari projelerin çoğunun gerektirdiği performansı sunamazlar. JDK (Java Development Kit) com.sun.odbc.JdbcOdbcDriver adı altında bu tip bir sürücü içerir

2. Tip - Java ve Platforma bağlı teknoloji karışımı: Bu tip sürücüler bir kısım Java ve diğer bir kısım veritabanına ve platforma özel teknolojiler kullanırlar. Aradaki arayüzlerin sayısının azalması performansı arttırıp bu tipi daha çekici hale getirmesine rağmen, 1. tip sürücülerde olduğu gibi bu tip sürücülerde de her istemciye platforma bağlı parça için bir kuruluş yapmak gerekir.

3. Tip - Arakat Veritabanı Erişim Sunucusu: En esnek mimariye sahip olan 3. tip JDBC sürücüleri, ortakat (middleware) yazılımlarından destek alırlar. Bu tip sürücüler birden fazla istemciyi birden fazla veritabanı sunucusuna bağlayabilirler. Genellikle, J2EE uygulama sunucuları, popüler veritabanı için özel, yüksek performanslı sürücüleri bulundururlar.

4. Tip - Saf Java JDBC sürücüsü: 2. Tip sürücülere alternatif olarak geliştirilmişlerdir. En büyük avantajları 100% Java yazılımları olup, istemci katında fazladan bir kuruluş yapmayı gerektirmemeleridir. Bu tür sürücüler, doğrudan veritabanına açılan Socketler aracılığıyla, veritabanına özel protokolleri kullanarak veri iletişimi sağlarlar. Oracle'ın Thin JDBC Driver bu tipe örnek olarak gösterilebilir.


JDBC ve SQL

Java DataBase Connectivity (JDBC) Java'dan bir SQL database'ine bağlanmak için kullanılan sistemdir. Bir database'e connection kurulur, bir SQL statement execute edilir ve result set değerlendirilir. JDBC hemen hemen bütün SQL Database'lerle çalışmaktadır. Bir database'de çalışan kod, değişiklik yapmadan diğerinde de çalışabilir. Yeterki kullanılan SQL komutları standart SQL komutları olsun ve o database'e ait özel bazı ifadeler içermesin.

Driver

JDBC'nin bir database'e bağlanabilmesi için öncelikle o database'e ait driver'ın yüklenmmesi gerekir. Driver bir Java class'ı olup standart JDBC classlarının belli bir database'le çalışmasını sağlar. Bir driver class'ı adıyla yüklenir.

Class.forName("com.anycompany.XxxDriver");

biçimide bir ifade adı verilen class'ı adıyla yükler. Belli bir database'in driver'ı o database'le birlikte gelebilir veya üçüncü firmalardan bulunabilir. bilinen bütün database'lerin çok sayıda JDBC driver'ları mevcuttur. Ancak database'i üreten firmanın driver'ını almak hem performans hem de güvenilirlik açısından faydalı olabilir.

JDBC-ODBC Köprüsü

Open DataBase Connectivity (ODBC) herhangi bir database'e bağlanmak için kullanılan standart bir sistemdir. Eğer bir database ODBC bağlantısını destekliyorsa, (herhangi bir dilde yazılmış) herhangi bir program bir driver aracılığıyla bağlanır. Bir server'a ODBC bağlantısı yapabilmek için o makinede bir data source tanımlanmalıdır. Java'da ODBC doğrudan kullanmaz, çünkü ODBC Java'nın standart nesne yapısına ve geleneğine uymaz. Ancak ODBC üzerinden JDBC bağlantısı yapan Driver'lar vardır. Bu driver yüklenerek, tanımlı bir data source üzerinden JDBC bağlantısı yapılabilir. Java ile birlike basit bir JDBC-ODBC Driver gelir. En bilinen driver Java'nın default implementation'u ile birlikte gelen driver'dır. Yüklenişi

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

şeklinde gerçekleştirilebilir.

Data Source Tanımlama

Her işletim sisteminin ve versiyonunun kendine has bir ODBC data source tanımlama biçimi vardır. Windows'ta ControlPanel de ODBC adı geçen bir bağlantıdan yola çıkarak data source tanımı yapılabilir.

Connection URL'i

Her database'in kendisine has bir Connection URL formatı vardır. Bazıları

protocol://hostname:port/database

şeklinde bir formata sahiptir. JDBC-ODBC kullanıldığında URL

jdbc:odbc:mydatasource

biçiminde olur. Bağlanılan database server'ın URL gibi bilgileri o data source yaratılırken zaten belirlenmiş olmalıdır

Database Connection'ları

Database'e Connection Kurma

Bir database'e connection kurmak için DriverManager'ın createConnection() method'u kullanılır.

Connection connection = DriverManager.getConnection("myurl", "myusername", "mypassword");

Elbette bu url'deki database'de kullanıcı username'i belirtilen password'le tanımlı olmalıdır.

Bir veritabanına bağlantı kuran bir örnek. Bu örnek hata vermiyorsa veritabanına bağlantı sağlanmış demektir.

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class ConnectionTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {
  8     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
  9     Class.forName(driverClass);       
 10     String url="jdbc:odbc:mydatasource";     
 11     String username="myusername";
 12     String password="mypassword";
 13     Connection connection = DriverManager.getConnection(url,username,password);
 14     System.out.println("Connected to database");          
 15   }
 16     
 17 }
 

Statetement Hazırlama

Her SQL statement'i için bir Statement nesnesi yaratılmalıdır. Statement nesnesinin SQL execute etmek için çeşitli methodları bulunur.

Statement statement=connection.createStatement();

Query'ler

Sonuçsuz Sorgular

SQL ifadeleri sonuç döndüren ve döndürmeyen olarak ikiye ayrıldığından, sonuç döndürmeyen SQL statement'leri executeUpdate() methodu kullanılarak

statement.executeUpdate("mysqlstatement");

şeklinde çalıştırılır. Etkilenen row sayısı sonuç olarak döndürüldüğünden

int count=statement.executeUpdate("mysqlstatement")

şeklinde de kullanılabilir.

İçinde 'Member' adlı bir tabloyu 'members' adlı data source'undan erişerek bir satır ekleyen ve sonra silen bir uygulama yazalım.

SqlHesitator.java

  1 import java.sql.*;
  2 public class SqlHesitator{
  3   public static void main(String[] args){
  4     try{
  5       Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");  
  6       String url="jdbc:odbc:members";
  7       Connection conn = DriverManager.getConnection(url, "sa","pw");
  8       Statement stmt=conn.createStatement();
  9       stmt.executeUpdate("insert Member(un,age) values('ali','21')");
 10       stmt.executeUpdate("delete from Member where un='myname'");
 11     }catch(Exception e){
 12       e.printStackTrace();
 13     }
 14     
 15   }
 16 }
 

Sonuçlu Sorgular

Bir sorgu sonuç döndürdüğünde executeQuery() method'u kullanılır.

ResultSet rs=executeQuery(sql);

biçiminde kullanımla sonuç ResultSet nesnesi olarak alınır. Bu nesne sonuçtaki her bir satıra sırayla erişimi ve her satırda bulan veriye tipine göre veriyi alma işlemini gerçekleştirmeye yarar.

while(rs.next()){
      String s=rs.getString("myfield1");
      int i=rs.getInt("myfield2");
      float f=rs.getFloat("myfield3")
}

yukarıdaki gibi bir kodla satırlara tek tek erişilebilir. Koddaki rs.next() ifadesi 'bir sonraki satıra geç ve bitip bitmediğini kontrol et' demektir. Sonuçtaki satırlar bitince 'false' sonucunun döndürdüğünden döngüden çıkılmış olur.

'Member' adlı bir tabloya 'members' adlı data source'undan erişerek bütün kayıtları ekrana basan bir uygulama yazalım.

SqlLister.java

  1 import java.sql.*;
  2 public class SqlLister {
  3   public static void main(String[] args){
  4     try{
  5       Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");  
  6       String url="jdbc:odbc:members";
  7       Connection conn = DriverManager.getConnection(url, "sa","pw");
  8       Statement stmt=conn.createStatement();
  9       ResultSet rs=stmt.executeQuery("select * from Member");
 10       while(rs.next()){
 11         String s=rs.getString("un");
 12         int i=rs.getInt("age");
 13         System.out.println("Username : "+s+"Age : "+i );
 14       }
 15     }catch(Exception e){
 16       e.printStackTrace();
 17     }
 18     
 19   }
 20 }
 

ResultSet'ler Hakkında

ResultSet interface'i bir sorgudan dönen sonuçları içerir. Normal şartlar altında sonuçlar ilk satırdan başlayarak sırayla okunur ve gerekli işlemler yapılır. Şu anda üzerinde bulunduğumz satırı belirten kavrama veritabanları terminolojisinde 'Cursor' denir. Cursor normal şartlar altında sadece 'forward' (ileriye doğru) çalışır. Ayrıca ResultSet aksi belirtilmedikçe sadece read-only (salt okunur)'dur, update (güncelleme) yapılamaz. Üzerinde ileri geri harekete edilebilen ResultSet'lere 'scrollable' (gezinilebilir) denir. Güncellemye olanak tanıyan ResultSet'ler de updatable (güncellenebilir) denir.

Gezinilebilir (Scrollable) ResultSet

Bir Statement'in executeQuery() method'undan dönen ResultSet'in scrollabnle olması için Connection interface'inin createStatement() method'u uygun parametrelerle çağrılır :

Statement stmt=connection.createStatement(
      ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

ResulSet bu şekilde alındıktan sonra cursor'un konumu ResultSet'in çeşitli methodlarıyla değiştirlebilir. ResultSet'teki bazı gezinti methodları şunlardır :

previous()

Bir önceki satıra dön

next()

Bir sonraki satıra geç

first()

İlk satıra git

last()

Son satıra git

abslote(4)

4. satıra git

relative(4)

4 satır ileri (-4'se geri) git

BankAccount adında bir tablodan dönen sonuçlar içierisinde çeşitli scroll işlemleri yapan bir örnek :

ScrollableResultSetTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class ScrollableResultSetTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {
  8     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
  9     Class.forName(driverClass);       
 10     String url="jdbc:odbc:mydatasource";     
 11     String username="sa";
 12     String password="godoro";
 13     Connection connection = DriverManager.getConnection(url,username,password);
 14     System.out.println("Connected to database");          
 15     Statement stmt=connection.createStatement(
 16             ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
 17     ResultSet rs=stmt.executeQuery("select * from BankAccount");
 18    
 19     
 20     while(rs.next()){
 21       int accountNum=rs.getInt("AccountNumber");
 22       float amount=rs.getInt("Amount");      
 23       System.out.println(accountNum+" : "+amount);        
 24       if(accountNum==345){
 25          if(rs.previous()){      
 26            int accountNumPrevious=rs.getInt("AccountNumber");  
 27            System.out.println("Account before 345 : "+accountNumPrevious);
 28            break;
 29          }
 30       }
 31     }    
 32     if(rs.first()){
 33       int accountNumFirst=rs.getInt("AccountNumber");
 34       System.out.println("First Account : "+accountNumFirst);      
 35     }
 36     if(rs.last()){
 37       int accountNumLast=rs.getInt("AccountNumber");
 38       System.out.println("Last Account : "+accountNumLast);      
 39     }  
 40     if(rs.absolute(4)){
 41       int accountNum4=rs.getInt("AccountNumber");
 42       System.out.println("4th Account : "+accountNum4);      
 43     }      
 44   }
 45     
 46 }
 

Güncellenebilir (Updatebale) ResultSet

ResultSet'in updatable olması için Connection'un createStatement() method'una parametre olarak ResultSet.CONCUR_UPDATABLE verilir. Result set için herhangi bir hücresini değiştirmek için updateString(), updateInt(), updateFloat() gibi methodlar kullanılır. Ancak bu method'larla değiştirlen verilerin veritabanında da geçerli olması için updateRow() method'u kulllanılır. Örnek :

rs.beforeFirst();
rs.updateString("AccountName","Vadeli");
rs.updateFloat("Amount",500f);
rs.updateRow();    

Yeni bir satır eklemek için insertRow() method'u kullanılır. ResultSet'in en başına (ilk satırdan de öne) gitmek için beforFirst() methodu, en sona (son satırdan da sonraya) gitmek için de afterLast() methodu kullanılır. Örneğin :

rs.afterLast();
rs.updateInt("AccountNumber",999);    
rs.updateFloat("Amount",900f);
rs.insertRow();

Çeşitli update ve insert operasyonları içeren detaylı bir örnek :

UpdateableResultSetTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class UpdateableResultSetTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {
  8     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
  9     Class.forName(driverClass);       
 10     String url="jdbc:odbc:mydatasource";     
 11     String username="sa";
 12     String password="godoro";
 13     Connection connection = DriverManager.getConnection(url,username,password);
 14     System.out.println("Connected to database");          
 15     Statement stmt=connection.createStatement(
 16             ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
 17     ResultSet rs=stmt.executeQuery("select * from BankAccount");
 18    
 19     
 20     while(rs.next()){
 21       int accountNum=rs.getInt("AccountNumber");
 22       float amount=rs.getInt("Amount");      
 23       System.out.println(accountNum+" : "+amount);      
 24       if(accountNum==345){
 25         rs.updateFloat("Amount",500f);
 26         rs.updateRow();        
 27         System.out.println("Amount of Account 345 set to 500.");
 28       }
 29     }
 30     rs.beforeFirst();
 31     rs.updateInt("AccountNumber",111);    
 32     rs.updateFloat("Amount",150f);
 33     rs.insertRow();
 34     System.out.println("Account 111 added at the begining");
 35     rs.afterLast();
 36     rs.updateInt("AccountNumber",999);    
 37     rs.updateFloat("Amount",900f);
 38     rs.insertRow();
 39     System.out.println("Account 999 added at the end");
 40     
 41   }     
 42 }
 

Statement'ler Hakkında

Statement (ifade), bir seferde veri tabanına gönderilen sorguyu temsil eder. Bir connection'la birden fazla statement kullanılabilir. Normalde statement bir SQL metini çalıştırmak için kullanılır. Ancak statement'in değişik kullanımları da vardır.

Batch Çalıştırma

İstenirse birden fazla SQL ifadesi 'batch' (toplu) olarak yani tek seferde çalıştırılabilir. Bunun için öncelikle çalıştılacak SQL ifadeleri Statement'in addBatch() methoduyla eklenmesi gerekir. Eklenecekler bittikten sonra hepsini birden çalıştırmak için de Statement'in executeBatch() methodu kullanılır.

BatchTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class BatchTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {
  8     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
  9     Class.forName(driverClass);       
 10     String url="jdbc:odbc:mydatasource";     
 11     String username="sa";
 12     String password="godoro";
 13     Connection connection = DriverManager.getConnection(url,username,password);
 14     System.out.println("Connected to database");          
 15     Statement stmt = connection.createStatement();
 16     stmt.addBatch("insert BankAccount(AccountNumber,Amount) VALUES (345,1500)");
 17     stmt.addBatch("insert BankAccount(AccountNumber,Amount) VALUES (456,2300)");
 18     stmt.addBatch("insert BankAccount(AccountNumber,Amount) VALUES (567,4200)");
 19     int[] results=stmt.executeBatch();
 20     for(int i=0;i.length;i++){
 21         System.out.println("Affected count for batch "+i+" : "+results[i]);
 22     }
 23     
 24   }
 25     
 26 }
 

Bu örnekte, BankAccount tablosunda üç tane satır, batch olarak eklenmekte ve tek seferde çalıştırılmaktadır.

Prepared Statement (Hazırlanmış İfade)

Bir SQL metni statement aracılığıyla veritanbanına yollanınca veritabanı tarafından önce derlenir (yani SQL string'den çalıştırabileceği şekle çevirir). Bu işlem az ya da çok bir zaman almaktadır. Oysa bir uygulama veritabanınan birbirine çok benzeyen, sadece bazı parametreleri farklı bir çok SQL ifadesi yollar. Prepared Statement, bir SQL ifadesinin bir kez derlenerek bir farklı parametrelerle tekrar tekrar kullanmasını sağlar. Yani önceden sadece bir kere derleme yapılır ve istendiği kadar kullanılabilir.

PreparedStatement, Connection'un prepareStatement() ile yaratılır. Parametre olarak verile SQL ifadesinde sorgudan sorguya değişen kısımlara soru işareti ('?') konur. Bunlar, parametrelerin daha sonra yerleştirileceği yerle anlamına gelir. Çalıştırmadan önce de bu parametrelerin değerleri atanır. Kullanım örneği.

String sql="insert into TestTable(IntegerField,StringField,FloatField)  values(?,?,?)";
PreparedStatement prepared=connection.prepareStatement(sql);
prepared.setInt(1,123);
prepared.setString(2,"ABC");    
prepared.setFloat(3,3.14f); 

Burada Test tablosunun IntegerField,StringField ve FloatField alanlarını insert işlemi yapan bir SQL vardır. Üç alan için sırasıyla üç soru işlareti konmuştur. Çalıştırma esnasında da PreparedStatement'in setInt(), setString() ve setFloat() methodlarıyla da asıl değerlen yerleştirilmektedir. Bu methodlara verilen rakam parametre sıra numarasıdır. Örneğin tam hali

PreparedStatementTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class PreparedStatementTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {    
  8       
  9     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
 10     Class.forName(driverClass);      
 11     String url="jdbc:odbc:mydatasource";     
 12     String username="sa";
 13     String password="godoro";
 14     Connection connection = DriverManager.getConnection(url,username,password);
 15     System.out.println("Connected to database");    
 16     String sql="insert into TestTable(IntegerField,StringField,FloatField)"
 17                 +" values(?,?,?)";
 18     PreparedStatement prepared=connection.prepareStatement(sql);
 19     prepared.setInt(1,123);
 20     prepared.setString(2,"ABC");    
 21     prepared.setFloat(3,3.14f);      
 22        
 23     prepared.executeUpdate();
 24     System.out.println("Prepared statement executed");      
 25     
 26       
 27   } 
 28         
 29     
 30 }
 

Callable Statement (Çağrılabilir İfade)

Bazı durumlarda SQL ifadesi veritabanında bulunur, uygulama içerisinden gerekli parametreler verilerek çağrılır. Veritabanında tanımlı sorgular SQL olabilececeği gibi o veritabınının desteklediği herhangi bir dil de olabilir. Örneğin MS SQL Server'da Transact-SQL diliyle Stored Procedure adı verilen prosedürler yazılabilir. Uygulama tarafında bilinmesi gereken sadece çağırlacak ifadedin adı ve gerekli parametrelerdir. Bunun için Connection'un prepareCall() methoduyla CallableStatement yaratılır.

    CallableStatement callable=connection.prepareCall("{call TestProcedure(?)}");

Daha sonra varsa parametre değerleri atanır

    callable.setString(1,"ABC");

Daha sonra da sorgu çağrılır.

    ResultSet rs=callable.executeQuery();

Çağrılan method executeQuery() olduğu gibi executeUpdate()'de olabilir. Burada SQL server'da tanımlanmış bir Stored Procedure'ü çağrılmaktadır. Örneğin tamamı :

CallableStatementTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class CallableStatementTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {    
  8       
  9     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
 10     Class.forName(driverClass);      
 11     String url="jdbc:odbc:mydatasource";     
 12     String username="sa";
 13     String password="godoro";
 14     Connection connection = DriverManager.getConnection(url,username,password);
 15     System.out.println("Connected to database");    
 16     CallableStatement callable=connection.prepareCall("{call TestProcedure(?)}");
 17     callable.setString(1,"ABC");
 18     ResultSet rs=callable.executeQuery();
 19     while(rs.next()){
 20         int i=rs.getInt("IntegerField");
 21         String s=rs.getString("StringField");        
 22         float f=rs.getFloat("FloatField");                
 23         System.out.println(i+" "+s+" "+f);
 24     }      
 25     System.out.println("Callable Statement executed");          
 26       
 27   } 
 28     
 29 }
 

MetaData (ÜstVeri)

MetaData (ÜstVeri) Kavramı

Veri hakkındaki bilgiye metadata (üstveri) denir. Veritabanları bir takım verileri tutar ve gerektiğinde erişilmesine olanak verir. Bazı durumlarda tutulan veri hakkında bilgiler de gerekebilir. Örneğin Öğrenci diye bir tablo olsa, her satırında bir öğrenci gelecek şeklinde veri içerir. Bu tabloda hangi alanlar var, her bir alanın boyu ve tipi nedir gibi özellikler metadata'dır ve tablonun kendisinde yer almaz. Veritabanı gerektiğinde metada'yı ayrı bir sorgulam biçimiyle verir. Sadece tablolar hakkındaki bilgiler değil, veritabanının adı ve versiyonu gibi bilgiler, bir sorgudan dönen sonuçlar da metadata kapsamındadır.

DatabaseMetaData

Bir veritabanı hakkında bilgi Connection class'ının getMetaData() method'undan DatabaseMetaData interface'inin döndürülmesiyle alınabilir:

    DatabaseMetaData metaData=connection.getMetaData();  
    String productName=metaData.getDatabaseProductName();
    String version=metaData.getDatabaseProductVersion();
    System.out.println("Database : "+productName+" "+version);

Burada ürünün (veritabanının) adı ve versiyonu alınmaktadır. Bir veritabanındaki tablolara ait bilgi DataBaseMetaData'nın getTables() methopdundan alınabilir. Yanlız bu method oldukça fazla sayıda ve karşmaşık parametre içerir. Bu method'un dönüş değeri, alınan sonuçlara erişim için kullanılacak ResultSet nesnesidir. Sadece normal tabloların adlarını almak için

    ResultSet rs=metaData.getTables(null, null,"%",new String[]{"TABLE"});

şeklinde bir kod yeterlidir. Burada '%' hepsini al anlamına gelir.

DatabaseMetaData'nın kullanımını anlatan örneğin tamamı :

DatabaseMetaDataTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class DatabaseMetaDataTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {
  8     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
  9     Class.forName(driverClass);       
 10     String url="jdbc:odbc:mydatasource";     
 11     String username="sa";
 12     String password="godoro";
 13     Connection connection = DriverManager.getConnection(url,username,password);
 14     System.out.println("Connected to database");          
 15     
 16     DatabaseMetaData metaData=connection.getMetaData();  
 17     String productName=metaData.getDatabaseProductName();
 18     String version=metaData.getDatabaseProductVersion();
 19     
 20     System.out.println("Database : "+productName+" "+version);
 21         
 22     ResultSet rs=metaData.getTables(null, null,"%",new String[]{"TABLE"});
 23     System.out.println("Tablo ?simleri");
 24     while(rs.next()){
 25        System.out.println(rs.getString("TABLE_NAME"));
 26     }
 27   }
 28     
 29 }
 

ResultSetMetaData

Bir sorgudan dönen sonuçlar hakkında bilgi almak için ResultSet'in getMetaData() methodu kullanılır.Aşağıdaki örnek dönen sonuçların kolon isimleri ve tiplerini ekrana basmaktadır :

ResultSetMetaDataTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class ResultSetMetaDataTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {
  8     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
  9     Class.forName(driverClass);       
 10     String url="jdbc:odbc:mydatasource";     
 11     String username="sa";
 12     String password="godoro";
 13     Connection connection = DriverManager.getConnection(url,username,password);
 14     System.out.println("Connected to database");          
 15     
 16     Statement stmt=connection.createStatement();
 17     ResultSet rs=stmt.executeQuery("select * from BankAccount");
 18     ResultSetMetaData metadata=rs.getMetaData();
 19     System.out.println("Column count : "+metadata.getColumnCount());
 20     for(int i=1;i.getColumnCount()+1;i++){
 21         System.out.println(metadata.getColumnName(i)+" "+metadata.getColumnTypeName(i));
 22     }    
 23     
 24   }
 25     
 26 }
 

Transaction

Transaction Kavramı

Terim olarak 'işlem' anlamına gelen transaction aynı zamanda birbirleriyle ilişkili işlemler bütünü anlamına gelir. Bütünlük, biri işlemlerden bir bile olmadan bütün işlemlerin var olamayacağı, geçerli olamayacağı anlamına gelir. Bir bankadan hesabuından ötekine para transferi yapılacak olsun. İlk işlem bir hesaptan para çekmek, sonraki işlem de diğerine para yatırmaktır. Bir aksilik oldu ilk hesaptan çekildi bağlantı koptu. Diğerine para yatırılmadan. Bu durumda hesaplarda tutarszlık olacaktır. Yatrıma işlemi gerçekleşmediyse para çekme işleminin de geçersiz olması gerekir. İşte bu şekilde bir bütün halinde değerlendirilmesi gereken işlemler transaction'dır.

Veritabnaı veya veritabanıona erişen kütüphaneler hangi veritabanı işlemlerinin kendi aralarında bir bütün oluşturduğunu anlayamaz. Programcı işlem başlayacağı zaman başla (begin()) der. İşlem bittiğinde de teslim et (commit() ). Bir hata olursa da geriye dön ( rollback() ) denir. Tipik bir transction kod şu şekilde olur :

try{
      transaction.begin()
      do_operations();
      transaction.commit();
}catch(Exception e)
      transction.rollback();
}

JDBC Transaction'ları ve JTA Transaction'ları

JDBC'de basit bir transaction mekansızması bulmaktadır. Java'nın standart sürümü ile kullanılabilecek trasnaction sistemi sadece budur. JDBC, veritaabını ve veritabanıona bağlanan kodun transaction altyapsını kullanır. Öte yandan Java Enterprise sürümü JTA (Java Transaction API) adında daha gelişmiş özellikler içeren bir kütüphaneye sahiptir. Örneğin bir çok veritabınan okuyup yazan işlemleri destekler. Burada sadece JDBC transaction'ları anlatılacaktır. Ancak JTA'nın nasıl bir şey olduğunu okurun anlayabilmesi için basit bir örnek vermek faydalı olabilir.

UserTransaction transaction = context.getUserTransaction();
try {
      transaction.begin();
      // Cesitli Islemler
    transaction.commit();
} catch (Exception e) {
      e.printStackTrace();
      transaction.rollback();
}

JDBC Transaction'ları ve AutoCommit (Otomatik Teslim)

JDBC'de yapılan her işlem veritabanına yansıtılır. Bunu sağlayan Connection'ın AutoCommit property'sininz true olmasıdır. Transaction biçimidne çalışmak için bu property false yapılır. Daha sonra işlemler toplu halde veritabanına yansıtılacağı zaman Connection'un commit() method'u çağrılır. (Burada begin demeye gerek yok, connection'un yaratıldığı an başlangıç kabul edilir.) Herhangi bir hata olması (exception oluşması ) durumunda rollback() method'u çağrılır.

Bir banka hesabından diğerine para transferi yapapn bir örnek yapalım. Bu işlemler transaction içerisinde gerçekleşsin.

TransactionTest.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 public class TransactionTest {
  4     
  5   public static void main(String[] args)
  6     throws Exception
  7   {
  8     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
  9     Class.forName(driverClass);       
 10     String url="jdbc:odbc:mydatasource";     
 11     String username="sa";
 12     String password="godoro";
 13     Connection connection = DriverManager.getConnection(url,username,password);
 14     System.out.println("Connected to database");          
 15     
 16     try{
 17         double transferAmount=100; 
 18         int accountNumFrom=123;
 19         int accountNumTo=321;
 20         connection.setAutoCommit(false);
 21         
 22         String sqlFrom="update BankAccount"
 23                 +" set Amount=Amount-"+transferAmount
 24                 +" where AccountNumber="+accountNumFrom;
 25         Statement stmtFrom=connection.createStatement();
 26         stmtFrom.executeUpdate(sqlFrom);
 27         String sqlTo="update BankAccount"
 28                 +" set Amount=Amount+"+transferAmount
 29                 +" where AccountNumber="+accountNumTo;
 30         Statement stmtTo=connection.createStatement();
 31         int affected=stmtTo.executeUpdate(sqlTo);
 32         if(affected>0){
 33             connection.commit();        
 34             System.out.println("Money transferred");
 35         }else{
 36             connection.rollback();
 37             System.out.println("Transaction rolled back");                
 38         }   
 39     }catch(Exception e){
 40         e.printStackTrace();
 41         connection.rollback();
 42         System.out.println("Transaction rolled back");        
 43     }
 44     
 45     
 46     
 47     
 48   }
 49     
 50 }
 

Bu örnekte öncelikle

connection.setAutoCommit(false);

diyerek yapılan değişikliklerin veritabanına otomatik olarak yansımaması sağlanıyor. İki statement ardarda execute ediliyor. Hata olmaması durumunda

connection.commit();

olması durumunda da

connection.rollback();

yapılıyor. Örnekte iki şekilde hata kontrolü var. Birincisi executeUpdate'de etkilen satır sayısına bakılıyor. Çünkü hiç bir exception çıkmasada herhnagi biri güncelleme yapılmamış olması durumda da işlemin geri alınması gerekir. İkinci hata kontrolü ve rollback Exception çıkması durumda yapılıyor.

Aslında connection.rollback() de bir exception fırlata bilir. Doğru kod aşağıdakigibi olmalıydı.

JDBC ve Kullanıcı Arayüzü

JDBC herhangi bir arayüz sisteminden bağımsız çalışacak şekilde hazırlanmıştır. AWT, Swing, Applet,Servlet ve JSP gibi arayüz seçeneklerinden herhangi biriyle, aynı şekilde çalışır. Bazı geliştirme ortamları arayüz ve veritabanı işlevlerini birarada gören bileşenler üretmektedir. Örneğin bir tabloyo veritabından getirip Swing'in JTable'ında gösteren hazır bileşenler bulunmaktadır. Ancak Java'nın standardında böyle bir destek yoktur. Normal şartlar altında programcı bunu kendi yapar. Klasik bir program, arayüzden ilgili verilerin alınıp veritabanına gönderilmesi ve veritabanından gelen bilgilerin arayüzde sergilenmesinden oluşur. Arayüzle veritabanı arasındaki bağlantıyı programcı sağlar.

Swing JTable'la Veri Tabanı Örneği

Java'nın Swing pakedindeki JTable adlı nesne, veritabaından gelen sonuçları listelemek için kullanılabilir. JTable, veriyi TableModel interface'i (daha dourusu onu doğrudan veya dolaylı bir şekilde implement eden herhagi bir class) alır. JDBC'de de sonuçlar ResultSet nesneleri içerisindedir. TableModel'i implement eden ve veriyi ResultSet'ten alan bir class yapılırsa arayüzle veritabanındaki bağlantı sağlanmış olur. Aşağıdaki örnekte ResultSetTableModel diye bir class yazılmıştır. AbsractTableModel'i extend edip ilgili methodları implement eder. Elbette ilgili method'ları implement ederken de ResultSet'i kullanır.

ResultSetTableModel class'ı

ResultSetTableModel.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 import java.util.*;
  4 import javax.swing.*;
  5 import javax.swing.table.*;
  6 import javax.swing.event.*;
  7 public class ResultSetTableModel extends AbstractTableModel{
  8     
  9     private ResultSet resultSet;
 10     private int rowCount;
 11     private int columnCount;    
 12     private ArrayList data=new ArrayList();
 13     public ResultSetTableModel(ResultSet resultSet) 
 14         throws Exception
 15     {
 16         setResultSet(resultSet);
 17     }
 18     public void setResultSet(ResultSet resultSet)
 19         throws Exception
 20     {
 21         this.resultSet=resultSet;            
 22         ResultSetMetaData metaData=resultSet.getMetaData();
 23         rowCount=0;
 24         columnCount=metaData.getColumnCount();
 25         while(resultSet.next()){
 26             Object[] row=new Object[columnCount];
 27             for(int j=0;j++){
 28                 row[j]=resultSet.getObject(j+1);                
 29             }
 30             data.add(row);
 31             rowCount++;
 32         }
 33     }
 34     public int getColumnCount(){
 35         return columnCount;
 36     }        
 37     public int getRowCount(){
 38         return rowCount;
 39     }        
 40     public Object getValueAt(int rowIndex, int columnIndex){
 41         Object[] row=(Object[]) data.get(rowIndex);
 42         return row[columnIndex];
 43     }
 44     public String getColumnName(int columnIndex){
 45         try{
 46             ResultSetMetaData metaData=resultSet.getMetaData();
 47             return metaData.getColumnName(columnIndex+1);
 48         }catch(Exception e){
 49             e.printStackTrace();
 50             return "";
 51         }
 52     }        
 53 }
 

ResultSetTableModel'ı kullanarak JTable'da veri gösteren örnek

JdbcSwingTable.java

  1 package com.godoro.samples.jdbc;
  2 import java.sql.*;
  3 import java.util.*;
  4 import javax.swing.*;
  5 import javax.swing.table.*;
  6 import javax.swing.event.*;
  7 public class JdbcSwingTable {
  8   public static void main(String[] args)
  9     throws Exception
 10   { 
 11     String driverClass="sun.jdbc.odbc.JdbcOdbcDriver";     
 12     Class.forName(driverClass);       
 13     String url="jdbc:odbc:mydatasource";     
 14     String username="sa";
 15     String password="godoro";
 16     Connection connection = DriverManager.getConnection(url,username,password);
 17     System.out.println("Connected to database");                
 18     String sql="select * from BankAccount";
 19     Statement stmt=connection.createStatement();
 20     ResultSet rs=stmt.executeQuery(sql);
 21     ResultSetTableModel model=new ResultSetTableModel(rs);
 22     
 23     JFrame frame=new JFrame();
 24     JTable table=new JTable(model);
 25     JScrollPane scroll=new JScrollPane(table);
 26     frame.getContentPane().add(scroll);
 27     frame.setBounds(100,100,400, 300);
 28     frame.setVisible(true);
 29       
 30       
 31   }
 32     
 33 }