YAZILIM VE VERİTABANI C# ve T-SQL Geliştiriciler için Java ve Oracle Yalçın Kaya Editör C. Banu Üçüncüoğlu 19-0 C# ve T-SQL Geliştiriciler için Java ve Oracle Yalçın Kaya Editör: C. Banu Üçüncüoğlu Kapak Tasarımı: Melih Sancar Grafik Tasarım: Tuna Erkan Grafik Uygulama: Soner Işık Genel Yayın Yönetmeni: Mehmet Çömlekçi 1. Basım: Şubat 2008 Bilge Adam Yayınları: 21 Eğitim Yayınları Dizisi: 21 ISBN: 978-605-5987-19-0 Copyright © 2007, Bilge Adam Bilgisayar ve Eğitim Hizmetleri San. ve Tic. A.Ş. Eserin tüm yayın hakları Bilge Adam Bilgisayar ve Eğitim Hizmetleri San. ve Tic. A.Ş.’ye aittir. Yayınevinden yazılı izin alınmadan kısmen ya da tamamen alıntı yapılamaz, hiçbir şekilde kopya edilemez, çoğaltılamaz ve tekrar yayımlanamaz. Bilge Adam’ın öğrencilerine ücretsiz armağanıdır, para ile satılamaz. Bilge Adam Bilgisayar ve Eğitim Hizmetleri San. ve Tic. A.Ş. 19 Mayıs Mahallesi, 19 Mayıs Caddesi, UBM Plaza, No: 59-61, Kat: 4-7; Şişli, İstanbul Telefon: (212) 272 76 00 – (212) 217 05 55 Faks: (212) 272 76 01 www.bilgeadam.com - info@bilgeadam.com Tanıtım nüshasıdır, para ile satılamaz. İçindekiler Java’ya Giriş��������������������������������������������������������������������������������������������������������������������������������3 Java Nedir?����������������������������������������������������������������������������������������������������������������������������� 3 Windows Ortamında Java Kurulumu������������������������������������������������������������������������������3 Java Uygulamaları Nasıl Çalışır?�������������������������������������������������������������������������������������������� 7 Basit Veri Tipleri���������������������������������������������������������������������������������������������������������������������� 9 Ondalıklı Tipler�������������������������������������������������������������������������������������������������������������11 Mantıksal Tip����������������������������������������������������������������������������������������������������������������11 Karakter Tipi�����������������������������������������������������������������������������������������������������������������11 Aritmetik İşlemler ve Taşma�����������������������������������������������������������������������������������������11 Java’da Operatörler��������������������������������������������������������������������������������������������������������������� 14 Kaydırma (Shift) İşlemleri���������������������������������������������������������������������������������������������15 İşlemlerde Parantez Kullanımı�������������������������������������������������������������������������������������16 Karar Vermek ve Koşullu İfadeler��������������������������������������������������������������������������������16 Döngüler�������������������������������������������������������������������������������������������������������������������������������������23 While Döngüsü���������������������������������������������������������������������������������������������������������������������� 23 do Döngüsü��������������������������������������������������������������������������������������������������������������������������� 24 for Döngüsü��������������������������������������������������������������������������������������������������������������������������� 24 break ve continue Deyimleri������������������������������������������������������������������������������������������������� 25 Niteleyiciler (Modifiers)�������������������������������������������������������������������������������������������������������������29 Access Modifiers (Erişim Niteleyicileri)���������������������������������������������������������������������������������� 29 Niteleyicilerde Metod Ezme (Access Modifiers Overriding) ����������������������������������������30 Diğer Niteleyiciler ��������������������������������������������������������������������������������������������������������30 Casting ve Conversion�������������������������������������������������������������������������������������������������������������35 Basit Tiplerde Conversion����������������������������������������������������������������������������������������������������� 36 Basit Tiplerde Conversion: Atama��������������������������������������������������������������������������������36 Basit Tiplerde Conversion: Metod Çağırma�����������������������������������������������������������������38 Basit Tiplerde Conversion: Aritmetik Yükseltme ���������������������������������������������������������38 Basit Tiplerde Casting��������������������������������������������������������������������������������������������������39 Referans Tiplerde Conversion ���������������������������������������������������������������������������������������������� 40 Referans Tiplerde Conversion: Atama ������������������������������������������������������������������������40 Referans Tiplerde Csonversion: Metod Çağırma ��������������������������������������������������������41 IV İçindekiler Yapısal Programlama����������������������������������������������������������������������������������������������������������������45 Yapısal Programlama Nedir?������������������������������������������������������������������������������������������������ 45 Nesne Yönelimli Programlamanin Temel Bileşenleri���������������������������������������������������45 Nesne Yönelimli Programlamanın Temel Kavramları����������������������������������������������������������� 51 Kapsülleme (Encapsulation) ���������������������������������������������������������������������������������������51 Swing������������������������������������������������������������������������������������������������������������������������������������������67 Swing’e Giriş������������������������������������������������������������������������������������������������������������������������� 67 JButton�������������������������������������������������������������������������������������������������������������������������68 JLabel���������������������������������������������������������������������������������������������������������������������������69 JTextField���������������������������������������������������������������������������������������������������������������������70 Event Handling Mekanizması���������������������������������������������������������������������������������������71 JTextArea���������������������������������������������������������������������������������������������������������������������74 JOptionPane����������������������������������������������������������������������������������������������������������������75 JCheckBox�������������������������������������������������������������������������������������������������������������������77 JRadioButton����������������������������������������������������������������������������������������������������������������79 JComboBox������������������������������������������������������������������������������������������������������������������81 JList �����������������������������������������������������������������������������������������������������������������������������83 Mouse Event’lerinin Yönetilmesi:���������������������������������������������������������������������������������89 Klavye Event’lerinin Yönetilmesi������������������������������������������������������������������������������������������� 94 JDBC�����������������������������������������������������������������������������������������������������������������������������������������103 Veritabanı Bağlantısının Kurulması������������������������������������������������������������������������������������� 108 Veri Değiştirme Komutları (insert, update, delete)�������������������������������������������������������������� 109 Veri Sorgulama Komutları (Select)�������������������������������������������������������������������������������������� 110 Parametreli SQL İfadelerinin Çalıştırılması����������������������������������������������������������������112 Stored Procedure’lerin Çalıştırılması�������������������������������������������������������������������������113 JSP��������������������������������������������������������������������������������������������������������������������������������������������123 JSP Teknolojisinde Kullanılan Etiketler������������������������������������������������������������������������������� 128 JSP Direktifleri������������������������������������������������������������������������������������������������������������128 JSP Script Etiketleri����������������������������������������������������������������������������������������������������129 Form Verilerinin Kullanılması���������������������������������������������������������������������������������������������� 129 Durum Yönetimi ������������������������������������������������������������������������������������������������������������������ 131 JAVA I/O ����������������������������������������������������������������������������������������������������������������������������������143 Dosyalar ve Dizinler ����������������������������������������������������������������������������������������������������������� 143 İçindekiler FilterInputStream Sınıfları������������������������������������������������������������������������������������������145 FileInputStream ve FileOutputStream Sınıflarının Kullanımı�������������������������������������146 Reader ve Writer Sınıfları: �����������������������������������������������������������������������������������������150 Standart Giriş-Çıkış Biriminin Kullanılması:���������������������������������������������������������������154 Java ile Ağ Programlama: �������������������������������������������������������������������������������������������������� 155 Oracle Veritabanı Ailesi����������������������������������������������������������������������������������������������������������161 Oracle Üzerinde Uygulama Geliştirme�������������������������������������������������������������������������������� 161 Oracle Veri Tabanı Sisteminin Kurulması������������������������������������������������������������������161 Tablespace�����������������������������������������������������������������������������������������������������������������167 Undo Tablespace�������������������������������������������������������������������������������������������������������167 Oracle’da Veri Tabanı Oluşturmak�����������������������������������������������������������������������������168 Temel Veri Tipleri�������������������������������������������������������������������������������������������������������179 Oracle’da Kullanıcı Yönetimi ve Güvenlik������������������������������������������������������������������179 Oracle Üzerinde Programlama�����������������������������������������������������������������������������������������������185 PL/SQL Nedir?�������������������������������������������������������������������������������������������������������������������� 185 SQL Plus��������������������������������������������������������������������������������������������������������������������185 TOAD�������������������������������������������������������������������������������������������������������������������������187 PL/SQL ile Programlama�������������������������������������������������������������������������������������������189 PL/SQL’de Kontrol Yapıları����������������������������������������������������������������������������������������191 PL/SQL’de Alfanümerik Tipler������������������������������������������������������������������������������������201 PL/SQL’de Mantıksal Tipler���������������������������������������������������������������������������������������207 PL/SQL’de Tarih ve Zaman Tipleri�����������������������������������������������������������������������������207 Referans Tipleri����������������������������������������������������������������������������������������������������������213 LOB (Large Object) Tipleri�����������������������������������������������������������������������������������������216 PL/SQL’de Döngü Yapıları�����������������������������������������������������������������������������������������216 1 Java’ya Giriş 1 Java’ya Giriş • Java Nedir? • Java Uygulamaları Nasıl Çalışır? • Basit Veri Tipleri • Java’da Operatörler Java’ya Giriş Bölüm Hedefleri: Java platformu üzerinde Java diliyle basit uygulamalar yazmak. Temel veri tiplerini tanımak. Java Nedir? Java, işlemci mimarisinden ve işletim sisteminden bağımsız olarak tasarlanmış bir platformdur. Aynı zamanda da bu platform üzerinde uygulama geliştirilen dilin adıdır. Microsoft .NET platformuyla karşılaştırdığımızda .NET platformuna karşılık gelen yapının adı Java olduğu gibi C# .NET, Visual Basic .NET gibi .NET dillerine karşılık gelen dilin adı da Java’dır. Bu açıklamadan anlaşılacağı gibi Microsoft .NET platformundan farklı olarak Java platformunda uygulama geliştirilebilecek tek dil vardır. Java platformu pek çok yönden Microsoft .NET platformuna benzediği gibi Java dili de yetenekleri bağlamında C# diline çok benzer. Java platformu da Microsoft .NET platformu ve modern nesne yönelimli diller gibi geniş bir sınıf kütüphanesine sahiptir. Yine .NET platformuna benzer olarak bir Java uygulamasının çalışması için doğrudan işletim sistemi kullanılmaz. Bu sebeple bir Java uygulaması, işletim sistemine ve mimari platforma göre geliştirilmiş olan JVM (Java Virtual Machine) aracılığıyla çalıştırılır. Java ile uygulama geliştirme için kullanılan ortamlar (IDE – Integrated Development Environment), .NET için kullanılan ortamlara göre çok daha fazla sayıdadır ve kolaylık olarak farklılık gösterir. Yaygın olarak kullanılan bütünleşik geliştirme ortamlarından (IDE) biri Eclipse’tir. Eclipse’i http:// www.eclipse.org adresinden indirebilirsiniz. Kitaptaki örnekleri gerçekleştirmek için herhangi bir bütünleşik geliştirme ortamı indirmenize gerek yoktur, bütün örnekleri Windows üzerinde Notepad ya da herhangi bir metin editörü ile gerçekleştirebilirsiniz. Windows Ortamında Java Kurulumu Java platformunu Windows işletimi üzerinde kurmak ve uygulamaları yazabilir hale gelmek için öncelikle Java Development Kit’i (JDK) bilgisayarınızda kurmalısınız. JDK’nın sürümlerini http:// www.javasoft.com adresinden indirebilirsiniz. İndirdiğiniz dosyayı Windows’un tüm sürümlerinde kurabilirsiniz. Kurulumu gerçekleştirdikten sonra Windows ortamında çevre değişkenleri (environment variables) üzerinde işlem yapmamız gerekir. Komut satırında herhangi bir noktadan derleme ve çalıştırma programlarını kullanabilmek için Path değişkenine Java’yı kurduğumuz dizinin içindeki bin dizinini eklemelisiniz. Bölüm 1 Uygulama 1.1: Windows XP: 1� Şekil 1’de gösterilen, Bilgisayarım (My Computer) ikonunu sağ tıklayın. Açılan menüde Özellikler (Properties) öğesini seçin. Şekil 1 2. Şekil 2’de gösterildiği gibi, açılan pencerede Gelişmiş (Advanced) sekmesini tıklayın. 3� Şekil 2’de gösterildiği gibi, gelen pencerede Çevre Değişkenleri (Environment Variables) butonuna tıklayın. Şekil 2 4. Şekil 3’te gösterilen, Sistem Değişkenleri (System Variables) içinde Path’i bulun ve en sonuna “;” ekleyin. Java’ya Giriş Şekil 3 5. Şekil 4’te olduğu gibi, sonuna da <Java’yi kurduğunuz yer>\<JDKxxx>\bin yazın. şekil 4 Windows Vista: 1� Şeki 5’daki gibi, Bilgisayarım (My Computer) ikonunu sağ tıklayın. Açılan menüde Özellikler (Properties) öğesini seçin. Şekil 5 Bölüm 1 2. Şekil 6’de gösterilen, Tasks bölümünden Advanced System Settings link’ine tıklayın. Şekil 6 3� Şekil 8’de gösterildiği gibi, gelen pencerede Çevre Değişkenleri (Environment Variables) butonuna tıklayın. Şekil 7 Java’ya Giriş 4. Şekil 8’da gösterildiği gibi, Sistem Değişkenleri (System Variables) içinde Path’i bulun ve en sonuna “;” ekleyin. Şekil 8 5. Şekil 9’daki gibi, sonuna da <Java’yi kurduğunuz yer>\<JDKxxx>\bin yazın. Şekil 9 Java Uygulamaları Nasıl Çalışır? Bir Java uygulamasının makinenizde çalışması için JVM’in kurulu olması gerekir. JVM Microsoft .NET platformunun IL (Intermediate Language - MSIL) üzerinden çalışması mantığına benzer bir mantıkla çalışır. Java tarafında IL’e karşılık gelen yapı bytecode’dur. JVM sadece bytecode üzerinden çalışır. Bu sebeple yazmış olduğunuz Java kaynak kodunu öncelikle Java derleyicisiyle derleyerek bytecode’a çevirmelisiniz. Bununla birlikte dikkat edilmesi gereken bir nokta da bir Java sınıfının derlenebilmesi için kaynak kodu tutan .java uzantılı dosyanın adının, içindeki public sınıfın adıyla aynı olması gerektiğidir. Örnek Uygulama 1.1: 1public class IlkJavaUygulamasi 2{ 3 public static void main( String[] args) 4 { 5 6 } 7} System.out.println(“Ilk Java Uygulamasi”); Bölüm 1 İşlem adımları: Örnekleri kolaylık açısından Windows’ta oluşturacağınız bir dizin içinde toparlamanız faydalı olacaktır. 1� Yukarıdaki kodu herhangi bir metin editöründe yazın ve “IlkJavaUygulamasi.java” adıyla kaydedin. 2. Command Prompt’u açın. 3� Dosyayı kaydettiğiniz dizine girin. 4. javac IlkJavaUygulamasi.java ifadesini çalıştırın. 5. Eğer hata almazsanız IlkJavaUygulamasi.class dosyasının kaynak kodla aynı yerde oluştuğunu görmelisiniz. 6. Java IlkJavaUygulamasi ifadesini çalıştırdığınızda komut satırında “Ilk Java Uygulamasi” ifadesini görmelisiniz. Uygulamanın açıklaması: 1 numaralı satırda C#’ta olduğu gibi IlkJavaUygulamasi adında bir sınıf tanımlanır. Java dilinde de, C#’ta olduğu gibi kod blokları sınıfların içinde yer almalıdır. 2 numaralı ve 4 numaralı satırlarda “{“ (küme parantezi, curly brace) işareti aynen C#’ta olduğu gibi blok başı işaretidir. 2 numaralı satırdaki “{“ sınıf için tanımlanan bloğun başlangıcıyken 4 numaralı satırdaki “{“ işareti de main metodunun başlangıcıdır. 6 numaralı ve 7 numaralı satırlardaki “}” işareti blok sonu işaretidir. 6 numaralı satırdaki “}” işareti main bloğunun sonuyken, 7 numaralı satırdaki “}” işareti sınıf için açılan bloğun sonudur. Blok başı ve blok sonu ifadelerinde dikkat etmeniz gereken en önemli nokta son açılan blok her zaman ilk kapatılan bloktur ve her açılan blok mutlaka kapatılmalıdır. 3 numaralı satırda aynen C#’ta olduğu gibi main metodunun tanımını görebilirsiniz. public static void ifadelerinin anlamları C# ile aynı olmakla birlikte daha sonra detaylı olarak incelenecektir. Main metodunun parametresi olan String[] args de metodun parametre olarak bir String dizisi aldığını belirtir. Main metodu, Java’da da konsol uygulamalarının otomatik olarak çalışan ilk metodudur. 5 numaralı satırda main metodu çalıştığında ne yapacağını gösterir. System.out.println(); ifadesi C#’taki Console.WriteLine() ifadesine karşılık gelir. Burada System.out standart çıktı birimini temsil eder. Varsayılan olarak kullandığınız ekrandır. println() metodu, kendisine parametre olarak verilen ifadeye yazar ve sonundaki “ln” (line) kısmından dolayı bir satır aşağıya geçer. İfadenin sonundaki “;” işareti Java’da da C#’taki her ifadenin sonunda mutlaka bulunması gereken satır sonlandırıcısıdır. Bu basit uygulamaya bakarak C# dili ile Java dilinin birbirine pek çok noktada benzediğini görebilirsiniz. Bundan sonraki pek çok aşamada C# dili ile Java dili arasındaki benzerliklere tanık olacaksınız. İlk uygulamada konsola System.out.println() yazdırdığımız ifadeyi bir mesaj kutusunda yazdırmak istersek bir miktar değişiklik yapmamız gerekecektir. Örnek Uygulama 1.2a: 1public class MesajKutusu 2{ 3 public static void main( String[] args) 4 { 5 6 } 7} javax.swing.JOptionPane.showMessageDialog(null, “Ilk Java Uygulamasi”); Java’ya Giriş Uygulamanın açıklaması: Bu uygulamanın 5 numaralı satırı dışındakiler önceki uygulamadan farklı değildir. 5 numaralı satırda da C#’taki MessageBox.Show() metodunun Java’daki benzeri olan JoptionPane.showMessageDialog() metodu kullanılmıştır. Bu kodun başındaki javax.swing de JOptionPane sınıfının içinde bulunduğu pakettir. Java’daki paketleri .NET’teki assembly’ler gibi düşünebilirsiniz. Dolayısıyla C#’taki using ifadesinin karşılığı olarak import ifadesini kullanarak kodumuzu aşağıdaki şekilde güncelleyebiliriz. Örnek Uygulama 1.2b: 1import javax.swing.JOptionPane; 2public class MesajKutusu 3{ 4 public static void main( String[] args) 5 { 6 7 } JOptionPane.showMessageDialog(null, “Ilk Java Uygulamasi”); 8} Bu örnekte 1 numaralı satırdaki import ifadesi javax.swing paketindeki JOptionPane sınıfının yüklenmesini sağlar. Bir kere yüklendikten sonra tekrar paket adının kod içinde kullanmamıza gerek kalmaz. Burada yapılan yükleme işlemi, .NET’teki gibi, yüklenen paketin içerdiği kodu, oluşturulan .class dosyasının içine eklemez, sadece o pakete erişimi sağlayacak kodu oluşturur. Uygulama 1.2b’deki 6 numaralı satırda JOptionPane sınıfının showMessageDialog metodunun ilk parametresi swing paketinin detaylarına gelene kadar sürekli null olarak kullanılacaktır. 2. parametre de tahmin edebileceğiniz gibi mesaj kutusunda yazdırılacak ifadedir. Java’da comment’lerin kullanımı C# ile aynıdır. // Satır comment. /* */ Blok comment. Basit Veri Tipleri Java’daki basit (primitive) veri tipleri .NET’teki basit veri tipleriyle çok benzerdir. Herhangi bir veri tipinin tanımlanması C#’takinden farklı değildir. Bununla birlikte Java’daki sayısal tiplerin hepsi işaretlidir (signed – hem negatif hem de pozitif sayıları tutar), işaretsiz veri tipi yoktur. Öncelikle tam sayısal veri tiplerinden başlayalım: byte: Adından da anlaşılabileceği gibi 1 byte’lık, tam sayı tutan veri tipidir. C#’ta sbyte’a, .NET CTS’te System.SByte’a karşılık gelir. Herhangi bir byte değişken tanımlaması aşağıdaki şekillerde yapılabilir: byte yas; //ilk değer verilmeden. byte yas = 67; //ilk değer verilerek. short: 2 byte’lık, tam sayı tutan veri tipidir. C#’ta short’a, .NET CTS’te System.Int16’ya karşılık gelir. Herhangi bir short değişken tanımlaması aşağıdaki şekillerde yapılabilir: short derinlik; //ilk değer verilmeden. short derinlik = 23245; //ilk değer verilerek. int: 4 byte’lık, tam sayı tutan veri tipidir. C#’ta int’e, .NET CTS’te System.Int32’ye karşılık gelir. Herhangi bir int değişken tanımlaması aşağıdaki şekillerde yapılabilir: 10 Bölüm 1 int sayi; //ilk değer verilmeden. int sayi = 12; //ilk değer verilerek. long: 8 byte’lık, tam sayı tutan veri tipidir. C#’ta long’a, .NET CTS’te System.Int64’e karşılık gelir. Herhangi bir long değişken tanımlaması aşağıdaki şekillerde yapılabilir: long GNP; //ilk değer verilmeden long GNP = 1234567890; //ilk değer verilerek Bunların dışında basit veri tipi olmamakla birlikte şu aşamada bilmeniz gereken bir veri tipi daha var: String. String tipi referans üzerinden çalışan bir veri tipidir. Yani, aslında basit veri tiplerinden farklı olarak bir nesne üzerinden çalışırlar; fakat tanımlaması şu ana kadar bahsedilen veri tiplerinden farklı değildir. String’in detaylarına ilerleyen bölümlerde değineceğiz. Burada dikkat etmemiz gereken nokta Java’da küçük harf “s” ile başlayan string tanımının olmamasıdır. Herhangi bir String veri tanımlaması aşağıdaki şekillerde yapılabilir: String Ad; //ilk değer verilmeden String Ad = “Yalcin”; //ilk değer verilerek Artık kullanıcıdan 2 sayı alıp bunları matematiksel herhangi bir işlem içinde kullanabilecek hemen her şeye sahibiz. Tek eksiğimiz bu sayıları kullanıcıdan nasıl alacağımız. Matematiksel işlemlerin nasıl yapıldığına gelirsek, ileride farklılıklarına değineceğemizi belirterek C#’taki yazılışlarından şu an için hiçbir farkı yoktur diyebiliriz. Şimdi bunu örnekleyelim: Örnek Uygulama 1.3: 1import javax.swing.JOptionPane; 2public class MesajKutusu 3{ 4 public static void main( String[] args) 5 { 6 String birinciSayi, ikinciSayi; 7 int toplam, sayi1, sayi2; 8 birinciSayi = JOptionPane.showInputDialog(“Birinci sayiyi giriniz:”); 9 ikinciSayi = JOptionPane.showInputDialog(“Ikinci sayiyi giriniz:”); 10 sayi1 = Integer.parseInt(birinciSayi); 11 sayi2 = Integer.parseInt(ikinciSayi); 12 toplam = sayi1 + sayi2; 13 JOptionPane.showMessageDialog(null, toplam); 14 } 15} Kod Açıklaması: 6 numaralı satırda String tipinde 2 değişken tanımlanmıştır. Unutmayın ki bu 2 değişken de basit veri tipi değil referans tipli değişkenlerdir. Bu değişkenler JOptionPane sınıfının showInputDialog metodunun döndürdüğü değerleri tutmak için kullanılmıştır. 7 numaralı satırda 3 tane tam sayı tanımlanmıştır. toplam adlı değişken JOptionPane.showInputDialog() metoduyla aldığımız değerlerin sayıya dönüştürülmüş hallerini toplamak için kullanılmıştır. sayi1 ve sayi2 değişkenleri de JOptionPane.showInputDialog() metoduyla aldığımız Java’ya Giriş değerlerin sayıya dönüştürülmüş hallerini tutacaktır. 8 ve 9 numaralı satırlarda JOptionPane. showInputDialog() metodlarıyla, yukarıda tanımladığımız String’lerin içine, kullanıcının gireceği değerler alınmaktadır. Burada dikkat etmemiz gereken nokta, rakamlar dışında değerler girdiğimizde 10 ve 11 numaralı satırlarda dönüştürme hatasıyla karşılaşma durumumuzdur. Bu satırlarda kullanıcı tarafından girilmiş olan String’ler Integer sınıfının parseInt() aracılığıyla int’e çevrilmeye çalışılır. Integer sınıfı .NET’teli System.Int32 sınıfına karşılık gelen sınıftır ve terimsel olarak wrapper class olarak geçer. parseInt() metodu da Int32 sınıfının Parse() metoduyla aynı şekilde çalışır. Eğer kendisine gönderilen parametre int’e çevrilemiyorsa “NumberFormatException” oluşturur. Bunun için de C#’taki gibi istisna yönetimi (exception handling), yani try-catch-finally bloklarını kullanabiliriz. 10 ve 11 numaralı satırlarda eğer herhangi bir exception oluşmazsa, yani bir int’in alabileceği değer aşılmazsa 12 numaralı satır çalışır. Eğer 2 değerin toplamı bir int’in alabileceği en büyük değeri aşıyorsa toplam değişkeninin alacağı değer negatif bir sayıya dönecektir. Çünkü Java, C geleneğinden dolayı sayıları kontrol etmez. Bu durumu daha küçük bir tam sayı tipi olan byte üzerinden Uygulama 1.4’te açıklayacağım. 13 numaralı satırda bir önceki örnekte kullandığımız JOptionPane.showMessageDialog() metoduyla toplam değişkeninin değerini yazdırıyoruz. Sınıf adıyla kullanılması sebebiyle showMessageDialog() metodunun JOptionPane sınıfının static bir metodu olduğunu anlayabilirsiniz. Ondalıklı Tipler double: 8 byte’lık ondalıklı veri tipidir. Tanımlandığı standart (IEEE 754) çerçevesinde yüksek doğruluk isteyen işlemlerde kullanılmamalıdır; ancak ondalıklı kısımda yüksek yoğunluk çok önemli değilse kullanılabilir. C#’ta float’a, .NET CTS’te System.Single’a karşılık gelir. float: 4 byte’lık ondalıklı veri tipidir. Hem tanımlandığı standart çerçevesinde hem de double veri tipine göre daha küçük bir veri tipi olması sebebiyle yüksek doğruluk isteyen işlemlerde kullanılmamalıdır; ancak ondalıklı kısımda yüksek yoğunluk çok önemli değilse kullanılabilir. Eğer bellek yetersizliği ile ilgili bir problem yoksa float yerine double tercih edilmelidir. C#’ta double’a, .NET CTS’te System.Double’a karşılık gelir. Her 2 ondalıklı veri tipinin yerine java.math paketinde bulunan BigDecimal sınıfı kullanılabilir. Mantıksal Tip boolean: true ya da false değerlerinde birini taşıyabilen basit veri tipidir. Taşıdığı veri sadece 1 bit ile gösterilebiliyor olsa da kapladığı alan 1 bit değildir. C#’ta bool’a, .NET CTS’te System.Boolean’a karşılık gelir. Karakter Tipi char: 2 byte’lık Unicode karakter olarak herhangi bir karakter bilgisini tutmak için kullanılır. C ailesi geleneğinden dolayı işaretsiz tam sayısal veri tipi olarak değerlendirilebilir. C#’ta char’a, .NET CTS’te System.Char’a karşılık gelir. Aritmetik İşlemler ve Taşma Java’da tam sayısal aritmetik işlemler, işleme giren değerlerin tipleri eğer birer int değilse int’e dönüştürülerek yapılır. Eğer işleme giren tiplerden en az biri long ise long’a dönüştürülerek yapılır. Eğer tipler ondalıklı ise işlemler, işleme giren tipler double’a dönüştürülerek yapılır. Dolayısıyla aşağıdaki örnekleri dikkatle incelemek gereklidir: 11 12 Bölüm 1 Örnek Uygulama 1.4a: 1public class TipDonusumleri1 2{ 3 public static void main( String[] args) 4 { 5 byte sayi1 = 120; 6 byte sayi2 = 110; 7 byte toplam = sayi1 + sayi2; 8 } 9} Bu örnekte 7 numaralı satırda sayi1 + sayi2 ifadesi, işlem int üzerinden yapıldığından çalışacaktır; ancak toplam değişkeninin tipi byte olduğu için derlenemeyecektir ve possible loss of precision (değer kaybı ihtimali) hatası verecektir. Bunun sebebi toplanan byte tipindeki değişkenlerin değerlerinin toplamının, byte tipindeki bir alana sığmaması değildir. (120 + 110 = 230, byte tipinin alabileceği en büyük değer ise 127’dir). Bunu sayi1’in değerini 12, sayi2’nin değerini 11 yaparak görebilirsiniz. Possible loss of precision hatasını bu durumda da alacaksınız. (12 + 11 = 23, byte tipinin içine sığar). Dolayısıyla bu hatanın sebebi, sayi1 + sayi2 işleminin int üzerinden yapılmasıdır. Bunu anlamak için 7 numaralı satırda tanımlanan toplam değişkeninin tipini byte’tan short’a çevirip tekrar derleyerek de görebilirsiniz. Bu durumda 120 ile 110’un toplamı olan 230 değeri short tipine sığacak bir değer olmasına rağmen possible loss of precision hatasını yine alırsınız; ancak toplam değişkeninin değerini int ya da long yaparak bu hatadan kurtulabilirsiniz. Uygulama 1.4a örneği üzerinde düşündüğümüzde yapılan işlem anormal gelmemelidir. Çünkü burada 2 tane byte tipli değişkenin toplamının değerinin byte tipine sığmaması ihtimali yüksektir. Bu durumda, eğer bu işlemi byte tipi üzerinden gerçekleştirmek istersek ne yapmalıyız? Bunun cevabını da Uygulama 1.4b’de görebilirsiniz. Örnek Uygulama 1.4b: 1public class TipDonusumleri2 2{ 3 public static void main( String[] args) 4 { 5 byte sayi1 = 12; 6 byte sayi2 = 11; 7 byte toplam = (byte)(sayi1 + sayi2); 8 System.out.println(toplam); 9 } 10} Örnek Uygulama 1.4b’de 7 numaralı satırda yaptığımız işlem bir explicit cast (göstererek dönüştürme) işlemidir. Uygulama 1.4a’da int’e dönüştürme ise implicit cast (gizli dönüştürme) işlemi olarak adlandırılır. Daha sonra bu işlemleri farklı isimlerle tekrar değerlendireceğiz. Burada sayi1 + sayi2 işlemi önce yine int üzerinden yapılır ve sonra byte’a dönüştürme yapılır. Bu şekilde 12 ile 11’in toplamı olan 23 değerini byte olarak elde edebilirsiniz. Buraya kadar herşey güzel; ancak bu noktada Uygulama 1.3’deki taşma problemimize geri dönelim ve 5 ve 6 numaralı satırlarda tanımladığımız byte tipli değişkenlerimizin değerlerini sırasıyla 120 ve 110 yapalım. Yeni değerlerin her ikisi de byte tipine sığabilirler; fakat toplamları olan 230 değeri byte tipine Java’ya Giriş sığmaz. Java uygulaması bu durumda hata vermeyecektir ve 8 numaralı satırdaki System.out. println()’den dolayı konsola -26 yazacaktır. Bunu detayıyla şu şekilde açıklayabiliriz: Bildiğiniz gibi güncel bilgisayar sistemleri 2’lik (binary) sistemle çalışırlar, yani herşey 1 ve 0 ile ifade edilir. Dolayısıyla sayi1 ve sayi2 değişkenlerimizin 2’lik sistemdeki karşılıkları şu şekildedir: sayi1 = 120 = (01111000)2 sayi2 = 110 = (01101110)2 Sayıların 2’lik sistemdeki karşılıklarının başındaki 0, byte tipinin 1 byte’lık yani 8 bitlik olmasından dolayı sayıyı 8 bite tamamlamak içindir; ancak en başa eklediğimiz bu 0’ın taşıdığı çok daha farklı bir anlam vardır, Bu ilk değer sayının pozitif ya da negatif olmasını belirler. Eğer 0 ise arkasından gelen tüm bitler pozitif sayıya göre yorumlanacaktır, eğer 1 ise negatif sayıya göre yorumlanacaktır. Şimdi de sayıları toplayalım: sayi1 + sayi2 = 120 + 110 = 230 sayi1 + sayi2 = (01111000)2 + (01101110)2 = (11100110)2 toplam = (11100110)2 Az önce bahsettiğimiz işaret biti durumundan dolayı 2’lik sayının en başındaki değer olan 1, gerisindeki 7 bitin negatif sayıya göre yorumlanacağını gösterir. Dolayısıyla Java’da tamsayıların tümünde sayı değeri en soldaki bitin pozitif ya da negatif olması durumu netleştikten sonra ortaya çıkar. Örnek olarak şu sayıları ele alalım: 5 = (00000101)2 En baştaki 0 sayının pozitif olduğu anlamına gelir. Devamındaki 0000101’de de ilk 1’e kadar olan 0’ların bir anlamı yoktur. Böylece elimizde (101)2 kalır. Bunun da karşılığı 10’luk sistemde 5’tir. Açılımı ise şu şekildedir. 1 x 20 = 1 0 x 21 = 0 1 x 22 = 4 1 + 0 + 4 = 5 Bu noktadan sonra eğer sayımız negatif ise durumun değiştiğini göreceğiz. Örnek olarak -1’i ele alacağız. Eğer 1 = (00000001)2 ise; -1 = (11111110)2 + (00000001)2’dir. Yani (11111111)2’dir. Bu yönteme 2’nin tamlayanı yöntemi denir. (11111110)2 de (00000001)2’nin bire tamlayanıdır. Burada niçin bu kadar dolandırılmış diye düşünebilirsiniz; ancak unutmayın ki gündelik hayatta kullandığımız gibi bir “-” işaretine sahip değiliz. Bu durumu -1 ile 1’i toplayarak deneyelim: (00000001)2 + (11111111)2 = (100000000)2 Elimizde 9 bitlik bir veri oluştu; ancak byte veri tipi 8 bitlik bir veri tipiydi. Bu durumda en baştaki 1 değeri hiçbir değerlendirmeye katılmadan son 8 bit değerlendirilir; yani (00000000)2 değeri kullanılır. Burada ilk bit 0 olduğu için sayı pozitiftir diyebiliriz. Geri kalan 7 bit de 0 olduğu için sayı 10’luk sistemdeki 0’a karşılık gelir. Bu durumda aklınıza takılabilecek olan soru 0’ın pozitifliği ya da negatifliği olabilir. 0 sayısı matematikte ne pozitif tam sayılar kümesinin ne de negatif tamsayılar kümesinin bir üyesidir; ancak programlama dillerinde 0 değeri pozitiftir. 13 14 Bölüm 1 Sonuç olarak bilgisayar için negatif bir sayının 2’lik sistemde nasıl yazıldığını bulmak istiyorsak şu şekilde bir işlem gerçekleştirmeliyiz: Sayıyı 2’lik sistemde pozitif olarak yazın. Oluşan sayıdaki 0’ları 1, 1’leri 0 yapın. Sayıya 2’lik sistemde 1 ekleyin. Örnek olarak -5’e ulaşmaya çalışalım: 5 = (00000101)2 1’ e tamlayan = (11111010)2 2’ye tamlayan = (11111011)2 Böylece Uygulama 1.4b’nin açıklamasında elde ettiğimiz (11100110)2 değerini irdeleyebilir duruma geldik. Yukarıdaki işlemi tersten yaptığınızda da sayıya ulaşabilirsiniz. (11100110)2 – (00000001)2 = (11100101)2 1’e tamlayan = (00011010)2 (00011010)2 = 26 Dolayısıyla elimizdeki sayı 10’luk sistemdeki -26’ya karşılık gelir. Bu detayları bilmemeniz halinde basit bir sayısal problem üzerinde çok zaman harcayabilirsiniz. Çünkü gördüğünüz şekilde Java aritmetik işlemlerde taşan sayılar için hata vermez. Java’da Operatörler Java’da tipler üzerinde işlem yapmak için pek çok operatör bulunur. Bu operatörlerin çoğu C#’ta da aynı şekilde mevcuttur. Öncelik Sırası Öne gelen (prefix) ++ifade, --ifade Sona gelen (postfix) ifade++, ifade-- Tek ifade alan +ifade, -ifade, ~, ! Çarpımsal *, /, % Toplamsal +, - Kaydırma <<, >>, >>> İlişkisel <, >, <=, >=, instanceof Eşitlik ==, != Bit üzerinde (bitwise) VE (AND) & Bit üzerinde özel VEYA (EX-OR) ^ Bit üzerinde VEYA (OR) | Mantıksal VE (AND) && Mantıksal VEYA (OR) || Üçlü ifade alan (ternary) operatör ?: Atama =, +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=, >>>= Java’ya Giriş Gördüğünüz gibi Java’daki birkaç operatör dışında hemen hemen tüm operatörler C# ile aynıdır. Bit üzerinde işlem yapan operatörler kestirmeli (shortcut) operatörlerdir. Yani operandlarından (işleme giren değerlerinden) biri işlemin sonucunu kesin olarak ortaya koyuyorsa işlemin devamındaki operand değerlendirilmez. Örnek Uygulama 1.5a: 1public class Operatorler1 2{ 3 public static void main( String[] args) 4 { 5 int sayi, sonuc; 6 sayi = 5; 7 sonuc = sayi++ + ++sayi; 8 System.out.println(sonuc); 9 System.out.println(sayi); 10 } 11} Bu uygulamada 8 ve 9 numaralı satırlar işlendiğinde ekranda yazacak değerlerin ne olduğunu ve 7 numaralı satırın nasıl işlediğine bakalım. 6 numaralı satırda sayi değişkeninin değeri 5 yapıldıktan sonra 7 numaralı satırda sonuc = sayi++ + ++sayi; ifadesi işletilmektedir. Bu satırı daha detaylı inceleyelim: sonuc = sayi++ + ++sayi; ifadesinde önce ++sayi ifadesi çalışacaktır ve sayi değişkenine 1 ekleyerek değerini 6 yapacaktır. Daha sonra sayi++ işlemeden sayi değeri ile artırılmış sayi değerleri toplanarak sonuc değişkenine atama yapılacaktır. Böylece 6 + 6 = 12 değeri sonuc değişkeninin değeri olur. Bu atama yapıldıktan sonra sayi değişkenine sayi++’dan dolayı tekrar 1 eklenecektir ve sayi değişkeninin değeri 7 olacaktır. Kaydırma (Shift) İşlemleri Kaydırma işlemleri genellikle sistem programlama, oyun programlama gibi uzmanlık gerektiren alanlarda kullanılır. Değerimizi temsil eden 2’lik sayı sistemindeki değeri sağa ya da sola belirtilen bit sayısı kadar kaydırır ve kaydırılan alanlara 0 değerini koyar. İşaret bitiyle ilgili detayları da vardır. Bu sebeple bir örnekle gösterip detaylı olarak anlatmayacağım. Örnek Uygulama 1.5b: 1public class Operatorler2 2{ 3 public static void main( String[] args) 4 { 5 int sayi; 6 sayi = 5; 7 sayi <<= 2; 8 System.out.println(sayi); 9 } 10} Bu örnekte 7 numaralı satırda sayi değişkeninin bitleri 2 kademe sola kaydırılmaktadır. Bu işlem nasıl yapılıyor inceleyelim: 15 16 Bölüm 1 int 32 bit (4 byte) olduğu için 2’lik sayı sisteminde aşağıdaki gibi gösterilir. sayi = 5 = 00000000 00000000 00000000 00000101 Bu değer sola 1 bir kaydırıldığında; sayi = 0 00000000 00000000 00000000 00001010 = 10 değeri elde edilir. En baştaki 0 düşen bittir, ve en sondaki 101 değerleri sola kaydırılarak en sona da 1 tane 0 eklenmiştir. sayi <<= 2 ifadesi sola doğru 2 bitlik kaydırma işlemi olduğu için sayi 1 bit daha sola kaydırılmalıdır. sayi = 00 00000000 00000000 00000000 00010100 = 20 Böylece sayi değişkeninin değeri 20 olur. Dikkat ederseniz sola kaydırma 2 ile çarpma gibidir. İşlemlerde Parantez Kullanımı Matematiksel ya da mantıksal işlemlerde parantezlerin içine alınan ifadelerin önceliği ilk sıraya çıkar. Matematik bilgimizden tek farkı kullandığımız tek işaretin sadece normal parantez olmasıdır, köşeli parantez ya da küme parantezi kullanılmaz. Örnek: Bu ifadeyi matematiksel olarak yazmak istediğimizde eğer; d = m * P / n * r * T; şeklinde yazarsak çarpma ve bölmenin önceliklerinin aynı olması sebebiyle işlem soldan sağa sırayla gerçekleşecektir. Böyle olunca m ile P çarpıldıktan sonra n ile bölünecek, bunun sonucu da r ile T’nin çarpımıyla çarpılacaktır. Oysa ifadenin gösterdiği m ile P’nin çarpımının n, r ve T’nin çarpımına bölünmesi şeklindedir. Bu sebeple bu matematiksel ifadeyi; d = m * P / (n * r * T); şeklinde yazmalıyız. Karar Vermek ve Koşullu İfadeler Java’da da C#’taki gibi karar verme ve koşullu ifade yazmak için 2 ifade ve 1 operatör söz konusudur. Bunlar: 1� if...else 2. Üçlü ifade alan operatör (? :) 3� switch...case if...else Yapısı if... else yapısı Java’da da aynen C#’taki gibi kullanılır. En basit yapısı sadece if’in kullanıldığı şeklidir. Koşullu ifadelerde dikkat edilmesi gereken nokta koşulun sonucunun true ya da false, yani boolean bir ifade üretmesi gerekliliğidir. Bu yönüyle C geleneğiyle farklılaşır. Java’ya Giriş Örnek Uygulama 1.6a: 1import javax.swing.JOptionPane; 2public class Kosul1 3{ 4 public static void main( String[] args) 5 { 6 int sayi1 = Integer.parseInt(JOptionPane. showInputDialog(“Birinci sayıyı giriniz”)); 7 int sayi2 = Integer.parseInt(JOptionPane. showInputDialog(“Ikinci sayıyı giriniz”)); 8 if( sayi1 < sayi2) 9 { 10 JOptionPane.showMessageDialog(null, sayi1 + “ kucuktur “ + sayi2); 11 } 12 } 13} 8 numaralı satırda yapılan karşılaştırmanın sonucu, yani sayi1 değişkeninin değerinin sayi2 değişkeninin değerinden küçük olma durumu true ya da false, daha genel bir ifadeyle boolean bir sonuç üretir. Eğer karşılaştırmanın sonucu true ise if bloğunun içi çalıştırılır, if bloğu sonlandığında da akış kaldığı yerden devam eder; aksi takdirde if bloğunun içi çalıştırılmadan programın akışı if bloğu yokmuş gibi çalışmaya devam eder. Koşullar gibi bir sonraki bölümde inceleyeceğimiz döngü ve tekrarlı yapılarda da kodun okunurluğunu artırmak için if bloğunun içi tek ifadeden oluşsa bile blok parantezlerini, tek ifade için şart olmamasına rağmen kullanmayı alışkanlık haline getirin. Örnek Uygulama 1.6b: 1import javax.swing.JOptionPane; 2public class Kosul2 3{ 4 public static void main( String[] args) 5 { 6 int sayi1 = Integer.parseInt(JOptionPane. showInputDialog(“Birinci sayıyı giriniz”)); 7 int sayi2 = Integer.parseInt(JOptionPane. showInputDialog(“Ikinci sayıyı giriniz”)); 8 if( sayi1 < sayi2) 9 { 10 JOptionPane.showMessageDialog(null, sayi1 + “ kucuktur “ + sayi2); 11 } 12 else if( sayi1 > sayi2) 13 { 14 JOptionPane.showMessageDialog(null, sayi1 + “ buyuktur “ + sayi2); 15 } 16 } 17} 17 18 Bölüm 1 Bu örnekteki çalışma şekli Uygulama 1.6a’dakiyle aslında aynı şekildedir. Eğer 8 numaralı satırdaki karşılaştırma true sonuç vermezse akış 12 numaralı satırdan devam edecektir. Bu blok da else bloğudur. else bloğu aslında if bloğundan sonraki akışın ilk parçasıdır. else bloğunda bir karşılaştırma daha yapılmaktadır. Bu karşılaştırmada da aynen 8 numaralı satırdaki if bloğu gibi bir çalışma mekanizması söz konusudur. Eğer karşılaştırma sonucu true ise else bloğu çalışır; aksi takdirde else bloğundan sonraki noktadan; yani 16 numaralı satırdan itibaren çalışma devam eder. Bu uygulamada, Örnek Uygulama1.6a’ya göre farklılaşan tek nokta eğer 8 numaralı satırdaki karşılaştırma true sonuç üretirse 12 numaralı satırdaki else bloğu, her ne kadar kendinden sonra gelen ilk ifade olsa da çalışmaz. Örnek Uygulama 1.6c: 1import javax.swing.JOptionPane; 2public class Kosul3 3{ 4 public static void main( String[] args) 5 { 6 int sayi1 = Integer.parseInt(JOptionPane. showInputDialog(“Birinci sayıyı giriniz”)); 7 int sayi2 = Integer.parseInt(JOptionPane. showInputDialog(“Ikinci sayıyı giriniz”)); 8 if( sayi1 < sayi2) 9 { 10 JOptionPane.showMessageDialog(null, sayi1 + “ kucuktur “ + sayi2); 11 } 12 else if( sayi1 > sayi2) 13 { 14 JOptionPane.showMessageDialog(null,sayi1 + “ buyuktur “ + sayi2); 15 } 16 else 17 { 18 JOptionPane.showMessageDialog(null, sayi1 + “ esittir “ + sayi2); 19 } 20 } 21} Bu uygulamada değişen tek nokta 16 numaralı satırdaki koşulu olmayan else bloğudur. Böylece sayi1 değişkenin değeri ile sayi2 değişkeninin değeri arasında yapılan karşılaştırmada durum ne olursa olsun, bir exception üretilmediği sürece, bu 3 bloktan birinin çalışması kesindir. 16 numaralı satırdaki else bloğunun çalışması için 8 ve 12 numaralı satırlardaki karşılaştırmalardan false üretilmesi gerekir. Bu örnekte bu durum 2 sayıyı karşılaştırırken, iki sayının birbirine eşit olması durumudur. Bu bloklardan hangisi çalışırsa çalışsın, akış 20 numaralı satırdan devam edecektir. Java’ya Giriş Örnek Uygulama 1.6d: 1import javax.swing.JOptionPane; 2public class Kosul4 3{ 4 public static void main( String[] args) 5 { 6 int sayi1 = Integer.parseInt(JOptionPane. showInputDialog(“Birinci sayıyı giriniz”)); 7 int sayi2 = Integer.parseInt(JOptionPane. showInputDialog(“Ikinci sayıyı giriniz”)); 8 if( sayi1 < sayi2) 9 { 10 JOptionPane.showMessageDialog(null, sayi1 + “ kucuktur “ + sayi2); 11 } 12 else if( sayi1 < sayi2) 13 { 14 JOptionPane.showMessageDialog(null, sayi1 + “ buyuktur “ + sayi2); 15 } 16 } 17} Bu uygulamalarda göstermek istediğim durum 8 ve 12 numaralı satırlardaki karşılaştırmaların aynı olmasıdır. Bu durumda eğer 8 numaralı satırdaki karşılaştırma true üretirse 12 numaralı satır, aynı karşılaştırmayı yapmasına rağmen çalışmayacaktır. Eğer 8 numaralı satırdaki karşılaştırma false üretirse 12 numaralı satırdaki aynı karşılaştırma tekrar yapılacaktır ve doğal olarak o da false üreteceğinden else bloğu da çalışmayacaktır. Bunun da anlamı uygulamada durum ne olursa olsun hiçbir zaman else bloğunun çalışmayacağıdır. Sayısal karşılaştırmalardan sonra şimdi mantıksal işlemlere bakalım. Mantıksal işlemler sayısal ifadelerden farklı olarak zaten boolean sonuç üretir. Mantıksal işlemler AND (VE), OR (VEYA), EX-OR (ÖZEL VEYA), NOT (DEĞİL) işlemleridir. Mantıksal işlemlerin kestirmeli ve kestirmesiz olmak üzere 2 şekli vardır. Kestirmeli (short-circuited) işlemler doğruluğu kontrol edilecek ifadenin içinde bulunan ifadelerden herhangi biri bütün ifadenin sonucunu belirlediği anda diğer ifadeler kontrol edilmez. Kestirmeli (short-circuited) işlemlerde, bir ifadenin içinde bulunan bütün ifadelerin doğruluğu kontrol edilir, sonuç belirlense bile işlem sona ermez. 19 2 Döngüler 2 Döngüler • While Döngüsü • do Döngüsü • for Döngüsü • break ve continue Deyimleri Döngüler Birçok programlama dilinde olduğu gibi, Java dilinde de döngüler önemli bir yere sahiptir. Döngüler, yinelenen işlemler için oluşturulan kod bloğunun, belirli bir koşula bağlı olarak tekrarlanmasını sağlayan yapılardır. Java dilinde 3 tip döngü mevcuttur. Bunlar; while do for döngüleridir. Oluşturulan döngü yapıları birkaç farklılık dışında, içinde bulunan kod parçacığını baştan belirlenen ya da program akışı içerisinde belirlenen sayıda tekrarlanmasını sağlar. Döngüler belirli sayıda işlenmek üzere kurulabileceği gibi kod bloğu içerisinde gerçekleştirilecek bir koşul sağlanana kadar tekrarlanmak üzere de gerçekleştirilebilir. Döngülerin oluşturulması sırasında dikkat edilmesi gereken en önemli noktalardan biri, döngü içerisinde işlenecek kod parçacığının işlem sayısıdır. Belirli bir sayı ya da bir kontrol koşulu ile kurulmayan döngüler, program akışının sürekliliğini bozarak çalışan kodu sonsuz döngüye sokabilir. Bir döngünün işlenmesi sırasında ya da sonsuz döngü durumunda, kod bloğunun içerisinde müdahaleler gerekli olabilir. Bu müdahaler de break ve continue yapıları ile sağlanmaktadır. While Döngüsü while döngüsünün genel yapısı aşağıdaki gibidir. while( boolean_kontol_ifadesi) { Tekrarlanan_Ifade(ler) ; } Bu ifadede yer alan boolean_kontol_ifadesi kontrolu sonrasında boolean bir sonuç veren herhangi bir ifade olabilir. Tekrarlanan_Ifade(ler) ise yinelenmesi istenen kod bloğudur. C ve C++ dillerinde farklı veri tipleri ile kontrol yapılabilirken, Java da farklı olarak buradaki kontrol boolean bir ifade ile yapılabilir. While döngüsünde yineleme, belirlenen koşulun sağlanmasına kadar devam edecektir. Yineleme kontrolu çevrimin başında yapılır. Kontol ifadesinin sağlanmaması halinde, döngü içerisinde tekrarlanması istenen kod parçacığı hiç çalışmayabilir. Örnek uygulama 2.1: 1 int sayac=0; 2 while (sayac++ < 5) { 3 system.out.println(“Merhaba Dünya”) 4} Uygulama 2.1’ i incelediğimizde, ilk satırda, sayac adlı bir değişken tanımlanarak 0 değeri atanmıştır. Kontrol ifadesi, sayac++ ifadesiyle artırılan sayac değeri 5’ten küçük olduğu sürece “Merhaba Dünya” ifadesi yazdırılacaktır. Bu örnekte “Merhaba Dünya” ifadesi 5 kez yazılacaktır. 24 Bölüm 2 do Döngüsü Genel yapısı, do { Tekrarlanan_Ifade (ler) ; } while (boolean_kontol_ifadesi) şeklindedir. do döngüsü işleyiş biçimi olarak while döngüsüne benzer. Benzer şekilde bu yapıda da boolean bir koşul ile döngünün yinelenmesi kontrol edilmektedir. do döngüsünün en önemli farkı, kontol ifadesi başta değil sonda yapıldığından tekrarlanan ifade(ler) en azından bir kere işlenecektir. for Döngüsü Birçok programlama dilindeki temel gereksinimlerden biri olan, bir değişkenin belirli bir aralıkta artırılması ile kurulan döngü yapılarının en çok kullanılanı for döngüsüdür. Genel yapısı; for(ifade; booelan_kontrol_ifadesi ; deyim) { tekrarlanan_ifade(ler) } şeklindedir. ifade olarak adlandırılan kod, sıra döngünün işlenmesine geldiğinde ilk olarak çalışan ifadedir. Henüz döngü içerisindeki kod parçacığı çalışmaya başlamadan bir defaya mahsus olarak çalışacak, sonraki iterasyonlarda bu ifade çalışmayacaktır. Çoğunlukla döngüde kullanılacak değişkenin başlangıç değer atamasında kullanılır. boolean_Kontrol_ifadesi doğru değerini verdiği sürece döngünün yinelenmesini sağlayan kontrol ifadesidir. while döngüsünde olduğu gibi koşulun sağlanmaması durumunda for döngüsü de hiç çalışmayabilir. deyim ise döngü bloğunun sonunda çalışır. Çoğunlukla kullanılan değişkenin arttırımı bu bölümde yazılan kod ile sağlanır. Örnek Uygulama 2.2: 1For(int x=0; x < 10; x++) { 2 System.out.println(“Değer=” + x ); 3} Uygulama 2.2’de, döngüde kullanılan sayısal bir x değişkeni ilk ifadede tanımlanmış ve 0 değer ataması yapılmış, deyim bölümünde ise arttırımı gerçekleşmiştir. Kontrol ifadesi x değişkeninin 10’ dan küçük olduğu tüm değerler için doğrulanacak ve döngü aşağıdaki sonucu üretecektir: Değer=0 Değer=1 ..... Değer=8 Değer=9 Döngüler for döngülerinde genellikle döngünün sayılması için kullanılan bir değişken ihtiyacı oldduğundan, ifade bölümünde değişken tanımlamasına ve değer atamasına izin verilmektedir. Burada tanımlanan değişken, sadece for döngüsü içinde geçerlidir. Böylece diğer kısımlarda tanımlanan değişkenler ile çakışma ihtimali engellenmektedir. Döngü bloğu (scope) içerisinde kullanılacak başka değişkenler de yine ifade olarak adlandırılan bölümde tanımlanabilir. Bu işlem Örnek Uygulama 2.3’de belirtildiği gibi, tanımlanacak değişkenler arasına virgül konularak yapılır. Örnek Uygulama 2.3: 1int j,k; 2For(j = 3, k = 6; j + k < 20 ; j++, k +=2 ) { 3 System.out.println(“ j değeri=” + j +” k değeri= “ + k); 4} break ve continue Deyimleri break ve continue deyimleri Java’da da C#’ ta olduğu gibi kullanılır. Döngü yapıları içerisinde zaman zaman kod bloğunun işlenmesi ile ilgili müdahale ihtiyaçları ortaya çıkabilir. Belirli bir noktada döngüden çıkmak ya da o anda işlenen çevrimin sonlandırılması istenebilir. Bu ihtiyaçlar break ve continue deyimleri ile sağlanır. break deyimi, kod bloğunda kullanıldığı yerde döngünün tamamen sonlandırılmasını sağlar. Ancak dikkat edilmesi gereken en önemli nokta, içiçe (nested) yapılar kullanıldığında break sadece bulunduğu döngü bloğunun sonlandırılmasını sağlar, bir üst döngü işlenmeye devam edecektir. 25 3 Niteleyiciler (Modifiers) 3 Niteleyiciler (Modifiers) • Access Modifiers (Erişim Niteleyicileri) • Niteleyicilerde Metod Ezme (Access Modifiers Overriding) • Diğer Niteleyiciler Niteleyiciler (Modifiers) Niteleyiciler derleyiciye, kod bloğu içinde oluşturulan sınıf ve sınıf elemanları ile ilgili bilgi sağlayan anahtar kelimeler grubudur. En sık kullanılan niteleyiciler erişim niteleyicileridir. Bunlar; public protected private Erişim niteleyicileri dışında belirli bir biçimde kategorize edilemeyen niteleyiciler de bulunmaktadır. Bunlar ise; final abstract static transient synchoronized volatile Tanımlama sırasında bir niteleyici ile belirlenmeyen sınıf elemanları default olarak belirlenir. Default kod içerisinde kullanılan bir anahtar kelime olmayıp, bir niteleyici saptanmamasını ortadan kaldırmak için otomatik olarak atanır. Access Modifiers (Erişim Niteleyicileri) Erişim niteleyicileri olarak adlandırılan niteleyiciler, sınıf ve sınıf elemanlarının (değişken, method) erişim seviyeleri ile ilgili kısıtlama sağlar. Sınıf, değişken ya da metodların tanımlamalarında kullanılan bu anahtar kelimelerle erişim kısıtlamaları belirlenerek oluşturulan kodun güvenliği artırılır. Bazı istisnalar dışında erişim niteleyicileri tarafından kontrol edilebilen değişkenler sınıf seviyesi değişkenlerdir. Bir sınıfın içerisinde tanımlanan değişkenler erişim niteleyicileri ile belirlenmemiş olabilir. Bir metod değişkeni sadece o metodun içerisinde kullanılır. public Bir Java programında public olarak tanımlanan bir sınıf, değişken ya da metod herhangi bir kısıtlama olmaksızın kullanılabilir. Bir uygulamanın main() metodu, tüm Java runtime ortamlarından çağırılabilmesi için public olarak tanımlanır. Üst sınıflarda (top-level class) kullanılabilen tek erişim niteleyicisi public’tir. Üst sınıflarda private ya da protected tanımlaması yapılmamaktadır. private private olarak tanımlanan değişken ya da metodlar, sadece bulunduğu sınıf içinde kullanılabilir. Tanımlandığı sınıf dışından erişimi mümkün değildir. protected Sadece değişken ve metodlar protected olarak tanımlanabilir. protected sınıf elemanları bulundukları paket (package) içerisinde erişilebilir durumdadır. Bu özelliğiyle default yapısında benzemekle beraber, yer aldığı sınıfın alt sınıfları (sub classes) tarafından da erişilebilir durumdadır. protected olarak tanımlanan sınıf elemanı, bulunduğu sınıfın inherit edildiği bir başka package içerisinden de erişilebilir durumdadır. 30 Bölüm 3 Niteleyicilerde Metod Ezme (Access Modifiers Overriding) Niteleyici olarak adlandırılan sınıf elemanlarında overriding aşağıda belirtilen biçimlerde gerçekleşir. Geçerli Overriding Şekil 1: private default protected public private olarak tanımlanan bir metod; private, default, protected ve public metodlar tarafından override edilebilir. Default bir metod; default, protected ve public metodlar tarafından override edilebilir. protected bir metod; protected ve public metodlar tarafından override edilebilir. public bir metod ise sadece public bir metod tarafından override edilebilir. Geçersiz Overriding Şekil 2: public protected default private Default bir metod; private bir metod tarafından override edilemez. protected olarak tanımlanan bir metod; default ve private metodlar tarafından override edilemez. public bir metod ise; protected, default ve private metodlar tarafından override edilemez. Diğer Niteleyiciler Java’da kullanılan diğer niteleyicilerin tanımlanmasında sıra önem taşımaz. Örneğin public final tanımlaması ile final public tanımlaması birbirinden farklı değildir. Yine ptotected static ile static protected tanımlamaları aynı şekilde geçerli olacaktır. final final niteleyicisi, sınıf, metod ve değişkenlere uygulanmaktadır. final olarak belirlenen bir sınıf elemanı değiştirilemez. Örneğin, class SubMath extends java.lang.Math { } ifadesi derlendiğinde, “Can’t subclass final classes.” hatası verecektir. Dikkat edilmesi gereken önemli noktalardan biri, final bir değişken bir nesneye refere edildiğinde, değiştirilemeyen nesnenin instance’ı (örnek) değil, referans tipli değişkendir. Bir başka deyişle; final bir referans nesne değişkeni değiştirilemez ancak yaratılan instance bu kısıtlanmadan etkilenmez. Final niteleyicisinin C#’taki karşılığı sealed anahtar kelimesidir. Niteleyiciler (Modifiers) abstract abstract niteleyicisi sınıf ve metodlara uygulanmaktadır. abstract,. Microsoft .NET platformu üzerinde kullanılan Visual Basic .NET dilinde abstract bir sınıf oluşturmak için kullanılan anahtar kelime MustInherit’tir. Buradan anlayacağınız gibi, abstract bir sınıftan mutlaka türetme yapılmalıdır ve türetilen sınıf kullanılmalıdır. Eğer bir sınıf birden fazla abstract metoda sahipse, derleyici sınıfın abstract olarak tanımlanmasını zorlar. Bu durum kullanılabilirliği artırır. Sınıfı kullanırken, türetilen sınıf kullanılması gerektiğini ya da örneklenebileceğinin belirlenmesi için bakılması gereken yer sınıfın tanımlamasıdır. Aşağıda belirtilen durumlardan en az birisi gerçeklendiğinde, derleyici bir sınıfın abstract olarak tanımlanmasını zorlayacaktır. Sınıf birden çok abstract metoda sahipse Sınıf implementasyon sağlamayan bir veya birden çok abstract metod inherit ediyorsa Sınıf bir arayüz (interface) implemente etmek üzere tanımlandığı halde, interface’in tüm metodları için implementasyon sağlamıyorsa static static niteleyicisi değişkenler, metodlar hatta bir metodun parçası olmayan kod blokları için kullanılabilir. static olarak belirlenen sınıf elemanları bir sınıfla bağlantılı olmaktan çok sınıfa ait olarak düşünülebilir. static olarak belirlenmiş bir değişkenin değeri, ne kadar instance (örnek) türetilmiş olursa olsun değerini koruyarak sabit kalacaktır. static bir değişkeni refere etmenin iki yolu vardır: Sınıfın instance’ına referans verme yolu ile. Sınıf adı ile. Metodlar da static olarak tanımlanabilir. Sınıfın static verilerine ulaşabildikleri ve sınıfın diğer static metodlarını çağırabildikleri halde, static metodlar sınıflarının non-static (statik olmayan) elemanlarını kullanamazlar. Non-static metodlar this adlı örtülü (implicit) bir değişkene sahiptir. Non-static metodlarda, hangi nesnenin ya da metodun olduğunu belirtmeden bir değişken veya metod çağırılabilir. Örnek Uygulama 3.1: 1class Xyz 2{ 3 int ix; 4 void f1() { 5 ix++; 6 } 7} Örnek Uygulama 3.1’de tanımlanan ix değişkeni, hangi nesne ya da metoda ait olduğu belirtilmeden ix++ ifadesi ile artırılmaktadır. Derleyici varsayılan olarak bu ifadeyi this.ix++ olarak algılar ve doğru bir biçimde işler. static metodlarda ise this yapısı bulunmamaktadır. static bir metod içerisinden, bir başka metod ulaşılmak ya da çağırılmak istendiğinde ”Undefined variable:this” hatası verecektir. 31 32 Bölüm 3 static bir metod, non-static bir değişkene ulaşmak istediğinde hangi sınıfın değişkeni olduğunu ya da metodu çalıştıran sınıfın hangisi olduğunu belirterek çağırmak zorundadır. static bir metod non-static olarak override edilemez. Bu durumda “Static methods can’t be overriden” hata mesajı verecektir. transient transient niteleyicisi sadece değişkenler için kullanılmaktadır. transient bir değişken kendi objesinin sürekli bir parçası olarak tutulmaz. synchronized synchronized değişkenler multithread programlarda, kritik kodlara erişimi kontrol etmek amacıyla kullanılır. Multithreading daha sonraki bölümlerde detaylı olarak incelenecektir. volatile volatile olarak belirlenen değişkenler asenkron olarak değiştirilebilen değişkenlerdir. volatile değişkenler çok işlemcili ortamların konusudur. Niteleyiciler ile ilgili özeti aşağıdaki tabloda bulabilirsiniz. Şekil 3: Niteleyici Sınıf Değişken Metod Constructor public protected – (Default)* private – final – abstract – – static – – transient – – – volatile – – – synchronized – – – * default bir niteleyici değil, niteleyici belirlenmediğinde varsayılan olarak kullanılan anahtar sözcüktür. 4 Casting ve Conversion 4 Casting ve Conversion • Basit Tiplerde Conversion • Referans Tiplerde Conversion Casting ve Conversion Java’ da, C# ve tüm programlama dillerinde olduğu gibi kullanılan her değişkenin bir tipi vardır. Bunlar int, long ve double gibi basit (primitive) değişkenler olduğu gibi, sınıf değişkenleri (Vector, Graphics vb) ya da arayüzler (LayoutManager ve Runnable vb) olabilir. Bu bölümde, veri tipleri arasındaki dönüşümleri ele alacağım. Veriler gerek açıkça (explicitly) ya da örtülü (implicitly) olarak dönüştürülebilir. Bir başka deyişle kendi bilgi ve isteğimiz dahilinde ya da derleyici tarafından otomatik olarak yapılan veri tipi dönüşümleri mümkündür. Açıkça gerçekleştirilen veri tipi dönüşümleri casting olarak adlandırılır. Bu durumda kodlama esnasında kendi isteğimizle bir değişkenin tipini değiştiririz. Örnek Uygulama 4.1: 1ix = (int) mylix; Örnek Uygulama 4.1’de, mylix değişkeni, int olarak ix değişkenine casting yapılarak atanmaktadır. Örtülü olarak gerçekleşen veri tipi dönüşümleri ise, değişken atamaları sırasında derleyici tarafından otomatik olarak yapılmaktadır. Açık olmayan, örtülü dönüşümler conversion olarak adlandırılır. Örnek Uygulama 4.2: 1int x, y; 2double d; 3x = 3; 4y = 5; 5d = x + y; Örnek Uygulama 4.2’de double değişkenine atanan tam sayı değerlerin toplamı 8.000000... değerini alacaktır. Bu durumda gerçekleştirilen değişken tipi dönüşümü derleyici tarafından otomatik olarak gerçekleştirilecektir. Java programlama dilinde basit tipler ve referans tipleri olmak üzere iki veri tipi kategorisi bulunmaktadır. Basit tipler olarak adlandırılan veri tipleri; boolean, char, byte, short, int, long, float, double’dır. Referans tipleri ise; JDK tarafından sağlanan birçok sınıf, arayüzün yanısıra, Java geliştiricileri tarafından geliştirilen sınıflardır. Basit tipler ve referans tipleri olarak anılan tüm veri tipleri arasında, belirli kurallar dahilinde casting ve conversion mümkündür. Bu bağlamda, 4 çeşit veri tipi dönüşümü ortaya çıkmaktadır: Basit veri tipleri arasında conversion Basit veri tipleri arasında casting Referans tipleri arasında conversion Referans tipleri arasında casting 36 Bölüm 4 Basit Tiplerde Conversion Basit veri tiplerinin dönüşümünde 3 tip conversion karşımıza çıkar, bunlar: Atama Metod çağırma Aritmetik yükseltme (arithmetic promotion) Basit Tiplerde Conversion: Atama Atama dönüşümü, bir değişkene orijinal değerinden farklı bir değer ataması yapıldığında gerçekleşir. Örnek Uygulama 4.3: 1int i; 2double d; 3i = 10; 4d = i; Örnek Uygulama 4.3’de tanımlanan i int, d ise double değişkenlerdir. double bir değişken tamsayı ifade saklayamayacağından, d = i ataması yapıldığında, d değişkeninin sakladığı değer 10.000000... olacaktır. Ancak bazı atamalar yukarıda verdiğimiz örnekteki gibi geçerli ifadeler olmayacaktır. Örnek Uygulama 4.4: 1double d; 2short s; 3d = 1.2345; 4s = d; Örnek Uygulama 4.4’te yer alan atama, double bir ifadenin short veri tipine atanması şeklindedir. short veri tipi double değerini saklayamaz. Bu kod işlenmeyecek, “Incompitable type for=.” hatasını verecektir. Daha büyük değer saklayan değişkenlerden daha küçük değer taşıyan değişkenlere dönüşüm bir şişedeki sıvıyı küçük bir bardağa taşımaya benzer ancak explicit casting kullanılmalıdır. Basit tiplerde atamanın genel kuralları şu şeklidedir: boolean bir veri tipi diğer veri tiplerine çevrilemez. Genişletme dönüşümü (widening conversion) sözkonusu ise, boolean olmayan (non-boolean) bir veri tipi, boolean olmayan bir veri tipine dönüştürülebilir. Daraltma dönüşümü (narrowing conversion) sözkonusu ise boolean olmayan bir veri tipi, boolean olmayan bir veri tipine dönüştürülemez. Genişletme dönüşümü, daha küçük değerler saklayan veri tiplerinden daha geniş değerler saklayan veri tiplerine gerçekleştirilebilir. Bu durumda herhangi bir veri kaybı olmayacaktır. Genişletme Dönüşümü; byte veri tipinden; short, int, long, float veya double veri tiplerine, short veri tipinden; int, long, float veya double veri tiplerine, char veri tipinden; int, long, float veya double veri tiplerine, Casting ve Conversion int veri tipinden; long, float veya double veri tiplerine, long veri tipinden; float veya double veri tiplerine, float veri tipinden; double veri tipine gerçekleştirilebilir. Şekil 4.1: char int byte long float double short Şekil 1’de belirtilen tüm dönüşümler veri kaybı yaşanmaksızın yapılan dönüşümlerdir. Yine şekilde belirtilen ok yönü dışında gerçekleştirilecek dönüşümler ise daraltma dönüşümü (narrowing conversion) olarak tanımlanır. Bu tip conversionlarda veri kaybı olabilir. Daraltma dönüşümü ise; byte veri tipinden; char veri tipine, short veri tipinden; byte veya char veri tiplerine, char veri tipinden; byte veya short veri tiplerine, int veri tipinden; byte, short veya char veri tiplerine, long veri tipinden; byte, short, char veya int veri tiplerine, float veri tipinden; byte, short, char, int veya long veri tiplerine, double veri tipinden; byte, short, char, int, long veya float veri tiplerine gerçekleştirilebilir. Java programlama dilinde ondalıklı sayısal değerler double, ondalıksız sayısal ifadeler integer veri tipindedir. Normal koşullarda, integer ve double değerlerin daha küçük değerler tutan değişkenlere atanmasının derleyici hatasına sebep olacağını düşünebiliriz. Ancak Java bu durumları kendi içinde esneterek ve atamayı hatasız olarak gerçekleştirir. Örnek Uygulama 4.5: 1float f = 2.345; 2byte b = 3; 3short s = 5; 4char c = 7; Örnek Uygulama 4.5’te belirtilen atamalar hatasız olarak gerçekleşecektir. Örnek Uygulama 4.6’de belirtilen kodun ikinci satırı ise sayısal ifade ataması yapılmadığından derlenmeyecektir. Örnek Uygulama 4.6: 1int i = 15; 2byte b = i; 37 38 Bölüm 4 Basit Tiplerde Conversion: Metod Çağırma Metod Çağırma dönüşümü, belirli bir veri tipinde argüman bekleyen bir metoda, farklı bir veri tipinde argüman sağlandığında derleyicinin otomatik olarak gerçekleştirdiği dönüşümdür. Örnek Uygulama 4.7: 1float f; 2double d; 3f = 2.34567f; 4d = Math.cos(f); Örnek Uygulama 4.7’de belirtilen Math sınıfına ait cos() metodu, veri tipi olarak double bir argüman beklemektedir. f değişkeninin float değeri cos() metodunda işlenmeden önce otomatik olarak double veri tipine dönüştürülecektir. Metod çağırma dönüşümlerinde de atama dönüşümlerini belirleyen kurallar geçerlidir. Derleyici zamanında (compile time) genişletme dönüşümüne izin verilirken, daraltma dönüşümüne izin verilmez. Basit Tiplerde Conversion: Aritmetik Yükseltme Aritmetik yükseltme, aritmetik ifadeler kullanılarak işlem yapıldığında gerçekleşen dönüşüm tipidir. Örnek Uygulama 4.8: 1short s = 5; 2int i = 8; 3float f = 13.2f; 4double d = 13.2; 5if ( s – i <= f * d) 6 system.out.println( “İlk işlem küçük veya eşit”; 7else 8 system.out.println( “İlk işlem küçük veya eşit değil”; Örnek Uygulama 4.8, if bloğunun içinde aritmetik ifadeler ile işlenen değişkenler farklı veri tiplerindedir. Derleyici, aritmetik işlemleri anlamlı bir biçimde gerçekleştirmek için gerekli dönüşümleri otomatik olarak gerçekleştirecektir. Bu dönüşümlerin tamamı daha önce belirttiğimiz genişletme dönüşümü olacaktır. Aritmetik yükseltme dönüşümü kuralları tekli operatörler (unary operators) ve ikili operatörler (binary operators) için farklılaşmaktadır. Tekli operatörler tek bir işlenen (operand) üzerinde işlem yaparken, ikili operatörler iki işlenen üzerinde işlem yapmaktadır. Şekil 4.2: Tekli operators Binary operators + - ++ -- + - * / & ^ | ~ % >> >>> << Casting ve Conversion Tekli operatörler için uygulanan dönüşüm kuralları aşağıdaki gibidir; Eğer kullanılan işlenenin veri tipi byte, short ya da char ise; int veri tipine dönüştürülür. (Eğer kullanılan operatör ++ ya da -- ise dönüşüm yapılmayacaktır) Yukarıda belirtilen dışındaki veri tiplerinde dönüşüm yapılmaz. İkili operatörler için uygulanan kurallar ise aşağıdaki şekilde belirlenmiştir. Eğer işlenenlerden birinin veri tipi double ise, diğer işlenen double veri tipine dönüştürülecektir. Eğer işlenenlerden birinin veri tipi float ise, diğer işlenen float veri tipine dönüştürülecektir. Eğer işlenenlerden birinin veri tipi long ise, diğer işlenen long veri tipine dönüştürülecektir. Yukarıda belirtilen dışındaki veri tiplerinde int veri tipine dönüştürülecektir. Basit Tiplerde Casting Şimdiye kadar ele aldığımız dönüşüm tipleri örtülü olarak gerçekleştirilen, Java’nın otomatik olarak gerçekleştirdiği dönüşümlerdi. Bu dönüşümler, açıkça bir kod yazmamızı gerektirmeyen dönüşümlerdir. Casting ise, Java’ya bir dönüşümü yapmasını söylemektir. Veri tipleri arasındaki dönüşümü belirleyen, yazacağımız kod olacaktır. Genişletme dönüşümü olarak belirttiğimiz dönüşümlerde casting’e ihtiyaç duyulmamakta, derleyici otomatik olarak yapmaktadır. Casting daha çok daraltma dönüşümü olarak bilinen dönüşümlerde kullanılmaktadır. Bu dönüşümlerde veride kayıp riski bulunduğundan, derleyici otomatik olarak yapmamakta, riskin alınarak dönüşümün sağlanması gerektiği yazılacak kod ile belirtilmelidir. Örnek Uygulama 4.9: 1short s = 362; 2byte b = s; 3system.out.println(“s = ” + s + “, b = ” + b ) Örnek Uygulama 4.9’da belirtilen ifade derlendiğinde, 2. satırda derleyici hatası verecektir. Çünkü short veri tipinden daha dar bir veri tipi olan byte veri tipine atama yapılmak istenmektedir. Bu dönüşüm Şekil 4.3’ te belirtilmiştir. Şekil 4.3: 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1 0 b = (byte)s Bu durumda derleyici açıkça dönüşüm yapılması gerektiğini bildiren; Explicit cast needed to convert short to byte uyarısı verecektir. Örtülü casting yaparsak aşağıdaki durum oluşacak ve veri kaybı sözkonusu olacaktır. 39 Bölüm 4 Örnek Uygulama 4.10: 1short s = 362; 2byte b = (byte)s; 3system.out.println(“b = ” + b ) b = 106 olarak yazılacaktır. Basit veri tiplerinin casting yapılmasında aşağıdaki kurallar geçerlidir. Boolean olmayan herhangi bir veri tipi, boolean olmayan herhangi bir veri tipine dönüştürülebilir. Boolean bir veri tipi herhangi bir veri tipine, herhangi bir veri tipi boolean veri tipine dönüştürülemez. Referans Tiplerde Conversion Referans tipli değişkenleri de basit veri tipleri gibi atama, metod çağırma ve casting ile dönüştürülebilmektedir. Ancak eski ve yeni tipler arasındaki dönüşümlerde daha çok kombinasyon olduğundan daha karmaşıktır. Referans tiplerinde conversion da basit tiplerde conversion gibi derleyici zamanında gerçekleşmektedir. Daha sonra belirteceğimiz üzere object casting için ise bu geçerli değildir. Referans Tiplerde Conversion: Atama Referans tiplerde atama, bir nesne referans değerini farklı bir veri tipindeki değişkene atadığımızda gerçekleşir. Genel notasyon olarak aşağıdaki biçimde uygulanmaktadır: EskiTip x = new EskiTip(); YeniTip y = x; Referans tiplerde conversion’da, eskitip ve yenitip üyeler (member) arasındaki dönüşüm kombinasyonları Şekil 4.4’te belirtilmiştir. Şekil 4.4: Eski Tip Yeni Tip 40 Sınıf (Class) Arayüz (Interface) Dizi (Array) Sınıf Eskitip, Yenitip’in altsınıfı olmalıdır. Yenitip nesne olmalıdır. Yenitip nesne olmalıdır. Arayüz (Interface) Eskitip, yenitip’in arayüzünü implemente etmelidir. Eskitip, Yenitip’in alt-arayüz’ü (subinterface) olmalıdır. Yenitip cloneable ya da serializable olmalıdır. Dizi (Array) Derleyici hatası oluşacaktır. Derleyici hatası oluşacaktır. Eskitip, yenitip’in dizisine dönüştürülebilecek bir dizi olmalıdır. Tüm dönüşüm kurallarına uygulanmasa da, genel bir bakış açısı olarak, izin verilen referans tipi dönüşümleri, miras hiyerarşisinde (inheritance hierarchy) yukarı doğru olmaktadır. Yani, eskitip, yenitip’ten miraslanmalıdır. Casting ve Conversion Genel kuralları aşağıdaki şekilde belirlenebilir: Bir arayüz sadece yine bir arayüze ya da nesneye dönüştürülebilir. Eğer yenitip bir arayüz ise, eskitip’in alt-arayüzü olmalıdır. Bir sınıf yine bir sınıfa ya da arayüze dönüştürülebilir. Bir sınıfa dönüştürüldüğünde, yenitip eskitip’in alt-sınıfı olmalıdır. Bir arayüze dönüştürüldüğünde, eski sınıf arayüzü implemente etmelidir. Bir dizi, sınıf nesnesine ya da cloneable veya serializable bir arayüze ya da diziye dönüştürülebilir. Sadece nesne referans tipinin dizisi bir diziye dönüştürülebilir ve eski eleman tipi yeni eleman tipine çevrilebilir olmalıdır. Referans Tiplerde Csonversion: Metod Çağırma Genel olarak referans tiplerde atama dönüşümünde tanımlanan kurallar bu dönüşüm tipinde de geçerlidir. Bir süpersınıfa dönüşüme izin verilirken, altsınıfa dönüşüme izin verilmez. Genel kuralları aşağıda belirtilmiştir. Bir arayüz sadece yine bir arayüze ya da nesneye dönüştürülebilir. Eğer yenitip bir arayüz ise, eskitip’in alt-arayüzü olmalıdır. Bir sınıf yine bir sınıfa ya da arayüze dönüştürülebilir. Bir sınıfa dönüştürüldüğünde, yenitip eskitipin alt-sınıfı olmalıdır. Bir arayüze dönüştürüldüğünde, eski sınıf arayüzü implemente etmelidir. Bir dizi, sınıf nesnesine ya da klonlanabilir veya serileştirilebilir bir arayüze ya da diziye dönüştürülebilir. Sadece referans tipinin dizisi bir diziye dönüştürülebilir ve eski eleman tipi yeni eleman tipine çevrilebilir olmalıdır. Referans Tiplerin Çevrimi Referans tiplerde çevrim daha önce ele aldığımız basit tiplerin çevrimine benzer. Atama ve metod çağırma yöntemlerinde izin verilen tüm açık çevrimler (explicit casting) bu yöntemde de kullanılabilmektedir. Referans tiplerin çevriminde derleyici zamanında olduğu gibi çalışma zamanında uygulanan kurallar da bulunmaktadır. Bunu sebebi, referans tipli değişkenlere ilişkin bazı bilgilerin derleyici zamanında henüz bilinmemesidir. Referans tiplerin çevrimi için derleyici zamanı kuralları Şekil 4.5’ te belirtilmiştir. Şekil 4.5: Yeni Tip Eski Tip Miras verebilir sınıf (non-final class) Miras veremeyen sınıf (final - sealed class) Arayüz (interface) Dizi (array) Miras verebilir sınıf (nonfinal class) Eskitip, yenitip’i genişletmelidir Eskitip, yenitip’i genişletmelidir Herzaman geçerlidir Eskitip nesne olmalıdır Miras veremeyen sınıf (final sealed class) Eskitip, yenitip’i genişletmelidir Eskitip ve Yenitip aynı sınıf olmalıdır. Yenitip arayüzü implemente etmelidir Derleyici hatası oluşacaktır Arayüz (interface) Herzaman geçerlidir Eskitip, Yenitip’in arayüzününü implemente etmelidir Herzaman geçerlidir Derleyici hatası oluşacaktır Dizi (array) Yenitip nesne olmalıdır Derleyici hatası oluşacaktır Derleyici hatası oluşacaktır Eskitip, yenitip’in dizisine dönüştürülebilecek bir dizi olmalıdır 41 42 Bölüm 4 Eskitip ve Yenitip arasındaki dönüşümlerde derleyici zamanında geçerli kurallar aşağıda belirtilmiştir. Eskitip ve Yenitip dönüşümlerinde her ikisinin de sınıf olması halinde, bir sınıf diğerinin altsınıfı olmalıdır. Eskitip ve Yenitip’lerin her ikisi de dizi ise, her iki dizi de referans tipi barındırmalı ve eskitipin bir elemanının yenitipin bir elemanına çevrimi geçerli olmalıdır. Bir arayüz ile Miras verebilir sınıf (non-final class) arasında herzaman çevrim yapılabilir. Çalışma zamanında uygulanan kurallar ise; Eğer Yenitip bir sınıf ise, dönüştürülen ifadenin sınıfı ya Yenitip olmalıdır, ya da Yenitip’ten miras alınmalıdır. Eğer Yenitip bir arayüz ise, dönüştürülen ifadenin sınıfı Yenitip’i implemente etmelidir. olarak belirlenebilir. 5 Yapısal Programlama 5 Yapısal Programlama • Yapısal Programlama Nedir? • Nesne Yönelimli Programlamanın Temel Kavramları Yapısal Programlama Yapısal Programlama Nedir? Yapısal programlamada uygulamanın akışı fonksiyonlara bölünerek her bir fonksiyonun belirli bir işi gerçekleştirmesi sağlanır. Yazılımın amacı gerçek dünyadaki iş akışlarının bilgisayar ortamında yürütülmesi olduğu için, modellenmesi istenen kavramlar fonksiyonlar ve değişkenler kullanılarak modellenmeye çalışılmaktadır. Gerçek dünyada fonksiyon ve değişken gibi kavramlar yerine nesnelerin bulunmasından dolayı modelleme tam anlamıyla gerçekleştirilememektedir. Nesne Yönelimli Programlamanin Temel Bileşenleri Sınıf ve Nesne Modellememiz gereken iş akışlarını yazılım ortamında temsil etmek için kullandığımız temel bileşen sınıflardır. Sınıf sadece bir modeldir, uygulama çalışırken fiziksel olarak oluşturulmayan, kod üzerinde kalan soyut bir yapıdır. Nesne ise uygulamanın çalışması aşamasında fiziksel olarak bilgisayarın belleğinde oluşan ve başka nesnelerle iletişim kurarak iş akışlarının gerçekleşmesini sağlayan bir bileşendir. C# .Net dilinde olduğu gibi Java dilinde de yeni bir nesne oluşturmak için new anahtar kelimesi kullanılır, new anahtar kelimesini kullandığımız her kod parçası uygulamanın çalışması sırasında bellek üzerinde yeni bir nesne oluşturur. Nesne yönelimli programlamaya yeni başlayan yazılım geliştiriciler sınıf ve nesne kavramları arasındaki farkı anlamakta zorluk çekebilir, bunun sebebi uygulamanın çalışma aşamasında sınıf diye bir varlığın olmaması gibi gerçek hayatta da sınıf diye bir varlığın bulunmamasıdır. Nitelikler (Attributes) Bir sınıfın içinde bulunan ve o sınıftan oluşturulacak olan nesnelerin özelliklerini tutan yapılardır. Sınıfların kodlanması sırasında değişkenler şeklinde oluşturulurlar ve sınıftan nesne örneği oluşturulduğu anda bellek üzerinde oluşurlar. Gerçek hayattaki nesnelerin çeşitli niteliklere sahip olması gibi yazılım ortamında oluşan nesneler de çeşitli niteliklere sahiptir. Örnek Uygulama 5.1: 1 public class Nokta 2 { 3 private int x; 4 private int y; 5 } Örnekte, nokta kavramının yazılım ortamındaki modeli olan Nokta sınıfı oluşturulmuştur ve bu sınıftan oluşturulacak olan nesnelerin x ve y koordinatlarını temsil etmek için x ve y nitelikleri olacaktır. x ve y nitelikleri sınıfın içinde yer alsa da değerlerini Nokta sınıfından bir nesne oluştuktan sonra ve oluşan nesne üzerinden alacaktır. Davranışlar Yazılım ortamında bellek üzerinde oluşan nesnelerin sergileyeceği davranışlar da nitelikler gibi bu nesnenin modeli olan sınıf üzerinde bulunur. Davranışlar, sınıfların kodlanması sırasında metodlar şeklinde oluşturulur ve bir sınıfın içinde bulunan metodlar bu sınıftan oluşturulacak olan bütün nesnelerin ortak davranışlarını belirler. Gerçek hayattaki nesnelerin çeşitli davranışlara sahip olması gibi yazılım ortamındaki nesnelerin de çeşitli davranışları bulunur. Her bir nesnenin kendi davranışını modelleyen metodlara örnek metodu(instance method) adı verilir. Örnek Uygulama 5.2: 1 public class Nokta 2 { 46 Bölüm 5 3 private int x; 4 private int y; 5 public void YerDegistir(int yeniX, int yeniY) 6 { 7 x = yeniX; 8 y = yeniY; 9} 10} Örnek Uygulama 5.1’de oluşturduğumuz Nokta sınıfımıza YerDegistir davranışını ekledik. Bu metod sayesinde Nokta sınıfından oluşan bütün nesnelere iki boyutlu uzayda yer değiştirme kabiliyeti vermiş olduk. YerDegistir metodumuz iki tane tam sayı cinsinden parametre alır ve nesnemizin x ve y koordinat değerlerini yeni koordinat değerleriyle değiştirir. Nesnelere ait metodlar parametre olarak int, string gibi yerleşik tiplerden değişkenler alabilecekleri gibi kendi sınıfı veya başka bir sınıf cinsinden nesneler de alabilir. Örnek Uygulama 5.3: 1 public class Nokta 2 { 3 private int x; 4 private int y; 5 public void YerDegistir(int yeniX, int yeniY) 6 { 7 x = yeniX; 8 y = yeniY; 9} 10 public double UzaklikHesapla(Nokta ikinciNokta) 11 { 12 double uzaklik = Math.sqrt(Math.pow(x - ikinciNokta. x, 2) + Math.pow(y - ikinciNokta.y, 2)); 13 14 } return uzaklik; 15} Herhangi bir nokta nesnesinin başka bir nokta nesnesi ile arasında olan uzaklığı bulmak için bir metod yazdık. UzaklikHesapla metodu parametre olarak bir Nokta nesnesi alıyor ve üzerinde çalışmakta olduğu nesneyle parametreden gelen nesne arasındaki uzaklığı double cinsinden hesaplayarak döndürüyor. Kare alma ve karekök hesaplama işlemleri için yerleşik Java sınflarından biri olan Math sınıfını kullandık. Metod içinde kullanılan x ve y, davranışın sahibi olan nesneye ait x ve y değerleridir. Parametre olarak gelen ikinci nesneye ait x ve y nitelikleri ise ikinciNokta.x ve ikinciNokta.y şeklinde kullanılmıştır. Bir sınıfın içinde aynı isimli birden fazla metod tanımlanabilir. Bu durumda tek şart tanımlanacak olan metodların parametre sayılarının veya parametre tiplerinin farklı olmasıdır, aynı tipten aynı sayıda parametre içeren aynı isimli birden fazla metod tanımlanırsa derleme hatası alırız. Bu işleme metodların aşırı yüklenmesi (overloading) adı verilir. Bu mekanizma C# dilindekine benzer bir mantıkla gerçekleştirilebilir. Yapısal Programlama Örnek Uygulama 5.4: 1 public class Nokta 2 { 3private int x; 4private int y; 5public void YerDegistir(int yeniX, int yeniY) 6{ 7 x = yeniX; 8 y = yeniY; 9} 10 public double UzaklikHesapla(Nokta ikinciNokta) 11 { 12 x, 2) double uzaklik = Math.sqrt(Math.pow(x - ikinciNokta. 13 return uzaklik; 14 } + Math.pow(y - ikinciNokta.y, 2)); 15 public double UzaklikHesapla(int ikinciNoktaX, int ikinciNoktaY) 16 { 17 double uzaklik = Math.sqrt(Math.pow(x ikinciNoktaX, 2) + Math.pow(y - ikinciNoktaY, 2)); 18 return uzaklik; 19 } 20} Son durumda, Nokta sınıfımızın içinde aynı işi yapan aynı isimli iki tane metod var. İlk yazdığımız metod parametre olarak bir Nokta nesnesi ile çalışıyor, ikinci metod ise harici bir noktanın x ve y koordinat değerlerinin parametre olarak verilmesini bekliyor. Sınıfların içinde bulunan metodlar yine aynı sınıfın içinde bulunan başka metodları da kullanabilir. Nokta sınıfında yazdığımız UzaklikHesapla metodları içinde aynı algoritmayı iki kere yazdık. Bunun yerine UzaklikHesapla metodlarından birinin diğer UzaklikHesapla metodunu kullanarak hesaplama yapmasını aşağıdaki gibi sağlayabiliriz. Böylece gerçeklememiz gereken bir algoritmayı veya iş mantığını bir kere gerçekleyerek koddan tasarruf ettiğimiz gibi uygulamamızı olası değişimler için daha uygun hale getiririz. Örnek Uygulama 5.5: 1 public class Nokta 2 { 3 private int x; 4 private int y; 5 public void YerDegistir(int yeniX, int yeniY) 6 { 7 x = yeniX; 47 48 Bölüm 5 8 9 } 10 y = yeniY; public double UzaklikHesapla(Nokta ikinciNokta) 11 { 12 13 } return UzaklikHesapla(ikinciNokta.x, ikinciNokta.y); 14 public double UzaklikHesapla(int ikinciNoktaX, int ikinciNoktaY) 15 { 16 double uzaklik = Math.sqrt(Math.pow(x ikinciNoktaX, 2) + Math.pow(y - ikinciNoktaY, 2)); 17 return uzaklik; 18 } 19} Yapıcı Metod (Constructor) Uygulama içinde nesnelerin oluşturulması aşamasında otomatik olarak çalışan metodlara kurucu veya yapıcı metodlar denir. Genelde nesnelerimizin içinde bulunan niteliklere ilk değerler atamak için kullanılır. Yapıcı metodlarla ilgili sınırlamalar aşağıdaki gibidir: 1� Yapıcı metodlar yeni nesne oluşturulmasını sağlamaz, bir nesnenin oluşma aşamasında çalışır. 2. Yapıcı metodların isimleri sınıfın ismiyle aynı olmak zorundadır. 3� Yapıcı metodların geri dönüş değeri olamaz, geri dönüş değeri olan bir yapıcı metod tanımı yaparsak derleme hatası alırız. 4. Bir sınıf içinde birden fazla yapıcı metod bulunabilir. Parametre tiplerinin veya sayılarının farklı olması birden fazla yapıcı metod tanımlanabilmesi için yeterlidir. 5. Java dilinde de .Net ortamında olduğu gibi varsayılan, parametresiz bir yapıcı metod vardır. Sınıfımızın içinde parametreli veya parametresiz bir tane yapıcı metod oluşturduğumuzda varsayılan yapıcı metod geçersiz hale gelir. 6. New anahtar kelimesi ile sınıftan nesne oluşturulmasına izin vermek istiyorsak yapıcı metodları public olarak tanımlamamız gerekir. Sınıfın içinde bir tane yapıcı metod varsa ve bu metodun erişim tipi private ise, bu sınıftan nesne oluşturulamaz. Örnek Uygulama 5.6: 1 public class Nokta 2 { 3private int x; 4private int y; 5public Nokta() 6{ 7 x = 0; 8 y = 0; 9} 10} Yapısal Programlama Nokta sınıfımıza parametresiz bir yapıcı metod ekleyerek yeni oluşan her nesnenin x ve y koordinatlarının sıfır değerine sahip olmasını yapıcı metod aracılığıyla sağladık. Sınıfımıza bir tane de parametreli yapıcı metod ekleyelim. Örnek Uygulama 5.7: 1 public class Nokta 2 { 3private int x; 4private int y; 5public Nokta() 6{ 7 x = 0; 8 y = 0; 9} 10 public Nokta(int x, int y) 11 { 12 this.x = x; 13 this.y = y; 14 } 15} Son durumda iki tane yapıcı metodumuz oldu. Parametresiz yapıcı metod kullanılarak oluşan bütün nesnelerin x ve y koordinatları sıfır değerini alıyor, parametreli yapıcı metodumuz ise nesnenin oluşması sırasında noktanın farklı koordinat değerlerine sahip olmasını sağlıyor. Parametreli yapıcı metodumuzun gövdesinde yer alan this anahtar kelimesi, üzerinde çalışmakta olduğumuz nesneyi temsil eder. Nesne özelinde değerlendirilebilen nitelik ve metodlara this anahtar kelimesi ile ulaşabiliriz. Daha önceki örneklerde this anahtar kelimesini kullanmamıza gerek kalmamıştı, çünkü x ve y isimli birer tane değişken bulunuyordu. Son yazdığımız metodda ise biri parametreden gelen, diğeri nesnenin sahip olduğu niteliği temsil eden iki tane x değişkeni olduğundan bu değişkenleri biribirinden ayırabilmek için nesneden gelen x değişkeninin başına this anahtar kelimesini ekliyoruz. Yapıcı metodun parametrelerinden biri olan x değişkeni ile nesne üzerindeki niteliği temsil eden x değişkeni arasında hiçbir organik bağ yoktur, farklı isimlere sahip olsalar da uygulama istendiği şekilde çalışır. Aralarındaki ilişki yapıcı metodumuzun içinde yaptığımız değer ataması ile sınırlıdır. Parametreli yapıcı metodumuz Şekil 5.8’de belirtilen iki şekilde de yazılabilirdi: Örnek Uygulama 5.8: 1 public Nokta(int yeniX, int yeniY) 2 { 3this.x = yeniX; 4this.y = yeniY; 5 } 6 public Nokta(int yeniX, int yeniY) 7 { 8x = yeniX; 9 10} y = yeniY; 49 50 Bölüm 5 Nokta sınıfının son halini ve bu sınıftan birkaç tane nesne oluşturan ana programı Şekil 5.9’daki gibi yazabiliriz: Örnek Uygulama 5.9: 1 public class Nokta 2 { 3private int x; 4private int y; 5public Nokta() 6{ 7 x = 0; 8 y = 0; 9} 10 public Nokta(int x, int y) 11 { 12 this.x = x; 13 this.y = y; 14 } 15 public void YerDegistir(int yeniX, int yeniY) 16 { 17 x = yeniX; 18 y = yeniY; 19 } 20 public double UzaklikHesapla(Nokta ikinciNokta) 21 { 22 23 } return UzaklikHesapla(ikinciNokta.x, ikinciNokta.y); 24 public double UzaklikHesapla(int ikinciNoktaX, int ikinciNoktaY) 25 { 26 double uzaklik = Math.sqrt(Math.pow(x ikinciNoktaX, 2) + Math.pow(y - ikinciNoktaY, 2)); 27 return uzaklik; 28 } 29} 30 public class AnaProgram 31{ 32 public static void main(String[] args) 33 { // Parametresiz yapıcı metodla bir Nokta nesnesi oluşturuldu 34 Nokta nokta1 = new Nokta(); 35 nokta1.YerDegistir(3, 5); Yapısal Programlama // Parametreli yapıcı metodla bir Nokta nesnesi oluşturuldu 36 Nokta nokta2 = new Nokta(5, 5); // nokta1 ve nokta2 nesneleri arasındaki uzaklık nokta1 nesnesi // üzerinden hesaplandı 37 38 basıldı double uzaklik = nokta1.UzaklikHesapla(nokta2); System.out.println(uzaklik); // Ekrana 2.0 değeri // nokta1 ve nokta2 nesneleri arasındaki uzaklık nokta2 nesnesi // üzerinden hesaplandı 39 40 basıldı uzaklik = nokta2.UzaklikHesapla(nokta1); System.out.println(uzaklik); // Ekrana 2.0 değeri aldı // nokta1 nesnesinin yeni koordinatları (4, 6) değerini 41 nokta1.YerDegistir(4, 6); // nokta1 nesnesinin (5, 7) koordinatlarında bulunan noktaya uzaklığı // hesaplandı 42 43 basıldı 44 uzaklik = nokta1.UzaklikHesapla(5, 7); System.out.println(uzaklik); // Ekrana 1.414 değeri } 45} Nesne Yönelimli Programlamanın Temel Kavramları Kapsülleme (Encapsulation) Bir nesnenin içinde yer alan niteliklerin ve iş mantığının nesneyi kullanan kişiler tarafından görülmesinin veya değiştirilmesinin engellenmesine kapsülleme denir. Kapsüllemede temel amaç nesneyi kullanacak kişilerin iş akışıyla ilgili gereksiz detaylarla uğraşmamasını sağlamak ve nesnelerin içinde bulunan hassas verilerin değiştirilmesini engellemektir. Günlük hayatta düzenli olarak kullandığımız bir mönitör nesnesi üzerinden örnek verelim: Monitörümüzün içinde birçok elektronik devre elemanı olduğunu bilsek de bu devrelerin nasıl çalıştığı hakkında çoğumuzun bir fikri bulunmamaktadır. Monitörün içinde bulunan elektronik elemanlar ve bunların çalışma mantıkları son kullanıcıdan soyutlanmış durumdadır. Monitörün dışında yer alan kılıfın bulunmadığını ve monitör üzerinde herhangi bir işlem yapmak için çeşitli devre elemanlarının değerlerini değiştirmek zorunda kaldığımızı düşünecek olursak bahsettiğimiz gereksiz karmaşıklığın ne anlama geldiğini daha iyi anlayabiliriz. Bahsedilen durumda monitör gibi basit bir nesneyi kullanmak için yoğun çaba harcamamız gerekeceği gibi yanlış bir hareketle içerideki yapılara zarar vermemiz de ihtimal dahilinde olacaktır. Monitörün kullanıcıyla etkileşim kurmak için düğmelerden oluşan bir iletişim arayüzüne sahip olması gibi, yazılımsal olarak gerçeklediğimiz nesnelerimizin de kendilerini kullanacak yazılımcılarla aralarında bir iletişim arayüzü bulundurmaları kullanımı kolaylaştıracaktır. Sınıflarımızın içinde bulunan niteliklerin ve metodların sadece ihtiyaç duyulduğu kadarının kullanıcıya açılması kapsülleme mantığı çerçevesinde gerçeklenebilir. Daha önce anlatıldığı gibi nitelikler ve metodların erişimi aşağıdaki gibi kısıtlanabilir: 51 52 Bölüm 5 Şekil 5.1: Erişim tipi Anlamı public Sınıfın içinden, dışından ve türetilmiş sınıflardan. protected Sınıfın ve türetilmiş sınıfların içinden. private Sadece sınıfın içinden. Genel olarak sınıf içindeki bütün niteliklerin private veya protected olarak tanımlanması tavsiye edilmektedir. Sınıfı kullanacak olan yazılımcıların sınıf içinde bulunan niteliklere kendilerine sağlanan bağlantı noktaları üzerinden erişmesi get ve set metodları yardımıyla sağlanabilir. Örnek Uygulama 5.10: 1 public class BankaHesabi 2 { 3public int hesapNumarasi; 4public double bakiye; 5public boolean ParaCek(double miktar) 6{ 7 if (miktar <= this.bakiye) 8 { 9 this.bakiye -= miktar; 10 11 } 12 else 13 { 14 15 } 16 } 17 public void ParaYatir(double miktar) 18 { 19 20 } return true; return false; this.bakiye += miktar; 21} 22public class AnaProgram 23{ 24 public static void main(String[] args) 25 { 26 BankaHesabi hesap = new BankaHesabi(); 27 hesap.hesapNumarasi = 12003345; 28 hesap.ParaYatir(1500); 29 hesap.ParaCek(500); 30 31 hesap.bakiye -= 1500; // iş akışının dışına çıkıldı } 32} Kodun açıklaması: BankaHesabi, hesap numarası ve bakiye verilerini tutan basit bir sınıftır. Para çekmek için ParaCek metodunun, para yatirmak içinse ParaYatir metodunun kullanılması öngörülmüştür. BankaHesabi sınıfı içindeki hesapNumarasi ve bakiye niteliklerinin Yapısal Programlama public olmasından dolayı, Main metodunda oluşturulan bir BankaHesabi nesnesinin bakiye değeri ParaCek veya ParaYatir metodları kullanılmadan da değiştirilebilir. Bakiyenin yetersiz olması durumunda bile istenen miktar hesaptan çekilebilmekte, bu nedenle iş akışının doğru olarak gerçekleştirilmesi Main metodunu yazan kişiye bağlı olmaktadır. Sınıfın aşağıdaki şekilde tasarlanması banka hesabıyla ilgili iş akışının tamamen BankaHesabi sınıfı içinde yer almasını sağlamaktadır: Örnek Uygulama 5.11: 1 public class BankaHesabi 2 { 3private int hesapNumarasi; 4private double bakiye; 5public boolean ParaCek(double miktar) 6{ 7 if (miktar <= this.bakiye) 8 { 9 this.bakiye -= miktar; 10 11 } 12 else 13 { 14 15 } 16 } 17 public void ParaYatir(double miktar) 18 { 19 20 } 21 public int getHesapNumarasi() 22 { 23 24 } 25 public void setHesapNumarasi(int hesapNo) 26 { 27 28 } 29 public double getBakiye() 30 { 31 32 } return true; return false; this.bakiye += miktar; return this.hesapNumarasi; this.hesapNumarasi = hesapNo; return this.bakiye; 33} 34public class AnaProgram 35{ 36 public static void main(String[] args) 53 54 Bölüm 5 37 { 38 BankaHesabi hesap = new BankaHesabi(); 39 hesap.setHesapNumarasi(12003345); 40 hesap.ParaYatir(1500); 41 hesap.ParaCek(500); 42 // hesap.bakiye -= 1500; 43 } 44} Kodun açıklaması: BankaHesabi sınıfındaki hesapNumarasi ve bakiye niteliklerinin erişim kısıtları private olarak değiştirilmiştir. Böylece bu alanlara dışarıdan erişim engellenmiştir. hesapNumarasi niteliğinin değeri getHesapNumarasi metodu üzerinden, bakiye niteliğinin değeri getBakiye metodu üzerinden okunabilmektedir. hesapNumarasi niteliğine setHesapNumarasi metodu aracılığıyla yeni bir değer atanabilmesine rağmen bakiye değeri sadece ParaCek ve ParaYatir metodları kullanılarak değiştirilebilmektedir. Böylece bakiye değişikliği işleminin arkasındaki iş mantığı sadece ilgili metodlar yardımıyla belirlenmekte ve sınıfı kullanacak olan yazılımcının bu konu üzerinde çalışmasına gerek kalmamaktadır. Ana programdaki yorum satırı haline getirilmiş olan kod parçası derleme hatası vermektedir. Bu derleme hatasının sebebi bakiye alanının private olarak tanımlanmış olmasıdır. .Net Platformu üzerinde bulunan Property kavramının Java Platformunda doğrudan bir karşılığı bulunmamaktadır. Bunun yerine niteliğin adının başına get veya set sözcüklerinin eklenmesiyle oluşturulan metodların kullanılması gelenekselleşmiştir. Kodun Tekrar Kullanımı Kodun tekrar kullanımı, daha önce yazılmış olan ve belirli bir işlemi gerçekleştiren kod bloklarının aynı projenin farklı bir yerinde veya başka bir projede kullanılmasıdır. Bu işlem yazılımın gerçeklenmesi aşamasındaki kodlama maliyetlerini düşürdüğü gibi sistemde yapılacak değişikliklerin de daha düşük maliyetle gerçekleştirilmesini sağlar. Yapısal programlama teknolojilerinde kodun yeniden kullanımı kavramı bulunsa da, gerçeklenen işlevler fonksiyon bazında olduğundan iki farklı proje arasında kod aktarımı sınırlı kalır, genelde kodun önemli bir kısmının veya hepsinin yeniden yazılması gerekir. Nesne yönelimli programlama teknolojilerinde ise temel amaçlardan biri kodun yeniden kullanımını sağlayarak projelerin zaman ve iş gücü maliyetlerini mümkün olduğu kadar düşürmektir. Nesne yönelimli yaklaşımda kodun tekrar kullanımı sınıf bazında gerçeklenir ve daha önce oluşturulmuş olan sınıflar aynı proje içinde veya farklı projelerde esnek olarak kullanılır. Nesne yönelimli dillerde kodun tekrar kullanımı birkaç farklı şekilde sağlanabilir: 1� Daha önce oluşturulmuş sınıflardan oluşturulan nesneler uygulama içinde kullanılabilir. 2. Daha önce oluşturulmuş sınıflardan oluşturulan nesneler yeni oluşturulan sınıfların içinde bulunabilir ve bir sınıf değişik tiplerde ve sayılarda nesne içerebilir. Bu ilişkiye composition adı verilir ve genel olarak “sahip olma” (has-a) ilişkisi olarak tanımlanır. 3� Daha önce oluşturulmuş sınıflardan yeni sınıfların türetilmesi ile ile uygulamanın genişletilmesi sağlanır. Bu kavram kalıtım adını alır ve genel olarak “olma” (is-a) ilişkisi olarak tanımlanır. Kalıtım Kalıtım; bir sınıfın daha önce oluşturulmuş başka bir sınıftan türetilmesidir. Kalıtım sayesinde önceden gerçeklenmiş özelliklerin ve davranışların yeniden gerçeklenmesine gerek kalmaz, kodun tekrar kullanımı sağlanmış olur. İki farklı iş kavramının modellenmesi sırasında aralarında kalıtım ilişkisi olduğuna karar vermek için türetilecek olan sınıfın, temel sınıfın sahip olduğu bütün özelliklere sahip olduğundan emin olmak gerekir. En basit yaklaşımla, iki farklı kavram arasında olma (is-a) ilişkisi kurulabiliyorsa iki sınıf arasında kalıtım ilişkisi vardır. Kalıtım ilişkisi uygulamanın Yapısal Programlama ihtiyaçlarına göre iki veya daha fazla sınıf arasında olmak üzere tasarlanabilir. .Net Platformunda olduğu gibi Java Platformunda da çoklu kalıtım desteklenmez, bir sınıf sadece bir sınıftan türetilebilir. Örnek: Bir şirketteki çalışanları modellemek için bir yazılım tasarladığımızı varsayalım. İlk aşamada bütün çalışanlarımızı Calisan adlı bir sınıf ile temsil edelim. Bu durumda bütün çalışan nesnelerimiz aynı sınıftan oluşturulacaği için farklı çalışan tiplerine özel bilgilerin ve davranışların Calisan sınıfından oluşturulan nesnelerde saklanması mümkün olmaz. Örnek Uygulama 5.12: Çalışan sınıfının ilk hali 1 public class Calisan 2 { 3private String ad; 4private String soyad; 5public Calisan(String ad, String soyad) 6{ 7 this.ad = ad; 8 this.soyad = soyad; 9} 10} Tasarladığımız uygulamayı biraz daha geliştirerek farklı çalışan tiplerini temsil etmek üzere özelleşmiş çalışan sınıfları oluşturabiliriz. private erişim tipine sahip alanların erişim tipini protected olarak değiştirirsek bu niteliklere türetilmiş sınıflardan ulaşılmasını da sağlarız. Örnek Uygulama 5.13: 1 public class Calisan 2 { 3protected String ad; 4protected String soyad; 5 6public Calisan(String ad, String soyad) 7{ 8 this.ad = ad; 9 this.soyad = soyad; 10 } 11} 12public class Muhendis extends Calisan 13{ 14 protected String[] projeler; 15 protected String departman; 16 protected int projeSayisi; 17 public Muhendis(String ad, String soyad, String departman) 18 { 19 super(ad, soyad); 20 this.departman = departman; 55 56 Bölüm 5 21 this.projeler = new String[5]; 22 this.projeSayisi = 0; 23 } 24 public void ProjeEkle(String proje) 25 { 26 projeler[projeSayisi] = proje; 27 projeSayisi++; 28 } 29} Calisan sınıfında sadece niteliklerin erişim tipini protected olarak değiştirdik ve yeni eklenen Muhendis sınıfının Calisan sınıfından türemesini “extends” anahtar kelimesi ile sağladık. “extends” anahtar kelimesi, C# dilindeki “:” operatörünün karşılığıdır. Muhendis sınıfının içinde bulunan projeler adındaki string dizisi ile belirli bir mühendisin üzerinde çalıştığı projeleri tutuyoruz ve ProjeEkle metodu yardımıyla belirli bir mühendisin projelerine ekleme yapabiliyoruz. Muhendis sınıfımızın yapıcı metodu ad, soyad ve departman olmak üzere üç tane parametre alır. Departman parametresi Muhendis sınıfı içindeki departman niteliğine ilk değer vermek için kullanılırken ad ve soyad parametreleri “super” anahtar kelimesi yardımıyla temel sınıf olan Calisan sınıfının yapıcı metodunu çalıştırmak için kullanılır. Muhendis sınıfında super(ad, soyad) komutuyla Calisan sınıfının yapıcı metodunu çağırmazsak derleme hatası alırız. Bunun sebebi türetilmiş sınıflardan nesne oluşturulurken sırasıyla temel ve türetilmiş sınıfların yapıcı metodlarının çağrılmasıdır. Örnek Uygulama 5.14, temel ve türetilmiş sınıflarda yapıcı metodların çalışmasını göstermektedir: Örnek Uygulama 5.14: 1 public class TemelSinif 2 { 3public TemelSinif() 4{ 5 System.out.println(“Temel sınıfın yapıcı metodu çalıştı”); 6} 7 } 8 public class TuretilmisSinif extends TemelSinif 9 { 10 public TuretilmisSinif() 11 { 12 çalıştı”); 13 System.out.println(“Türetilmiş sınıfın yapıcı metodu } 14} 15public class AnaProgram 16{ 17 public static void main(String[] args) 18 { Yapısal Programlama 19 TuretilmisSinif turetilmisNesne = new TuretilmisSinif(); 20 } 21} Program çalıştırıldığında ekranda çıkan mesajlar şu şekildedir: Temel sınıfın yapıcı metodu çalıştı Türetilmiş sınıfın yapıcı metodu çalıştı Çalışan modelimizi biraz daha genişleterek mühendisleri branşlarına göre farklı sınıflara ayırırsak ve bir Muhasebeci sınıfı eklersek sınıfların son hali aşağıdaki gibi olur. Örnek Uygulama 5.15: 1 public class Calisan 2 { 3protected String ad; 4protected String soyad; 5public Calisan(String ad, String soyad) 6{ 7 this.ad = ad; 8 this.soyad = soyad; 9} 10} 11public class Muhendis extends Calisan 12{ 13 protected String[] projeler; 14 protected String departman; 15 protected int projeSayisi; 16 public Muhendis(String ad, String soyad, String departman) 17 { 18 super(ad, soyad); 19 this.departman = departman; 20 this.projeler = new String[5]; 21 this.projeSayisi = 0; 22 } 23 public void ProjeEkle(String proje) 24 { 25 projeler[projeSayisi] = proje; 26 projeSayisi++; 27 } 28} 29public class BilgisayarMuhendisi extends Muhendis 30{ 31 protected String[] programlamaDilleri; 57 58 Bölüm 5 32 protected int programlamaDiliSayisi; 33 public BilgisayarMuhendisi(String ad, String soyad, String departman) 34 { 35 super(ad, soyad, departman); 36 this.programlamaDilleri = new String[5]; 37 this.programlamaDiliSayisi = 0; 38 } 39 public void ProgramlamaDiliEkle(String programlamaDili) 40 { 41 programlamaDilleri[programlamaDiliSayisi] = programlamaDili; 42 programlamaDiliSayisi++; 43 } 44} 45public class MakinaMuhendisi extends Muhendis 46{ 47 private String[] modellemeProgramlari; 48 private int modellemeProgramiSayisi; 49 public MakinaMuhendisi(String ad, String soyad, String departman) 50 { 51 super(ad, soyad, departman); 52 this.modellemeProgramlari = new String[5]; 53 this.modellemeProgramiSayisi = 0; 54 } 55 public void ModellemeProgramiEkle(String modellemeProgrami) 56 { 57 modellemeProgramlari[modellemeProgramiSayisi] = modellemeProgrami; 58 59 } modellemeProgramiSayisi++; 60} 61public class Muhasebeci extends Calisan 62{ 63 private String[] muhasebeProgramlari; 64 private int muhasebeProgramiSayisi; 65 public Muhasebeci(String ad, String soyad) 66 { 67 super(ad, soyad); 68 this.muhasebeProgramlari = new String[5]; 69 this.muhasebeProgramiSayisi = 0; Yapısal Programlama 70 } 71 public void MuhasebeProgramiEkle(String muhasebeProgrami) 72 { 73 muhasebeProgramlari[muhasebeProgramiSayisi] = muhasebeProgrami; 74 muhasebeProgramiSayisi++; 75 } 76 public void MuhasebeProgramiEkle(String muhasebeProgrami) 77 { 78 if (!muhasebeProgramlari.contains(muhasebeProgrami)) 79 80 } muhasebeProgramlari.add(muhasebeProgrami); 81} Çok Şekillilik (Polymorphism) Çok şekillilik, bir nesnenin çalışma aşamasında hangi tipten olduğunu bilmesi ve kendi tipine göre davranış sergilemesidir. Oluşturduğumuz iş modeli çerçevesinde bir müdür ve bu müdüre bağlı çalışanlar bulunduğunu ve bütün çalışanların rapor verme kabiliyetine sahip olduğunu varsayalım. Müdür aynı anda bütün çalışanlardan kendisine haftalık rapor vermelerini istediğinde aşağıdaki iki yöntemle istekte bulunabilir: 1� Yalçın Bey Bilgisayar Mühendisliği ile ilgili rapor göndersin, Erkut Bey muhasebecilikle ilgili rapor göndersin. 2. Bütün çalışanlar rapor göndersin. 3� Rapor isteme-gönderme mekanizmasının işlevsel olması için ikinci senaryonun kullanılmasının daha mantıklı olduğunu söyleyebiliriz. Şirket müdürü bütün çalışanlarından rapor isterken tek tek yaptıkları işe göre rapor isteyecek olursa bunun gereksiz zaman kaybına yol açacağı rahatlıkla gözlenebiliyor. Çok şekillilik, ikinci senaryomuzu gerçekleyen yapıdır. Gerçek hayattaki mühendislerin ve muhasebecilerin hangi işi yaptıklarını bilmeleri gibi, nesne yönelimli yazılımda da nesneler çalışma aşamasında hangi işi nasıl yaptıklarını bilirler ve ona göre davranırlar. Bu durumda rapor verme davranışı farklı nesne tiplerine göre farklı şekillerde çalışabildiğinden çok şekilli bir davranıştır. .Net ortamında metodların çok şekilliliği desteklemesi için “virtual” olarak tanımlanması gerekir, Java ortamında ise metodlar varsayılan olarak çok şekilliliği destekler. Çok şekillilik yapısı sadece kalıtım ilişkisi içinde olan sınıflar arasında kullanılabilir. Örneğimizin çok şekilliliği destekleyen hali Örnek Uygulama 5.16’daki gibi olacaktır: Örnek Uygulama 5.16: 1 public class Calisan 2 { 3protected String ad; 4protected String soyad; 5public Calisan(String ad, String soyad) 6{ 7 this.ad = ad; 8 this.soyad = soyad; 9} 59 60 Bölüm 5 10 public void RaporVer() 11 { 12 soyad); 13 System.out.println(“Ad: “ + ad + “ Soyad: “ + } 14} 15public class Muhendis extends Calisan 16{ 17 protected String[] projeler; 18 protected String departman; 19 protected int projeSayisi; 20 public Muhendis(String ad, String soyad, String departman) 21 { 22 super(ad, soyad); 23 this.departman = departman; 24 this.projeler = new String[5]; 25 this.projeSayisi = 0; 26 } 27 public void ProjeEkle(String proje) 28 { 29 projeler[projeSayisi] = proje; 30 projeSayisi++; 31 } 32 public void RaporVer() 33 { 34 super.RaporVer(); 35 System.out.println(“Departman: “ + departman); 36 System.out.println(“Projeler: “); 37 for (int i = 0; i < projeSayisi; i++) 38 { 39 40 } 41 } System.out.println(“\t” + projeler[i]); 42} 43public class BilgisayarMuhendisi extends Muhendis 44{ 45 protected String[] programlamaDilleri; 46 protected int programlamaDiliSayisi; 47 public BilgisayarMuhendisi(String ad, String soyad, String departman) 48 { 49 super(ad, soyad, departman); 50 this.programlamaDilleri = new String[5]; Yapısal Programlama 51 this.programlamaDiliSayisi = 0; 52 } 53 public void ProgramlamaDiliEkle(String programlamaDili) 54 { 55 programlamaDilleri[programlamaDiliSayisi] = programlamaDili; 56 programlamaDiliSayisi++; 57 } 58 public void RaporVer() 59 { 60 super.RaporVer(); 61 System.out.println(“Görev: Bilgisayar Mühendisi”); 62 System.out.println(“Programlama Dilleri: “); 63 for (int i = 0; i < this.programlamaDiliSayisi; i++) 64 { 65 System.out.println(“\t” + programlamaDilleri[i] + “ “); 66 67 } } 68} 69public class MakinaMuhendisi extends Muhendis 70{ 71 private String[] modellemeProgramlari; 72 private int modellemeProgramiSayisi; 73 public MakinaMuhendisi(String ad, String soyad, String departman) 74 { 75 super(ad, soyad, departman); 76 this.modellemeProgramlari = new String[5]; 77 this.modellemeProgramiSayisi = 0; 78 } 79 public void ModellemeProgramiEkle(String modellemeProgrami) 80 { 81 modellemeProgramlari[modellemeProgramiSayisi] = modellemeProgrami; 82 83 } 84 public void RaporVer() 85 { 86 super.RaporVer(); 87 System.out.println(“Görev: Makina Mühendisi”); 88 System.out.println(“Modelleme Programları: “); 89 for (int i = 0; i < modellemeProgramiSayisi; i++) modellemeProgramiSayisi++; 61 62 Bölüm 5 90 { 91 System.out.println(“\t” + modellemeProgramlari[i] + “ “); 92 93 } } 94} 95public class Muhasebeci extends Calisan 96{ 97 private String[] muhasebeProgramlari; 98 private int muhasebeProgramiSayisi; 99 public Muhasebeci(String ad, String soyad) 100 { 101 super(ad, soyad); 102 this.muhasebeProgramlari = new String[5]; 103 this.muhasebeProgramiSayisi = 0; 104 } 105 public void MuhasebeProgramiEkle(String muhasebeProgrami) { 106 muhasebeProgramlari[muhasebeProgramiSayisi] = muhasebeProgrami; 107 108 } muhasebeProgramiSayisi++; 109 public void RaporVer() 110 { 111 super.RaporVer(); 112 System.out.println(“Görev: Muhasebeci”); 113 System.out.println(“Muhasebe Programları: “); 114 for (int i = 0; i < muhasebeProgramiSayisi; i++) 115 { 116 System.out.println(“\t” + muhasebeProgramlari[i]); 117 118 } } 119} 120public class Mudur 121{ 122 private Calisan[] calisanlar; 123 private int calisanSayisi; 124 public Mudur() 125 { 126 this.calisanlar = new Calisan[5]; 127 this.calisanSayisi = 0; 128 } Yapısal Programlama 129 public void CalisanEkle(Calisan calisan) 130 { 131 calisanlar[calisanSayisi] = calisan; 132 calisanSayisi++; 133 } 134 public void RaporIste() 135 { 136 for (int i = 0; i < calisanSayisi; i++) 137 { 138 calisanlar[i].RaporVer(); 139 System.out.println(); 140 } 141 } 142} 143public class AnaProgram 144{ 145 public static void main(String[] args) 146 { 147 BilgisayarMuhendisi blgMuh = new BilgisayarMuhendisi (“Yalçın”, “Kaya”, “IT”); 148 blgMuh.ProgramlamaDiliEkle(“Java”); 149 blgMuh.ProgramlamaDiliEkle(“C#”); 150 MakinaMuhendisi mknMuh = new MakinaMuhendisi(“Hasan”, “Polat”, “Bakım”); 151 mknMuh.ModellemeProgramiEkle(“Katia”); 152 “Kutlar”); Muhasebeci muhasebeci = new Muhasebeci(“Erkut”, 153 Mudur genelMudur = new Mudur(); 154 genelMudur.CalisanEkle(blgMuh); 155 genelMudur.CalisanEkle(mknMuh); 156 genelMudur.CalisanEkle(muhasebeci); 157 genelMudur.RaporIste(); 158 } 159} Program çalıştırıldığında aşağıdaki gibi bir çıktı üretir: Ad: Yalçın Soyad: Kaya Departman: IT Projeler: Görev: Bilgisayar Mühendisi Programlama Dilleri: Java C# 63 64 Bölüm 5 Ad: Hasan Soyad: Polat Departman: Bakım Projeler: Görev: Makina Mühendisi Modelleme Programları: Katia Ad: Erkut Soyad: Kutlar Görev: Muhasebeci Muhasebe Programları: Mudur sınıfının içinde, çalışanları tutmak için Calisan sınıfı cinsinden bir dizi kullandık. Ana programda oluşturduğumuz üç tane farklı tipte çalışanı (bilgisayar mühendisi, makina mühendisi, muhasebeci) müdür nesnemizin çalışanlarına ekledik. Mudur sınıfının RaporIste metodu butun çalışanlardan rapor istemek amacıyla yazıldı. Müdür nesnesinin RaporIste metodunu çalıştırdığımızda bütün çalışan nesnelerinin kendi tiplerine ait raporları oluşturduğunu görüyoruz. Raporların doğru bir şekilde oluşması RaporVer metodunun çok şekilli olduğunu gösterir. Müdür sınıfında bütün çalışan bilgilerinin Calisan sınıfından oluşan bir dizide tutulmasından dolayı derleme aşamasında çalışanların tipi belli değildir. Eğer RaporVer metodu çok şekilli olmasaydı, müdür nesnesinin RaporIste metodu çağrıldığında çalışanların hepsi derleme aşamasında olduğu gibi çalışma aşamasında da temel Calisan sınıfının raporunu oluşturacaktı. 6 Swing 6 Swing • Swing’e Giriş • Klavye Event’lerinin Yönetilmesi Swing Java platformunun 1.0 versiyonunda görsel kullanıcı arayüzleri oluşturmak için AWT (Abstract Windowing Toolkit) teknolojisi kullanılıyordu. AWT’nin temel amacı Java programlarının arayüzlerini işletim sisteminden bağımsız hale getirmek olsa da bu amaca ulaşılamadı. AWT paketinin içinde bulunan bileşenler işletim sistemine bağımlı olduğundan farklı işletim sistemleri üzerinde faklı görünümlere sahip olan programlar ortaya çıktı. Java platformunun 2.0 versiyonuyla birlikte AWT paketinin üzerine Swing adı verilen yeni görsel programlama modeli de eklendi. Swing paketi Java ile yazılmış olmasının da etkisiyle İşletim sisteminden bağımsız bir görsel programlama modeli sunar. Yeni geliştirilen Java uygulamalarında Swing paketinin kullanılması uygulamanın görsel arayüzünün güçlü olması için şarttır. Bu bölümde Java platformunda bulunan Swing paketinin genel özellikleri ve bu paketin içinde bulunan temel arayüz kontrolleri anlatılacaktır. Swing bu bölümde anlatılanlarla sınırlı değildir, özellikle karmaşık kullanıcı etkileşimi gerektiren Java uygulamalarının geliştirebilmesi için Swing teknolojisinin detaylı olarak incelenmesi gerekir. Swing’e Giriş .Net platformunda Windows uygulamaları geliştirirken kullandığımız kontrollerin benzerleri Java platformunda da vardır. Swing kontrolleri çeşitli olayları (event) tetikleyerek uygulama arayüzüyle kullanıcının etkileşim halinde olmasına olanak tanır. Uygulamada kullanılan kontroller çeşitli container’lar (saklayıcı) yardimiyla gruplanabilir. Java platformuyla işletim sistemi üzerinde çalışan uygulamalar oluşturmak için uygulamanın görsel arayüzünü temsil edecek sınıfın JFrame sınıfından türetilmesi gerekir. JFrame sınıfının içinde bulunan ve yeni oluşturulan sınıflara kalıtımla aktarılan getContentPane metodu kullanılarak uygulamada bulunan kontrolleri içeren Container nesnesine erişilir. Menü kontrolleri dışındaki bütün kontroller Container nesnesi içinde bulunur ve Container nesnesi hiçbir zaman null (boş) değer almaz. Container nesnesinin setLayout metodu uygulama arayüzünde kullanılacak yerleşim düzenini(layout) belirlemek için kullanılır. Layout seçenekleri ilerleyen sayfalarda anlatılacaktır. Boş bir arayüz oluşturan Java kodu Örnek Uygulama 6.1’de verilmiştir: Örnek Uygulama 6.1: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import javax.swing.JFrame; 4 public class IlkOrnek extends JFrame 5 { 6public IlkOrnek() 7{ 8 super(“İlk Örnek”); 9 Container container = getContentPane(); 10 container.setLayout(new FlowLayout()); 11 setSize(200, 200); 12 setVisible(true); 13} 14public static void main(String[] args) 15{ 16 IlkOrnek test = new IlkOrnek(); 17 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 68 Bölüm 6 18} 19} Kodun açıklaması: IlkOrnek isimli sınıfımız JFrame sınıfından türetilmiştir. Yapıcı metod içinde JFrame sınıfının yapıcı metodu super(“İlk Örnek”) kod bloğuyla çalıştırılır, oluşan ekranın başlığında “İlk Örnek” metni bulunur. Container nesnesi getContentPane metodu ile yukarıda açıklandığı gibi alınır. Forma başka kontroller de ekleyecek olursak bu kontrolleri Container nesnesinin içine veya Container nesnesinin içinde bulunan bir nesneye eklememiz gerekir. Uygulamanın çalışması sırasında formun görünür hale gelmesi için boyutunu vermemiz ve setVisible metodunu kullanarak formu görünür hale getirmemiz gereklidir. Main metodunun içinde, yazdığımız sınıftan bir nesne oluşturarak formun oluşmasını sağlarız. JFrame sınıfından türetilmiş olan arayüzün kullanıcı tarafından kapatılması durumunda uygulamanın sonlandırılmasının sağlanması için test.setDefaultCloseOperation(JFrame. EXIT_ON_CLOSE); kod bloğunu Main metoduna ekleriz. Bu satırı eklemezsek, uygulama kapatıldığında varsayılan işlem olarak form görünmez hale gelindiği halde uygulama kapanmaz. Swing ile ilgili bütün örneklerde yukarıda verilen kodu kullanacağız. Uygulama çalıştırıldığında oluşan arayüz Şekil 1’deki gibidir. Şekil 1 JButton .Net platformundaki Button kontrolünün karşılığı Java platformundaki JButton kontrolüdür. Temel olarak üzerinde bir metin yazılı olabilen ve kullanıcı tarafından üzerine tıklandığında event oluşturan bir yapıya sahiptir. Form üzerinde bir buton oluşturmak için JButton sınıfından bir nesne yaratılır ve Container nesnesine eklenir. JButton sınıfının yapıcı metodu butonun üzerinde görüntülenecek metni parametre olarak alır. JButton nesnesi oluşturan ve formun üzerine ekleyen kod Örnek Uygulama 6.2’de gösterilmiştir. Örnek Uygulama 6.2: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import javax.swing.JButton; 4import javax.swing.JFrame; 5 public class JButtonTest extends JFrame 6 { 7private JButton btnTest; 8public JButtonTest() 9{ 10 super(“JButton Örneği”); 11 Container container = getContentPane(); 12 container.setLayout(new FlowLayout()); 13 btnTest = new JButton(“Örnek Buton”); 14 container.add(btnTest); 15 setSize(200, 200); 16 setVisible(true); 17 } Swing 18 public static void main(String[] args) 19 { 20 JButtonTest test = new JButtonTest(); 21 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 22 } 23} Kodun açıklaması: Formu oluşturan kod bloklarından farklı olarak bir adet JButton nesnesi oluşturan ve Container nesnesine ekleyen kod satırları yukarıdadır. JButton sınıfının yapıcı metoduna parametre olarak butonun üzerinde bulunmasını istediğimiz metni veririz. Container nesnesinin add metodu herhangi bir kontrolün Container içine eklenmesini sağlar. Uygulama çalıştırıldığında Şekil 2’deki gibi bir arayüz oluşur. Oluşturduğumuz butona henüz bir event eklemediğimiz için butonun herhangi bir işlevi yoktur. JLabel JLabel kontrolü, .Net ortamındaki Label kontrolünün karşılığıdır. Uygulama üzerinde metin görüntülemek istediğimiz noktalarda kullanılır. Bir JLabel kontrolünün uygulama üzerinde görünür hale gelmesi için bir JLabel nesnesi oluşturup Container nesnesine eklememiz yeterlidir. Örnek kod Örnek Uygulama 6.3’de verilmiştir. Şekil 2 Örnek Uygulama 6.3: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import javax.swing.JFrame; 4 import javax.swing.JLabel; 5 public class JLabelTest extends JFrame 6 { 7private JLabel lblTest; 8public JLabelTest() 9{ 10 11 12 super(“JLabel Kullanımı”); Container container = getContentPane(); container.setLayout(new FlowLayout()); 13 lblTest = new JLabel(“JLabel Örneği”); 14 container.add(lblTest); 15 setSize(200, 200); 16 setVisible(true); 17 } 18 public static void main(String[] args) 19 20 21 22 23} { } JLabelTest test = new JLabelTest(); test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 69 70 Bölüm 6 Kodun açıklaması: lblTest adındaki JLabel nesnesin yapıcı metoduna parametre olarak üzerinde görüntülenecek metin verilmiştir. Container nesnesinin add metodu kullanılarak JLabel nesnesinin form üzerinde görüntülenmesi sağlanmıştır. Label üzerindeki metni uygulamanın çalışma aşamasında değiştirmek için setText metodu, label üzerindeki metni almak için getText metodu kullanılır. Bu iki metodun kullanımıyla ilgili örneği daha sonra vereceğiz. Uygulama çalıştırıldığı zaman Şekil 3’de olduğu gibi görünür. JTextField JTextField kontrolü, .Net ortamındaki TextBox kontrolülün Java ortamındaki karşılığıdır. Kullanıcı tarafından girilecek metin türünden bilgileri uygulamaya aktarmak amacıyla kullanılır. Bir JTextField kontrolünün uygulama üzerinde görünür hale gelmesi için bir JTextField nesnesi oluşturup Container nesnesine eklememiz yeterlidir. Örnek kod, Örnek Uygulama 6.4’te verilmiştir. Şekil 3 Örnek Uygulama 6.4: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import javax.swing.JFrame; 4 import javax.swing.JLabel; 5 import javax.swing.JTextField; 6 public class JTextFieldTest extends JFrame 7 { 8private JTextField txtTest; 9public JTextFieldTest() 10 { 11 super(“JTextField Kullanımı”); 12 Container container = getContentPane(); 13 container.setLayout(new FlowLayout()); 14 txtTest = new JTextField(“JTextField Örneği”); 15 container.add(txtTest); 16 setSize(200, 200); 17 setVisible(true); 18 } 19 public static void main(String[] args) 20 { 21 JTextFieldTest test = new JTextFieldTest(); 22 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 23 } 24} Kodun açıklaması: txtTest adındaki JTextField nesnesin yapıcı metoduna parametre olarak üzerinde görüntülenecek metin verilmiştir. Container nesnesinin add metodu kullanılarak JTextField nesnesinin form üzerinde görüntülenmesi sağlanmıştır. JTextField üzerindeki metni uygulamanın çalışma aşamasında değiştirmek için setText metodu, label üzerindeki Swing metni almak için getText metodu kullanılır. Bu iki metodun kullanımıyla ilgili örneği daha sonra vereceğiz. Uygulama çalıştırıldığı zaman Şekil 4’teki gibi görünür. Şekil 4 Event Handling Mekanizması Kullanıcıların Swing kontrolleri aracılığıyla uygulamayla iletişim kurması sonucunda çeşitli event’ler (olaylar) oluşur. Bu event’leri kontrol etmek (handle) için .Net ortamından farklı olarak event sınıfları kullanmamız gerekir. Java ortamında herşeyi sınıflar halinde kullanmak gibi bir amaç olduğundan event handling mekanizması .Net ortamındakinden farklıdır. Net uygulamalarında herhangi bir event için bir tane metod yazmak yeterli olurken Java’da bu işlem için harici veya dahili (inner class) bir sınıf yazarak çeşitli event tipleri için özelleşmiş event interface’lerini implemente etmemiz gerekir. Herhangi bir event birden fazla Swing kontrolü tarafından kullanılabilir, boyle bir kullanımda event’in hangi kontrol üzerinden oluştuğunu öğrenmek için event parametreleri kullanılır. JLabel, JTextField ve JButton kontrolleriyle birlikte event mekanizmasini örnek kodu, Örnek Uygulama 6.5 üzerinden inceleyelim: Örnek Uygulama 6.5: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import javax.swing.JButton; 6 import javax.swing.JFrame; 7 import javax.swing.JLabel; 8 import javax.swing.JTextField; 9 public class EventMekanizmasi extends JFrame 10{ 11 private JLabel lblGoruntule; 12 private JTextField txtGiris; 13 private JButton btnGoruntule; 14 public EventMekanizmasi() 15 { 16 super(“Event Mekanizması”); 17 Container container = getContentPane(); 18 container.setLayout(new FlowLayout()); 19 txtGiris = new JTextField(“JTextField”); 71 72 Bölüm 6 20 container.add(txtGiris); 21 btnGoruntule = new JButton(“Görüntüle”); 22 container.add(btnGoruntule); 23 btnGoruntule.addActionListener(new ButtonHandler()); 24 lblGoruntule = new JLabel(“JLabel”); 25 container.add(lblGoruntule); 26 setSize(400, 200); 27 setVisible(true); 28 } 29 private class ButtonHandler implements ActionListener 30 { 31 public void actionPerformed(ActionEvent e) 32 { 33 34 } 35 } 36 public static void main(String[] args) 37 { 38 EventMekanizmasi test = new EventMekanizmasi(); 39 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 40 } lblGoruntule.setText(txtGiris.getText()); 41} Kodun açıklaması: EventMekanizmasi sınıfının yapıcı metodunda daha önce yaptığımız gibi JLabel, JTextField ve JButton nesnelerini oluşturduk ve Container nesnesine ekledik. EventMekanizmasi sınıfının içindeki private erişim tipine sahip ButtonHandler sınıfının ActionListener interface’ini implemente etmesi, bu sınıfın içinde actionPerformed metodunun yazılmasını zorunlu kılar. Bu metod, Action cinsinden bir event oluştuğunda çalışır. JButton nesnesi üzerinde çalıştırdığımız addActionListener metodunu buton ile event’i ilişkilendirmek için kullandık, bu durumda buton kullanıcı tarafından tıklandığında ButtonHandler sınıfındaki actionPerformed metodu çalışacaktır. actionPerformed metodunun içinde txtGiris nesnesinin getText metodunu kullanarak kullanıcı tarafından girilen metni alıp lblGoruntule nesnesinin setText metodu ile JLabel kontrolünün metnine atıyoruz. Aynı işlevi, event handler sınıfını dahili olarak tanımlayarak da gerçekleyebiliriz. Event handler sınıfının dahili olarak gerçeklenmesini gösteren kod bloğu Örnek Uygulama 6.6’daki gibidir. Örnek Uygulama 6.6: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import javax.swing.JButton; 6 import javax.swing.JFrame; 7 import javax.swing.JLabel; 8 import javax.swing.JTextField; 9 public class EventMekanizmasi extends JFrame Swing 10{ 11 private JLabel lblGoruntule; 12 private JTextField txtGiris; 13 private JButton btnGoruntule; 14 public EventMekanizmasi() 15 { 16 super(“Event Mekanizması”); 17 Container container = getContentPane(); 18 container.setLayout(new FlowLayout()); 19 txtGiris = new JTextField(“JTextField”); 20 container.add(txtGiris); 21 btnGoruntule = new JButton(“Görüntüle”); 22 container.add(btnGoruntule); 23 btnGoruntule.addActionListener 24 ( 25 new ActionListener() 26 { 27 event) public void actionPerformed(ActionEvent 28 { 29 lblGoruntule.setText(txtGiris.getText()); 30 31 } 32 ); 33 lblGoruntule = new JLabel(“JLabel”); 34 container.add(lblGoruntule); 35 setSize(400, 200); 36 setVisible(true); } 37 } 38 public static void main(String[] args) 39 { 40 EventMekanizmasi test = new EventMekanizmasi(); 41 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 42 } 43} Her iki kod bloğu da aynı şekilde çalışır. Kullanıcının metin kutusuna yazdığı metin butona tıklandıktan sonra label üzerinde görüntülenir. Uygulama çalıştırıldığında butona tıklamadan önce ve sonraki görünümler Şekil 5 ve Şekil 6’daki gibidir. 73 74 Bölüm 6 Şekil 6 Şekil 5 JTextArea JTextArea kontrolü, .Net ortamındaki RichTextBox kontrolünün karşılığıdır. JTextField kontrolünden daha geniş kullanım kabiliyetlerine sahiptir ve birden fazla satırdan oluşan metinlerin girilmesine olanak verir. Append metodu yardımıyla üzerine metin eklemesi yapılabilir. Girilen metnin kontrolün boyutlarından büyük olması durumunda bir alt satıra geçmek için setLineWrap metodu kullanılır. JTextArea kontrolünün örnek kullanımı Örnek Uygulama 6.7’de verilmiştir. Örnek Uygulama 6.7: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import javax.swing.JFrame; 4 import javax.swing.JTextArea; 5 public class JTextAreaTest extends JFrame 6 { 7private JTextArea txtArea; 8public JTextAreaTest() 9{ 10 super(“JTextArea Kullanımı”); 11 Container container = getContentPane(); 12 container.setLayout(new FlowLayout()); 13 txtArea = new JTextArea(“JTextArea fazla satirdan oluşan metin girilebilir.”); alanına birden 14 txtArea.append(“\nYeni metin eklenebilir”); 15 txtArea.setLineWrap(true); 16 txtArea.setSize(150, 150); 17 container.add(txtArea); 18 setSize(200, 200); 19 setVisible(true); 20 } 21 public static void main(String[] args) 22 { 23 JTextAreaTest test = new JTextAreaTest(); 24 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 25 } 26} Swing Kodun açıklaması: JTextArea nesnesini oluşturulup Container nesnesine ekledikten sonra append metodu ile üzerine yeni metin ekledik. Append metodunun içinde kullandığımız “\n” karakteri metnin bir alt satıra geçmesini sağlar. setLineWrap(true) komutuyla metnin kontrolün genişliğinden fazla yer kaplaması durumunda otomatik olarak bir alt satıra geçmesi sağlanmıştır. setSize metodu ile kontrolün x ve y boyutu 150 olarak atanmıştır. Uygulama çalıştırıldığında Şekil 7’deki gibi bir arayüz oluşur. JOptionPane JOptionPane sınıfı, .Net ortamında bulunan MessageBox sınıfının Java karşılığıdır. MessageBox.Show() metoduna benzer bir şekilde çalışan birçok statik metod içerir. Arayüzün çalışma mantığı içinde çeşitli mesajları kullanıcıya göstermek ve onay almak amacıyla kullanılır. Gösterilen onay kutusu uygulamayı oluşturan formun dışında ayrı bir pencere içinde oluşur. Bir JOptionPane onay kutusunu kullanıcının karşısına çıkartan ve kullanıcının onay durumuna göre bir tane JOptionPane mesaj diyaloğu gösteren kod Örnek Uygulama 6.8’de verilmiştir. Şekil 7 Örnek Uygulama 6.8: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import javax.swing.JButton; 6 import javax.swing.JFrame; 7 import javax.swing.JOptionPane; 8 public class JOptionPaneTest extends JFrame 9 { 10 11 private JButton btnOnay; public JOptionPaneTest() 12 13 { Super(“JOptionPane”); 14 Container container = getContentPane(); 15 container.setLayout(new FlowLayout()); 16 btnOnay = new JButton(“Onay al”); 17 btnOnay.addActionListener(new ButtonHandler()); 18 container.add(btnOnay); 19 setSize(200, 200); 20 setVisible(true); 21 } 22 private class ButtonHandler implements ActionListener 23 24 { public void actionPerformed(ActionEvent event) 25 { 26 int secim = JOptionPane. showConfirmDialog(null, “Seçiminizi onaylıyor musunuz?”, “Onay ekranı”, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_ MESSAGE); 75 76 Bölüm 6 27 if(secim == JOptionPane.YES_OPTION) 28 JOptionPane.showMessageDialog(null, “Onaylandı”, “Onay”, JOptionPane.INFORMATION_MESSAGE); 29 else if(secim == JOptionPane.NO_OPTION) 30 JOptionPane.showMessageDialog(null, “Onaylanmadı”, “Red”, JOptionPane.ERROR_MESSAGE); 31 else 32 JOptionPane.showMessageDialog(null, “Onaylanmadı”, “İptal”, JOptionPane.WARNING_MESSAGE); 33 } 34 } 35 public static void main(String[] args) 36 { 37 JOptionPaneTest test = new JOptionPaneTest(); 38 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 39 } 40} Kodun açıklaması: Formun üzerine bir tane buton ekledik ve bu buton için ActionListener interface’ini implemente eden ButtonHandler adında bir event sınıfı yazdık. Kullanıcı tarafından butona tıklanması durumunda actionPerformed metodu çalışır ve kullanıcıya JOptionPane.showConfirmDialog metodu yardımıyla onay verip vermediği sorulur. Bu statik metodun ikinci parametresi onay kutusunda görüntülenmesini istediğimiz metin, üçüncü parametresi ise onay kutusunun başlığını oluşturan metindir. Dördüncü parametrede onay kutusunda hangi butonların görüntülenmesini istediğimizi belirttik. Yaptığımız örnekte Yes, No ve Cancel butonlarının görüntülenmesini istedik. Kullanıcının herhangi birşeyi onaylamasını istediğimiz için soru işareti şeklinde bir resim kullanmak duruma uygun olduğundan beşinci parametrede soru mesajı seçeneğini seçtik. Java’daki birçok metod parametresinde olduğu gibi JOptionPane. showConfirmDialog metodunun parametrelerinde de sınıfın üzerinde bulunan değerleri kullanabildiğimiz gibi istersek bu değerlerin tam sayı karşılıklarını da kullanabiliriz. Metodun geri dönüş değerinin tam sayı(int) olduğuna dikkat edelim. Bu geri dönüş değerini secim isimli bir tam sayı değişkenine atarak bir sonraki aşamada seçime göre bir işlem yapılmasını sağlayabiliriz. Sonraki aşamada, onay durumuna göre değişen bir simgeye, başlık metnine ve mesaj kutusu metnine sahip bir mesaj kutusunu kullanıcıya gösteriyoruz. Uygulama çalıştırıldığında görünen arayüz, butona tıklandığı zaman görüntülenen onay kutusu ve çeşitli seçimlere göre görüntülenen mesaj kutuları Şekil 8, Şekil 9, Şekil 10, Şekil 11 ve Şekil 12’de gösterildiği gibidir. Şekil 8: Programın ilk çalışması. Şekil 9: Onay ekranı. Swing Şekil 10: Yes butonuna tıklandı. Şekil 11: No butonuna tıklandı. Şekil 12: İptal butonuna tıklandı. JCheckBox JCheckBox sınıfı, .Net platformunda bulunan CheckBox sınıfının Java ortamındaki karşılığıdır. Seçili durumda olabilen küçük bir kutudan ve yanında bulunan bir Label’dan oluşan JCheckBox, kullanıcının temel açık/kapalı seçimi yapmasını sağlayan basit bir arayüz kontrolüdür. Belirli bir zamanda nesnenin seçili olup olmadığı kontrol edilebilir, seçim değiştiğinde bir event’in tetiklenmesi sağlanabilir, seçili olma durumu değiştirilebilir. JCheckBox kontrolünün kullanımını gösteren kod bloğu Örnek Uygulama 6.9’da verilmiştir. Örnek Uygulama 6.9: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import java.awt.GridLayout; 4 import java.awt.event.ActionEvent; 5 import java.awt.event.ActionListener; 6 import java.awt.event.ItemEvent; 7 import java.awt.event.ItemListener; 8 import javax.swing.JButton; 9 import javax.swing.JCheckBox; 10 import javax.swing.JFrame; 11 import javax.swing.JLabel; 12 import javax.swing.JOptionPane; 13 import javax.swing.JTextField; 14 public class JCheckBoxTest extends JFrame 15 { 16 private JCheckBox chkKabul; 17 private JLabel lblAd; 18 private JLabel lblSoyad; 19 private JTextField txtAd; 20 private JTextField txtSoyad; 21 private JButton btnKaydet; 22 public JCheckBoxTest() 23 { 24 super(“Kullanıcı kayıt formu”); 25 Container container = getContentPane(); 77 78 Bölüm 6 26 container.setLayout(new FlowLayout()); 27 lblAd = new JLabel(“Ad: “); 28 container.add(lblAd); 29 txtAd = new JTextField(10); 30 container.add(txtAd); 31 lblSoyad = new JLabel(“Soyad: “); 32 container.add(lblSoyad); 33 txtSoyad = new JTextField(10); 34 container.add(txtSoyad); 35 btnKaydet = new JButton(“Kaydet”); 36 btnKaydet.setEnabled(false); 37 btnKaydet.addActionListener 38 ( 39 new ActionListener() 40 { 41 event) public void actionPerformed(ActionEvent 42 { 43 JOptionPane. showMessageDialog(null, “Ad: “ + txtAd.getText() + “\nSoyad: “ + txtSoyad.getText()); 44 45 } 46 ); 47 } container.add(btnKaydet); 48 ediyorum”); chkKabul = new JCheckBox(“Sözleşmeyi kabul 49 chkKabul.addItemListener 50 ( 51 new ItemListener() 52 { 53 event) public void itemStateChanged(ItemEvent 54 { 55 if(chkKabul.isSelected() == true) 56 57 else 58 59 } 60 } 61 ); 62 container.add(chkKabul); 63 setSize(200, 150); 64 setVisible(true); 65 } btnKaydet.setEnabled(true); btnKaydet.setEnabled(false); Swing 66 public static void main(String[] args) 67 { 68 JCheckBoxTest test = new JCheckBoxTest(); 69 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 70 } 71 } Kodun açıklaması: İki tane JLabel, iki tane JTextField, bir tane JButton ve bir tane JCheckBox nesnesi oluşturularak Container nesnesine eklendi. JCheckBox nesnesi üzerinde çalıştırılan addItemListener metodu ile, bu nesnenin durumunun değişmesi halinde tetiklenecek olan event handler’i oluşturuldu. Yazdığımız koda göre, uygulama ilk çalıştırıldığında buton aktif değildir. Kullanıcı ad ve soyad bilgilerini girdikten sonra sözleşmeyi kabul ettiğini belirten JCheckBox’ı işaretlediğinde (checkbox’ı seçili hale getirdiğinde) JCheckBox nesnesinin uzerindeki ItemListener metodu çalışır ve buton aktif hale gelir. Benzer bir şekilde kullanıcı CheckBox üzerindeki seçimi iptal ederse buton aktiflik özelliğini yitirir. Kullanıcı gereken bilgileri girdikten sonra Kaydet butonuna tıkladığında ad ve soyad bilgileri bir mesaj kutusu (JOptionPane) yardımıyla ekranda görüntülenir. Uygulama çalıştırıldığında görünen arayüz Şekil 13, Şekil 14 ve Şekil 15’teki gibidir: Şekil 13: Buton aktif değil. Şekil 14: Buton aktif. Şekil 15: Kaydet butonuna tıklandı. JRadioButton JRadioButton bileşeni, .Net ortamındaki RadioButton kontrolünün Java platformundaki karşılığıdır. Birden fazla seçeneğe sahip bir veri kümesi arasından birinin seçilmesi gereken durumlarda kullanılır. .Net ortamındaki RadioButton kontrolünden farklı olarak arayüz üzerinde aynı anda birden fazla JRadioButton kontrolü seçili durumda bulunabilir. Bu durumu önlemek için, arayüz üzerindeki ilişkili JRadioButton kontrolleri bir tane ButtonGroup nesnesi kullanılarak gruplandırılır. JRadioButton kontrolü üzerindeki seçim durumundaki değişiklik JCheckBox kontrolü gibi ItemListener interface’i ile kontrol edilebilir. İki tane JRadioButton kontrolü ile bir tane ButtonGroup kontrolü içeren ve JRadioButton nesneleri üzerindeki seçim durumunun değişikliğini takip eden kod bloğu Örnek Uygulama 6.10’da gösterilmiştir. Örnek Uygulama 6.10: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 79 80 Bölüm 6 3 import java.awt.event.ItemEvent; 4 import java.awt.event.ItemListener; 5 import javax.swing.ButtonGroup; 6 import javax.swing.JFrame; 7 import javax.swing.JOptionPane; 8 import javax.swing.JRadioButton; 9 import javax.swing.plaf.basic.BasicComboBoxUI.ItemHandler; 10 public class JRadioButtonTest extends JFrame 11 { 12 private JRadioButton rbErkek; 13 private JRadioButton rbKadin; 14 private ButtonGroup grpRadio; 15 public JRadioButtonTest() 16 { 17 super(“JRadioButton örneği”); 18 Container container = getContentPane(); 19 container.setLayout(new FlowLayout()); 20 rbErkek = new JRadioButton(“Erkek”, true); 21 rbErkek.addItemListener(new RadioButtonHandler()); 22 container.add(rbErkek); 23 rbKadin = new JRadioButton(“Kadın”, false); 24 rbKadin.addItemListener(new RadioButtonHandler()); 25 container.add(rbKadin); 26 ButtonGroup // JRadioButton nesnelerini gruplandırmak için kullanılır 27 grpRadio = new ButtonGroup(); 28 grpRadio.add(rbErkek); 29 grpRadio.add(rbKadin); 30 setSize(200, 100); 31 setVisible(true); 32 } 33 private class RadioButtonHandler implements ItemListener 34 { 35 public void itemStateChanged(ItemEvent event) 36 { 37 SELECTED) if(event.getStateChange() == ItemEvent. 38 { 39 JOptionPane.showMessageDialog(null, ((JRadioButton)event.getSource()).getText()); 40 } 41 } 42 } 43 public static void main(String[] args) 44 { Swing 45 JRadioButtonTest test = new JRadioButtonTest(); 46 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 47 } 48 } Kodun açıklaması: JRadioButtonTest sınıfının yapıcı metodunda iki tane JRadioButton, bir tane ButtonGroup nesnesi oluşturduk. JRadioButton nesnelerini oluştururken kullandığımız yapıcı metodun ikinci parametresi mantıksal değer alır ve uygulama ilk çalıştığında JRadioButton nesnesinin seçili durumda olup olmayacağını belirtir. Uygulama ilk çalıştığında Erkek değerine sahip JRadioButton nesnesi seçili durumdadır. İki JRadioButton nesnesi için de aynı event handler sınıfını kullanacağımız için private bir iç sınıf oluşturduk. ItemStateChanged event handler metodu JRadioButton nesnelerinden birinin seçili olma durumu değiştiğinde çalışır. Metodun başında kullandığımız if mekanizmasıyla sadece seçili hale gelen nesnelerle ilgilendiğimizi belirttik ve JRadioButton kontrollerinden biri seçili hale geçtiğinde bu nesnenin text özelliğini mesaj kutusu kullanarak kullanıcıya gösterdik. Eğer ButtonGroup nesnesini kullanmasaydık aynı anda birden fazla JRadioButton kontrolü seçili durumda bulunabilecekti. ButtonGroup nesnesinin olması ve olmaması durumlarında uygulamanın davranışı Şekil 16, Şekil 17 ve Şekil 18’de gösterilmiştir. Şekil 16: ButtonGroup nesnesi kullanılmadığında herhangi bir zamanda iki JRadioButton bir arada seçilebilir. Şekil 17: ButtonGroup nesnesi kullanıldı. Şekil 18: rbErkek seçili durumda. JComboBox JComboBox kontrolü, .Net ortamındaki ComboBox kontrolü gibi bir veri kümesi üzerinden bir tane eleman seçmek amacıyla kullanılır. .Net ortamındaki ComboBox kontrolü seçme, metin cinsinden veri girişi, otomatik tamamlama gibi amaçlar için kullanılabildiği halde JComboBox kontrolü sadece veri seçmek amacıyla kullanılabilir, aynı anda birden fazla elemanın seçilmesine izin verilmez. Temel amacı gereği bir arada kullanılan JRadioButton nesnelerine benzese de çalışma zamanında dinamik veri bağlama kolaylığından dolayı bu kontrolden ayrılır. JComboBox kontrolüne veri bağlanması ve seçili elemanın değişmesi durumunda oluşan event’in kullanılması aşağıdaki kod üzerinde gösterilmiştir. Örnek Uygulama 6.11: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import javax.swing.JComboBox; 81 82 Bölüm 6 6 import javax.swing.JFrame; 7 import javax.swing.JLabel; 8 import javax.swing.JOptionPane; 9 public class JComboBoxTest extends JFrame 10 { 11 private JLabel lblOgrenimDurumu; 12 private JComboBox cmbOgrenimDurumu; 13 public JComboBoxTest() 14 { 15 super(“JComboBox örneği”); 16 Container container = getContentPane(); 17 container.setLayout(new FlowLayout()); 18 seçin”); lblOgrenimDurumu = new JLabel(“Öğrenim durumunuzu 19 container.add(lblOgrenimDurumu); 20 String[] ogrenimDurumlari = {“İlkokul”, “Ortaokul”, “Lise”, “Üniversite”, “Yüksek lisans”, “Doktora”}; 21 cmbOgrenimDurumu = new JComboBox(ogrenimDurumlari); 22 cmbOgrenimDurumu.addActionListener 23 ( 24 new ActionListener() 25 { 26 Event event) public void actionPerformed(Action 27 { 28 JOptionPane. showMessageDialog(null, cmbOgrenimDurumu.getSelectedItem(). toString()); 29 } 30 } 31 ); 32 container.add(cmbOgrenimDurumu); 33 setSize(200, 100); 34 setVisible(true); 35 } 36 public static void main(String[] args) 37 { 38 JComboBoxTest test = new JComboBoxTest(); 39 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 40 } 41 } Kodun açıklaması: Uygulama içinde bir tane JLabel ve bir tane JComboBox nesnesi tanımladık. Öğrenim durumlarını tutan bir string dizisi oluşturarak JComboBox nesnesinin yapıcı metoduna parametre olarak verdik. JComboBox nesnesi oluşurken yapıcı metoduna parametre olarak verdiğimiz string dizisini veri kaynağı olarak kullanır. JComboBox nesnesinin seçili elemanı de- Swing ğiştiğinde çalışmak üzere bir ActionListener event handler metodu tanımladık. Uygulama ilk çalıştığında ilk elemanın seçili durumda olduğu ve event handler metodunun uygulamanın ilk çalışması sırasında tetiklenmediği gözlenebilir. Bu aşamadan sonra seçili eleman değiştiği zaman event handler metodu tetiklenir ve seçilmiş olan elemanın sahip olduğu metni bir JOptionPane mesaj kutusu aracılığıyla kullanıcıya gösterir. Uygulamanın ihtiyacına göre birden fazla JComboBox nesnesinin aynı event handler metodunu kullanması sağlanabilir, bu durumda ActionListener interface’ini implement eden bir sınıf oluşturmak gerekir. Ayrıca bir JComboBox nesnesinin seçili elemanının değişmesiyle başka bir JComboBox nesnesinin veri kümesinin değişmesi de sağlanabilir. İlerleyen kısımlarda bu durumla ilgili bir örnek yapacağız. Uygulama ilk çalıştığında ve seçili eleman değiştiğinde Şekil 19 ve Şekil 20’deki gibi bir görünüm oluşur: Şekil 19 Şekil 20: Seçili eleman değişti. JList .Net ortamındaki ListBox kontrolünün Java platformundaki karşılığıdır. Belirli bir veri kümesi üzerinden bir veya daha çok sayıda eleman seçmek amacıyla kullanılır. JComboBox kontrolünde olduğu gibi sahip olduğu veri kümesi uygulamanın çalışması sırasında değiştirilebilir. Uygulamanın gereksinimlerine bağlı olarak bir tane, belirli bir aralıkta olmak üzere birden fazla veya belirli bir aralıkta bulunma şartı bulunmadan birden fazla elemanın seçili olmasına izin verilebilir. Belirli bir aralıkta olmak üzere birden fazla elemanın seçilmesi sağlanırsa kullanıcı, klavyedeki shift tuşuyla birlikte mouse imlecini kullanarak aralık şeklinde seçim yapabilir. Belirli bir aralıkta bulunma şartı olmadan seçim yapılabilecekse kullanıcı, klavyedeki ctrl tuşuyla birlikte mouse imlecini kullanabilir. Gereksinime göre JComboBox nesnesinde olduğu gibi ActionListener interface’i kullanılarak seçili elemanların değişmesi izlenebilir ve kontrol edilebilir. JList nesnesinin oluşturulması ve kullanımı aşağıdaki Örnek Uygulama 6.12’de gösterilmiştir. Örnek Uygulama 6.12: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import javax.swing.JButton; 6 import javax.swing.JFrame; 7 import javax.swing.JLabel; 8 import javax.swing.JList; 8 import javax.swing.JScrollPane; 10 import javax.swing.JTextArea; 11 import javax.swing.ListSelectionModel; 12 public class JListTest extends JFrame 13 { 14 private JLabel lblIlgiAlanlari; 15 private JList lstIlgiAlanlari; 83 84 Bölüm 6 16 private JScrollPane scrIlgiAlanlari; 17 private JTextArea txtSonuc; 18 private JButton btnGoster; 19 public JListTest() 20 { 21 super(“JListBox örneği”); 22 Container container = getContentPane(); 23 container.setLayout(new FlowLayout()); 24 seçin”); lblIlgiAlanlari = new JLabel(“İlgi alanlarınızı 25 container.add(lblIlgiAlanlari); 26 String[] ilgiAlanlari = new String[]{“Spor”, “Müzik”, “Edebiyat”, “Tiyatro”, “Resim”, “Sinema”}; 27 lstIlgiAlanlari = new JList(ilgiAlanlari); 28 //lstIlgiAlanlari.setSelectionMode(ListSelectionModel. SINGLE_ SELECTION); 29 //lstIlgiAlanlari.setSelectionMode(ListSelectionModel. SINGLE_ INTERVAL_SELECTION); 30 Model.MULTIPLE_ lstIlgiAlanlari.setSelectionMode(ListSelection INTERVAL_SELECTION); 31 lstIlgiAlanlari.setVisibleRowCount(4); 32 scrIlgiAlanlari = new JScrollPane(lstIlgiAlanlari); 33 container.add(scrIlgiAlanlari); 34 btnGoster = new JButton(“Göster”); 35 btnGoster.addActionListener 36 ( 37 new ActionListener() 38 { 39 event) public void actionPerformed(ActionEvent 40 { 41 txtSonuc.setText(“”); 42 Object[] seciliElemanlar = lstIlgiAlanlari.getSelectedValues(); 43 seciliElemanlar) for (Object eleman : 44 { 45 toString() + “\n”); 46 } 47 } 48 } 49 ); txtSonuc.append(eleman. Swing 50 container.add(btnGoster); 51 txtSonuc = new JTextArea(); 52 container.add(txtSonuc); 53 setSize(200, 200); 54 setVisible(true); 55 } 56 public static void main(String[] args) 57 { 58 JListTest test = new JListTest(); 59 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 60 } 61 } Kodun açıklaması: Uygulamada ilgi alanlarını görüntülemek için bir tane JList nesnesi ve bu nesneye scroll özelliği vermek için bir tane JScrollPane nesnesi kullandık. Kullanıcının bir veya birkaç tane seçebileceği ilgi alanı veri kümesini tutmak için kullanılan string dizisi JList nesnesinin yapıcı metoduna parametre olarak verildi. Jlist nesnesinin sahip olduğu setSelectionMode metodu, JList üzerinde aynı anda birden fazla elemanın seçilmesine izin verilip verilmeyeceğini belirler. Tek bir elemanın seçilmesine izin vermek isteseydik SINGLE_SELECTION seçeneğini, belirli bir aralıkta olmak üzere birden fazla elemanın seçilmesine izin vermek isteseydik SINGLE_INTERVAL_SELECTION seçeneğini kullanacaktık. Kullandığımız yapıda ise belirli bir aralığa dahil olan veya olmayan birden fazla eleman seçilebilir. JList nesnesi varsayılan olarak sahip olduğu bütün elemanları alt alta gösterir. Eleman sayısı arttıkça nesne uygulama arayüzü üzerinde fazla yer kaplamaya başlayacağından scroll özelliğine ihtiyaç duyulabilir. JList nesnesinin kendine ait bir scroll özelliği olmadığı halde JScrollPane bileşeni kullanılarak JList nesnesine scroll özelliği verilebilir. Koda dikkat edilirse JList nesnesinin doğrudan Container nesnesine eklenmediği görülebilir. JList nesnesi JScrollPane nesnesine, JScrollPane nesnesi ise Container nesnesine eklenmiştir. JList nesnesinin setVisibleRowCount metodu scroll işleminden önce görüntülenecek eleman sayısını belirlemek amacıyla kullanılır. Örnek uygulamada, dört tane elemanın görünür halde olmasına izin verilmiştir, diğer elemanlara ancak scroll işlemiyle ulaşılabilir. Örnek uygulamada, JList nesnesi üzerinde kullanıcının seçtiği elemanların değeri butona basıldıktan sonra JTextArea nesnesine yazılır. Butona her tıklandığında JTextArea bileşenin text özelliği boş string değeri almaktadır, bunun sebebi sadece butona tıklandığı anda seçili olan elemanların değerlerini görüntülemek istememizdir. Uygulama çalıştırıldığında ve butona tıklandığında Şekil 21 ve Şekil 22’de olduğu gibi görünür. Şekil 21: Uygulama çalıştırıldığında JTextArea nesnesi görünmüyor. Şekil 22: Üç tane eleman seçildi ve Göster butonuna tıklandı. 85 86 Bölüm 6 JList kontrolü sıklıkla kullanılabilecek bir kontrol olduğu için, birçok uygulamada karşımıza çıkabilecek bir arayüz oluşturalım ve iki tane JList kontrolü arasında veri aktarımı yapalım. Örnek Uygulama 6.13: 1 import java.awt.Container; 2 import java.awt.FlowLayout; 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import javax.swing.DefaultListModel; 6 import javax.swing.JButton; 7 import javax.swing.JFrame; 8 import javax.swing.JList; 9 import javax.swing.ListSelectionModel; 10 public class VeriAktarimi extends JFrame 11 { 12 private JList lstIlgiAlanlari; 13 private JList lstSecilmisIlgiAlanlari; 14 private JButton btnEkle; 15 private JButton btnCikar; 16 private DefaultListModel modelElemanlar; 17 private DefaultListModel modelSeciliElemanlar; 18 public VeriAktarimi() 19 { 20 super(“JListBox nesneleri arasında veri aktarımı”); 21 Container container = getContentPane(); 22 container.setLayout(new FlowLayout()); 23 modelElemanlar = new DefaultListModel(); 24 modelSeciliElemanlar = new DefaultListModel(); 25 modelElemanlar.addElement(“Spor”); 26 modelElemanlar.addElement(“Müzik”); 27 modelElemanlar.addElement(“Edebiyat”); 28 modelElemanlar.addElement(“Tiyatro”); 29 modelElemanlar.addElement(“Resim”); 30 modelElemanlar.addElement(“Sinema”); 31 lstIlgiAlanlari = new JList(modelElemanlar); 32 MULTIPLE_ lstIlgiAlanlari.setSelectionMode(ListSelectionModel. INTERVAL_SELECTION); 33 container.add(lstIlgiAlanlari); 34 btnCikar = new JButton(“<<<<”); 35 btnCikar.setEnabled(false); 36 btnCikar.addActionListener 37 ( 38 new ActionListener() 39 { Swing 40 event) public void actionPerformed(ActionEvent 41 { 42 Object[] seciliElemanlar = lstSecilmisIlgiAlanlari.getSelectedValues(); 43 seciliElemanlar) for (Object eleman : 44 { 45 removeElement(eleman.toString()); modelSeciliElemanlar. 46 addElement(eleman.toString()); modelElemanlar. 47 } 48 == 0) if(modelSeciliElemanlar.getSize() 49 { 50 51 } 52 if(modelElemanlar.getSize() != 0) 53 { 54 55 } 56 } 57 } 58 ); 59 container.add(btnCikar); 60 btnEkle = new JButton(“>>>>”); 61 btnEkle.addActionListener 62 ( 63 new ActionListener() 64 { 65 event) public void actionPerformed(ActionEvent 66 { btnCikar.setEnabled(false); btnEkle.setEnabled(true); 67 Object[] seciliElemanlar = lstIlgiAlanlari.getSelectedValues(); 68 seciliElemanlar) for (Object eleman : 69 { 70 removeElement(eleman.toString()); modelElemanlar. 71 addElement(eleman.toString()); modelSeciliElemanlar. 72 } 73 if(modelElemanlar.getSize() == 0) 74 { 87 88 Bölüm 6 75 btnEkle.setEnabled(false); 76 } 77 != 0) if(modelSeciliElemanlar.getSize() 78 { 79 80 } 81 } 82 } 83 ); 84 container.add(btnEkle); btnCikar.setEnabled(true); 85 nlar); lstSecilmisIlgiAlanlari = new JList(modelSeciliElema 86 lstIlgiAlanlari.setSize(100, 100); 87 container.add(lstSecilmisIlgiAlanlari); 88 setSize(200, 200); 89 setVisible(true); 90 } 91 public static void main(String[] args) 92 { 93 VeriAktarimi test = new VeriAktarimi(); 94 test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 95 } 96 } Kodun açıklaması: Uygulamada iki tane JList kontrolü ile iki tane JButton kontrolü kullandık. Bir önceki örnekte JList kontrolüne veri kaynağı olarak bir string dizisi verdiğimiz halde bu örnekte DefaultListModel nesnesinden yararlandık. Bunun sebebi, DefaultListModel bileşeninin uygulamanın çalışması sırasında veri ekleme ve çıkartma işlemlerini desteklemesidir. Uygulama ilk çalıştığı sırada bütün ilgi alanları sol tarafta bulunan JList bileşenin içinde bulunur ve sol tarafa veri aktarmayı sağlayan buton aktif değildir. Bir veya birden fazla ilgi alanı seçilip sağ tarafa veri aktarmayı sağlayan butona tıklandığında, seçili elemanlar modelElemanlar nesnesinden removeElement metodu yardımıyla çıkartılır modelSeciliElemanlar nesnesine addElement metodu yardımıyla eklenir. Bu işlemler JButton nesneleri için yazılan ve ActionListener interface’ini implement eden metodlar içinde gerçeklenir. Sol taraftaki JList bileşenin içinde eleman kalmadığında sola aktarma işlemini gerçekleyen buton aktif olmaktan çıkar. JList nesnelerine veri kaynağı olarak bağladığımız DefaultListModel nesnelerini event handler metodları içinde tekrar bağlamaya gerek olmadığına dikkat edelim, bu durum kullandığımız nesnelerin referans tipinde olmasından kaynaklanır. DefaultListModel nesnelerinin barındırdığı veriler değiştiğinde otomatik olarak JList görünümleri de değişir. Uygulama çeşitli çalışma aşamalarında Şekil 23, Şekil 24 ve Şekil 25’te olduğu gibi görünür: Swing Şekil 23: Üç tane eleman seçili durumda, henüz butona tıklanmış. Şekil 24: Sağ tarafa aktarma işlemi yapıldıktan sonra iki buton da aktif. Şekil 25: Sol taraftaki bütün veriler sağ tarafa geçmiş, sağa aktarma butonu kullanılamıyor. Mouse Event’lerinin Yönetilmesi: Fare hareketleri ile tetiklenen event yapıları MouseListener ve MouseMotionListener interface’lerinin implement edilmesiyle kontrol edilebilir. Bu interface’lerin içinde bulunan metodlar şu şekildedir: MouseListener mousePressed: Fare butonlarından birine basıldığı anda tetiklenir. mouseClicked: Fare butonlarından birine basılıp bırakıldığında tetiklenir. mouseReleased: Fare butonlarından birine basıldıktan sonra bırakıldığında tetiklenir. mouseEntered: Fare imleci bir bileşenin alanına girdiğinde tetiklenir. mouseExited: Fare imleci bir bileşenin alanını terk ettiğinde tetiklenir. MouseMotionListener: mouseDragged: Fare imleci bir bileşenin üzerindeyken butonlardan birine basılarak mouse hareket ettirildiğinde tetiklenir. mouseMoved: Fare imleci bir bileşenin üzerindeyken fare hareket ettirildiğinde tetiklenir. Fare ile ilgili event’leri kontrol eden bütün metodlar parametre olarak bir MouseEvent nesnesi alırlar. Fare imlecinin x ve y koordinatları, hangi butona kaç kere basıldığı gibi bilgiler bu nesne aracılığıyla alınır. Mouse event’leri herhangi bir bileşen için kaydedilebilir ve bu bileşen üzerinde herhangi bir fare hareketi olduğunda ilgili event metodu tetiklenir. Interface mantığından dolayı MouseListener ve MouseMotionListener interface’lerinden herhangi biri implement edildiğinde bu interface’in içinde bulunan bütün metodlar implement edilmek zorundadır. Kullanılmayacak olan metodların tanımları yapılarak içleri boş bırakılır. Mouse event’lerini kontrol etmek amacıyla oluşturduğumuz uygulamanın kodları aşağıdaki gibidir. 89 90 Bölüm 6 1import java.awt.Container; 2import java.awt.FlowLayout; 3import java.awt.event.MouseEvent; 4import java.awt.event.MouseListener; 5import javax.swing.JFrame; 6import javax.swing.JLabel; 7import javax.swing.JTextArea; 8public class MouseYonetimi extends JFrame 9{ 10 private JTextArea txtContainerEvent; 11 private JTextArea txtLabelEvent; 12 private JLabel lblAlan; 13 public MouseYonetimi() 14 { 15 super(“Mouse yönetimi”); 16 Container container = getContentPane(); 17 container.setLayout(new FlowLayout()); 18 container.addMouseListener(new ContainerMouseListenerHandler()); 19 lblAlan = new JLabel(“Mouse imlecini bu alanın üzerine getirin”); 20 lblAlan.addMouseListener(new JLabelMouseListenerHandler()); 21 container.add(lblAlan); 22 txtContainerEvent = new JTextArea(); 23 container.add(txtContainerEvent); 24 txtLabelEvent = new JTextArea(); 25 container.add(txtLabelEvent); 26 setSize(200, 200); 27 setVisible(true); 28 } 29 private class ContainerMouseListenerHandler implements MouseListener 30 { 31 public void mouseClicked(MouseEvent event) 32 { 33 txtContainerEvent. append(ButonAdiAl(event.getButton()) + “ butona “ + event. getClickCount() + “ kez basıldı ve bırakıldı\n”); 34 } 35 public void mousePressed(MouseEvent event) 36 { 37 txtContainerEvent. append(ButonAdiAl(event.getButton()) + “ butona (“ + event.getX() + “,” + event.getY() + “) noktasında basıldı\n”); 38 } Swing 39 public void mouseReleased(MouseEvent event) 40 { 41 txtContainerEvent. append(ButonAdiAl(event.getButton()) + “ buton bırakıldı\n”); 42 } 43 public void mouseEntered(MouseEvent event) 44 { 45 } 46 public void mouseExited(MouseEvent event) 47 { 18 } 49 } 50 private class JLabelMouseListenerHandler implements MouseListener 51 { 52 public void mouseEntered(MouseEvent event) 53 { 54 txtLabelEvent.append(“Mouse imleci JLabel bölgesine girdi\n”); 55 } 56 public void mouseExited(MouseEvent event) 57 { 58 txtLabelEvent.append(“Mouse imleci JLabel bölgesinden çıktı\n”); 59 } 60 public void mouseClicked(MouseEvent event) 61 { 62 } 63 public void mousePressed(MouseEvent event) 64 { 65 } 66 public void mouseReleased(MouseEvent event) 67 { 68 } 69 } 70 public static String ButonAdiAl(int butonNumarasi) 71 { 72 String butonAdi = “”; 73 if(butonNumarasi == MouseEvent.BUTTON1) 74 { 75 76 } 77 else if(butonNumarasi == MouseEvent.BUTTON2) 78 { 79 butonAdi = “Sol”; butonAdi = “Orta”; 91 92 Bölüm 6 80 } 81 else if(butonNumarasi == MouseEvent.BUTTON3) 82 { 83 butonAdi = “Sağ”; 84 } 85 return butonAdi; 86 } 87 public static void main(String[] args) 88 { 89 MouseYonetimi test = new MouseYonetimi(); 90 CLOSE); test.setDefaultCloseOperation(JFrame.EXIT_ON_ 91 } 92 } Kodun açıklaması: Container nesnesi ve JLabel nesnesi için birer tane event handler sınıfı oluşturduk. İki sınıf da MouseListener interface’ini implement ediyor. Kullanılmayan metodların gövdelerini boş bırakarak interface yapısından kaynaklanan derleme hatalarını önlemiş olduk. Uygulamada kullanılan statik ButonAdiAl metodu, tıklanan butonun numarasını alarak butona bir isim vererek döndürür. Örneğin sol fare butonuna tıklandığında buton kodu olarak “1” değeri oluşur, ButonAdiAl metodunu kullanarak “Sol” değeri döndürülür. Container nesnesinin ilişkili olduğu ContainerMouseListenerHandler sınıfının içinde bulunan metodlar aşağıdaki işlevlere sahiptir: mousePressed: Fare butonlarından biriyle formun üzerine tıklandığında hangi butona basıldığı ve tıklanan pozisyonun x ve y değerleri JTextArea bileşenine eklenir. mouseReleased: Tıklandıktan sonra bırakılan mouse butonunun adı JTextArea bileşenine eklenir. mouseClicked: Tıklandıktan sonra bırakılan fare butonunun adı ve tıklanma sayısı JTextArea bileşenine eklenir. JLabel nesnesinin ilişkili olduğu JLabelMouseListenerHandler sınıfının içinde bulunan metodlar aşağıdaki işlevlere sahiptir: mouseEntered: Fare imleci JLabel kontrolünün üzerine geldiğinde ikinci JTextArea nesnesine bu bilgi yazılır. mouseExited: Fare imleci JLabel kontrolünün bölgesinden çıktığında ikinci JTextArea nesnesine bu bilgi yazılır. Uygulama çalıştırıldıktan sonra mouse ile JLabel kontrolünün bölgesine girilip çıkıldığında ve form üzerine farklı fare butonlarıyla çeşitli seferlerde tıklandığında, uygulama arayüzü aşağıdaki gibi bir görünüm alır. Swing Java ortamının event altyapısı interface’ler üzerine kurulu olduğu için, içinde birden fazla metod tanımı bulunan interface’lerin kullanılması durumunda gereksiz metodlar da implement edilmek zorundadır. Bu durumun önüne geçmek için Adapter sınıfları oluşturulmuştur. Adapter sınıfları ilgili interface’lerin bütün metodlarını gövdeleri boş olacak şekilde implement eder. Yeni oluşturulan event handler sınıfları Adapter sınıflarından türetilirse sadece gerekli event metodları override edilebilir. Mouse event’lerinin kontrol edilmesi için MouseAdapter sınıfının temel sınıf olarak kullanıldığı bir uygulamanın kodları aşağıdadır. 1import java.awt.Container; 2import java.awt.FlowLayout; 3import java.awt.event.MouseAdapter; 4import java.awt.event.MouseEvent; 5import javax.swing.JFrame; 6import javax.swing.JTextArea; 7public class MouseAdapterTest extends JFrame 8{ 9 private JTextArea txtContainerEvent; 10 public MouseAdapterTest() 11 { 12 super(“Mouse yönetimi”); 13 Container container = getContentPane(); 14 container.setLayout(new FlowLayout()); 15 container.addMouseListener(new ContainerMouseHandler()); 93 94 Bölüm 6 16 txtContainerEvent = new JTextArea(); 17 container.add(txtContainerEvent); 18 setSize(200, 200); 19 setVisible(true); 20 } 21 private class ContainerMouseHandler extends MouseAdapter 22 { 23 public void mouseClicked(MouseEvent event) 24 { 25 txtContainerEvent.append(event. getButton() + “ numaralı butona “ + event.getClickCount() + “ kez basıldı ve bırakıldı\n”); 26 } 27 } 28 public static void main(String[] args) 29 { 30 MouseAdapterTest test = new MouseAdapterTest(); 31 CLOSE); 32 } 33 } test.setDefaultCloseOperation(JFrame.EXIT_ON_ Uygulama çalıştırılıp formun üzerine çeşitli mouse butonlarıyla tıklanırsa arayüz aşağıdaki gibi bir görünüme sahip olur. Klavye Event’lerinin Yönetilmesi Kullanıcının klavye ile etkileşimi sonucunda oluşan event’ler KeyListener interface’ini implement eden sınıfların oluşturulması ile kontrol edilir. KeyListener interface’inin içinde aşağıdaki metodlar bulunur: keyPressed: Klavyedeki herhangi bir tuşa basıldığında tetiklenir. Swing keyTyped: Aksiyon tuşu olmayan tuşlardan herhangi birine basıldığında tetiklenir. Aksiyon tuşları ok tuşları, home, end, scroll lock gibi işlevsel özellikleri olan tuşlardır. keyReleased: Herhangi bir buton bırakıldıktan sonra, yani keyTyped veya keyPressed event’inden sonra tetiklenir. Klavye event’lerinin kullanımını gösteren uygulamayı oluşturan kodlar aşağıda verilmiştir. 1import java.awt.Container; 2import java.awt.FlowLayout; 3import java.awt.event.KeyEvent; 4import java.awt.event.KeyListener; 5import javax.swing.JFrame; 6import javax.swing.JTextArea; 7public class KeyboardEventTest extends JFrame 8{ 9 private JTextArea txtContainerEvent; 10 public KeyboardEventTest() 11 { 12 super(“Klavye yönetimi”); 13 Container container = getContentPane(); 14 container.setLayout(new FlowLayout()); 15 txtContainerEvent = new JTextArea(); 16 txtContainerEvent.setEnabled(false); 17 container.add(txtContainerEvent); 18 this.addKeyListener(new KeyboardEventHandler()); 19 setSize(200, 200); 20 setVisible(true); 21 } 22 KeyListener private class KeyboardEventHandler implements 23 { 24 public void keyPressed(KeyEvent event) 25 { 26 txtContainerEvent.append(“keyPressed\ tDeğer: “ + event.getKeyText(event.getKeyCode()) + “\n”); 27 } 28 public void keyReleased(KeyEvent event) 29 { 30 n\n”); 31 } 32 public void keyTyped(KeyEvent event) 33 { txtContainerEvent.append(“keyReleased\t\ 34 txtContainerEvent.append(“keyTyped\ tDeğer: “ + event.getKeyChar() + “\n”); 95 96 Bölüm 6 35 } 36 } 37 public static void main(String[] args) 38 { 39 KeyboardEventTest test = new KeyboardEventTest(); 40 CLOSE); 41 } 42 } test.setDefaultCloseOperation(JFrame.EXIT_ON_ Kodun açıklaması: Kullanıcı klavyedeki herhangi bir tuşa bastığında keyPressed event’i tetiklenir. Basılan tuş aksiyon tuşu değilse keyTyped event’i de tetiklenir. Basılan tuş ne olursa olsun keyReleased event handler metodu en son çalışır. keyPressed ve keyTyped event handler metodları; çalışan event’in adını ve basılan tuşa karşılık gelen karakter değerini JTextArea bileşenine ekler. keyReleased event handler metodu ise tuşun bırakıldığını belirtir, hangi tuş üzerine işlem yapıldığını bildirmez. Uygulama çalıştırıldıktan sonra sırasıyla q, home, F2, 7, alt, ctrl tuşlarına basılırsa arayüz aşağıdaki gibi bir görünüme kavuşur. Layout Yönetimi Swing tabanlı arayüzler içeren Java uygulamalarında arayüze eklenen bileşenlerin form üzerindeki görünümleri layout yapılarıyla şekillendirilir. FlowLayout: Bu görünümde arayüze eklenen bileşenler soldan sağa doğru eklendikleri sırada görüntülenirler. Bütün örneklerimizde FlowLayout görünümünü kullandık. Dört tane JButton nesnesiyle bir tane JTextArea nesnesini FlowLayout biçiminde düzenlenmiş bir Swing arayüzüne ekleyelim. Uygulamanın kodları aşağıda verilmiştir. 1import java.awt.Container; 2import java.awt.FlowLayout; 3import javax.swing.JButton; Swing 4import javax.swing.JFrame; 5import javax.swing.JTextArea; 6public class FlowLayoutTest extends JFrame 7{ 8 public FlowLayoutTest() 9 { 10 super(“BorderLayout”); 11 Container container = getContentPane(); 12 FlowLayout layout = new FlowLayout(); 13 container.setLayout(layout); 14 JButton btn1 = new JButton(“1. Buton”); 15 container.add(btn1); 16 JButton btn2 = new JButton(“2. Buton”); 17 container.add(btn2); 18 JButton btn3 = new JButton(“3. Buton”); 19 container.add(btn3); 20 JButton btn4 = new JButton(“4. Buton”); 21 container.add(btn4); 22 JTextArea txt = new JTextArea(“Metin alanı”); 23 container.add(txt); 24 setSize(300, 200); 25 setVisible(true); 26 } 27 public static void main(String[] args) 28 { 29 FlowLayoutTest test = new FlowLayoutTest(); 30 CLOSE); test.setDefaultCloseOperation(JFrame.EXIT_ON_ 31 } 32 } Bileşenler formun üzerine soldan sağa doğru eklenmiştir. Çalışma sırasında formun boyutu değiştirilirse arayüz kontrollerinin form üzerindeki yerleri de değişecektir. Formun iki farklı boyutu için arayüzün görünümü aşağıda verilmiştir. BorderLayout: JFrame sınıfından türeyen sınıfların varsayılan layout görünümüdür. Uygulama arayüzü kuzey, güney, doğu, batı ve merkez olmak üzere beş parçaya ayrılır. Uygulamaya kon- 97 98 Bölüm 6 trol ekleme aşamasında hangi bölgeye ekleneceği belirtilir. Dört tane JButton nesnesi ile bir tane JTextBox nesnesi içeren bir arayüzü BorderLayout biçiminde oluşturalım. import java.awt.BorderLayout; import java.awt.Container; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JTextArea; 1public class BorderLayoutTest extends JFrame 2{ 3 public BorderLayoutTest() 4 { 5 super(“BorderLayout”); 6 Container container = getContentPane(); 7 BorderLayout layout = new BorderLayout(10, 10); 8 container.setLayout(layout); 9 JButton btnNorth = new JButton(“Kuzey”); 10 container.add(btnNorth, BorderLayout.NORTH); 11 JButton btnSouth = new JButton(“Güney”); 12 container.add(btnSouth, BorderLayout.SOUTH); 13 JButton btnEast = new JButton(“Doğu”); 14 container.add(btnEast, BorderLayout.EAST); 15 JButton btnWest = new JButton(“Batı”); 16 container.add(btnWest, BorderLayout.WEST); 17 bölge”); JTextArea txtCenter = new JTextArea(“Merkez 18 container.add(txtCenter, BorderLayout.CENTER); 19 setSize(300, 200); 20 setVisible(true); 21 } 22 public static void main(String[] args) 23 { 24 BorderLayoutTest test = new BorderLayoutTest(); 25 CLOSE); 26 } 27 } test.setDefaultCloseOperation(JFrame.EXIT_ON_ JButton kontrolleri arayüzün kuzey, güney, doğu ve batı bölgelerine, JTextArea bileşeni ise merkez bölgesine eklenmiştir. Uygulama çalıştırıldığında aşağıdaki gibi görünür. Swing GridLayout: GridLayout biçimi, container’ı satırlar ve sütunlardan oluşan bir grid şeklinde düzenler. GridLayout şeklinde düzenlenmiş bir arayüzdeki bütün bileşenlerin boyutu aynı olur. GridLayout biçiminde düzenlenmiş bir Swing arayüzünü aşağıdaki kod bloğuyla oluşturabiliriz. 1import java.awt.Container; 2import java.awt.GridLayout; 3import javax.swing.JButton; 4import javax.swing.JFrame; 5import javax.swing.JTextArea; 6public class GridLayoutTest extends JFrame 7{ 8 public GridLayoutTest() 9 10 { super(“GridLayout”); 11 Container container = getContentPane(); 12 GridLayout layout = new GridLayout(2, 3); 13 container.setLayout(layout); 14 JButton btn1 = new JButton(“1. Buton”); 15 container.add(btn1); 16 JButton btn2 = new JButton(“2. Buton”); 17 container.add(btn2); 18 JButton btn3 = new JButton(“3. Buton”); 19 container.add(btn3); 20 JButton btn4 = new JButton(“4. Buton”); 21 container.add(btn4); 22 JTextArea txt = new JTextArea(“Metin alanı”); 23 24 25 container.add(txt); setSize(300, 200); setVisible(true); 26 27 } public static void main(String[] args) 28 29 { GridLayoutTest test = new GridLayoutTest(); 30 CLOSE); 31 test.setDefaultCloseOperation(JFrame.EXIT_ON_ 32 } } 99 100 Bölüm 6 Uygulama çalıştırıldığında aşağıdaki gibi bir arayüz oluşur: Bunların dışında daha az kullanılan BoxLayout, CardLayout ve GridBagLayout görünümleri de vardır. 7 JDBC 7 JDBC • Veritabanı Bağlantısının Kurulması • Veri Değiştirme Komutları (insert, update, delete) • Veri Sorgulama Komutları (Select) • Parametreli SQL İfadelerinin Çalıştırılması • Stored Procedure’lerin Çalıştırılması JDBC Java uygulamaları üzerinden veri kaynaklarını bulmak ve bulunan kaynaklara bağlanarak bu kaynaklar üzerinde çeşitli sorguları çalıştırmak için JDBC adı verilen Java API’si kullanılır. JDBC, .Net ortamındaki ADO.Net yapısının Java platformundaki karşılığıdır. JDBC, platform bağımsızlığını sağlayabilmek için, ortamdaki veri kaynaklarına bağlanmak amacıyla kullanılan sürücüleri çalışma zamanında tespit edebilen bir sürücü yönetim sistemine(Driver Manager) sahiptir. Veri kaynaklarına bağlanmadan önce sürücülerin yüklenmesini sağlamak için Class.forName() metodu gereken parametreler kullanılarak çağrılır. ODBC sürücüsüne sahip olan veri kaynaklarına bağlanmak için JDBC-ODBC köprüsü kullanılır. Sürücüler bulunduktan sonra ODBC üzerinde kayıtlı bulunan herhangi bir veri kaynağına bağlanılabilir. Windows tabanlı işletim sistemleri üzerinde bulunan bir veritabanı yönetim sistemi yazılımındaki bir veritabanına bağlanmak için, bu veritabanının ODBC kaynaklarına eklenmesi gerekir. Bu işlem denetim masasında veya yönetimsel araçlarda bulunan veri kaynakları (ODBC) seçeneneği kullanılarak gerçekleştirilebilir. Bilgisayarımızda bulunan Microsoft SQL Server 2005 veritabanı sunucusu üzerinde dört tablodan oluşan AlisveriSitesi adında bir veritabanı oluşturarak uygulamalarımızı bu veritabanı üzerinde çalıştıralım. AlisverisSitesi veritabanını oluşturmak ve sahip olduğu tabloları oluşturmak için kullanılacak SQL ifadeleri ve veritabanının diyagramı Şekil 1’de gösterilmiştir. Şekil 1 Örnek Uygulama 7.1: 1 create database AlisverisSitesi 2 use AlisverisSitesi 3 create table Urun 4 ( 5 urunID int primary key identity(1, 1), 6 urunAdi varchar(20), 7 fiyat money 8 ) 9 create table Musteri 104 Bölüm 7 10 ( 11 musteriID int primary key identity(1, 1), 12 ad varchar(20), 13 soyad varchar(30), 14 ) 15 create table Siparis 16 ( 17 siparisID int primary key identity(1, 1), 18 musteriID int foreign key references Musteri(musteriID), 19 siparisTarihi smalldatetime 20 ) 21 create table SiparisDetay 22 ( 23 siparisID int foreign key references Siparis(siparisID), 24 urunID int foreign key references Urun(urunID), 25 miktar int 26 ) Veritabanımızı oluşturduktan sonra denetim masasından veya yönetimsel araçlardan veri kaynakları (ODBC) ayarlarını açalım. Java üzerinden herhangi bir veri kaynağına bağlanmadan önce kullanacağımız veritabanı sunucusunu ve veritabanını System DSN menüsünden kayıt edeceğiz. System DSN menüsü ilk açıldığında Şekil 2’dekine benzer bir görünüme sahip olacaktır. Şekil 2 Yeni bir veri kaynağı eklemek için Add butonuna tıklayalım. Karşımıza çıkan pencerede, bağlanmak istediğimiz veri kaynağını seçtikten sonra Finish butonuna tıklayarak veri kaynağımızla ilgili JDBC ek bilgiler istenir. Microsoft SQL Server veritabanı sunucusu kullandığımız için en altta bulunan SQL Server seçeneğini seçmeliyiz. Şekil 3 Bir sonraki pencerede, veri kaynağımıza vereceğimiz ismi ve kullanılacak veritabanı sunucusunun adını veya adresini yazdıktan sonra Next butonuna tıklayalım. Veri kaynağı ile ilgili ayarlar Şekil 4’te görülebilir. Şekil 4 Veritabanı sunucumuz belirlendikten sonra, bu sunucuya bağlanmak için kullanılacak olan kimlik bilgilerinin girileceği ekran karşımıza çıkar. SQL Server veritabanı sunucusuna bağlanmak için Windows Authentication veya SQL Authentication kullanabiliriz. Windows Authentication kullanmamız durumunda kullanıcı adı ve şifre bilgileri istenmez, veritabanı sunucusuna bağlanmak için sisteme giriş yapmış olan Windows kullanıcı hesabı kullanılır. Bahsedilen ayarlar Şekil 5’te görülebilir. 105 106 Bölüm 7 Şekil 5 SQL Server 2005 veritabanı sunucusuna bağlanmak için Windows NT Authentication modunu seçtikten sonra Next butonuna tıklayın. AlisverisSitesi adını verdiğimiz sürücüyle bağlanılacak olan varsayılan veritabanını en üstteki change default database to checkbox’ı yardımıyla belirleyebiliriz. AlisverisSitesi veritabanını varsayılan veritabanı olarak seçtikten sonra Next butonuna ve bir sonraki pencerede Finish butonuna tıklayın. Son üç aşamada karşımıza çıkan ekran görüntüleri aşağıdadır. Şekil 6 JDBC Şekil 7 Finish butonuna tıkladıktan sonra ODBC veri kaynağının başarıyla eklendiğini belirten bir mesaj aldık. ODBC kaynağı eklendikten sonra Şekil 8’de gösterilen Test Data Source butonuna tıklayarak veritabanı bağlantımızın doğru çalışıp çalışmadığını kontrol edin. Şekil 9’da görülen mesaj, veri kaynağına başarıyla bağlandığımızı gösterir. Şekil 8 107 108 Bölüm 7 Şekil 9 Veritabanı Bağlantısının Kurulması Veritabanına bağlanmak için kullanılacak ODBC kaynağını işletim sistemine ekledikten sonra Java ortamı üzerinden AlisverisSitesi veritabanına bağlanmaya çalışalım. Veri kaynağına bağlanmak için aşağıdaki adımların gerçekleşmesi gerekir: 1� İşletim sistemi üzerinde bulunan JDBC sürücüsünün Class.forName() metodu yardımıyla bulunması. 2. DriverManager sınıfında bulunan getConnection() metodu ile veritabanı bağlantısının sağlanması. Class.forName metodunun uygulamada kullanılabilmesi için, bu metodu çağıran metodun ClassNotFoundException tipinden hata fırlatabilmesi gerekir. Benzer bir şekilde DriverManager.getConnection() metodunu çağıran metodun SQLException tipinden hata fırlatabilmesi gerekir. İki metod çağrısını da uygulamanın main metodundan yapacağımız için main metodumuz bu iki hatayı da fırlatabilecek şekişlde oluşturulur. Aşağıdaki kodu yazıp çalıştırdığımızda herhangi bir mesaj almazsak ODBC kaynağına bağlantı kurmayı başardık demektir. Örnek Uygulama 7.2: 1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.SQLException; 4 public class VeritabaniBaglantisi 5 { 6 public static void main(String[] args) throws ClassNotFoundException, SQLException 7 { 8 try 9 { 10 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); 11 Connection baglanti = DriverManager. getConnection(“jdbc:odbc:AlisverisSites1i”); JDBC 12 } 13 catch (Exception e) 14 { 15 16 } 17 } System.out.println(e.getMessage()); 18 } Eğer bağlantı kurma aşamasında SQLException veya ClassNotFoundException oluşursa aşağıdaki iki hata mesajından birini alırız. [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified sun.jdbc.ODBC.JdbcOdbcDriver JDBC altyapısı kullanılarak veritabanı sunucularının desteklediği komutlar çalıştırılabilir. SQL Server veritabanı sunucusu üzerinde veri değiştirme komutları (insert, update, delete), veri tanım komutları(create, alter, drop) ve sorgu komutları (select) çalıştırılarak uygulamanın ihtiyaçlarına göre bir veri erişim altyapısı oluşturulması sağlanabilir. Veri kaynağı üzerinde çalıştırılacak ifadeler, Connection nesnesi aracılığıyla oluşturulur. Connection nesnesinin createStatement() metodu ile oluşturulan Statement nesnesi veritabanına SQL ifadelerini göndermek amacıyla kullanılır. Statement nesnesi oluştuktan sonra, bu nesnenin sahip olduğu executeXxx metodları ile SQL ifadeleri veritabanı sunucusu üzerinde çalıştırılır. Connection nesnesini sahip olduğı metodlarla Statement tipinden başka tiplerde ifadeler de oluşturulabilir. (Örneğin BLOB, XML..) Veri Değiştirme Komutları (insert, update, delete) JDBC kütüphanesi kullanılarak veritabanı üzerinde insert, update, delete gibi veri ile alakalı komutlar çalıştırmak için genellikle Statement nesnesinin executeUpdate komutları kullanılır. executeUpdate komutları, veritabanı üzerinde çalıştırılan ifadeden etkilenen kayıt sayısını tam sayı değeri olarak döndürür ve ADO.Net mimarisinde bulunan Command nesnesinin ExecuteNonQuery metoduna benzerlik gösterir. Örnek Uygulama 7.3’de verdiğimiz kod bloğunda veritabanına üç tane müşteri ve beş tane ürün eklenmektedir. Tablolarda bulunan xxxID alanları identity olarak tanımlandığından, değerleri otomatik olarak ve birer birer artarak verilir. Bu sebepten dolayı insert ifadesinin içinde xxxID alanlarının bulunması hata oluşmasına neden olur. Örnek Uygulama 7.3: 1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.SQLException; 4 import java.sql.Statement; 5 public class VeritabaniBaglantisi 6 { 7 public static void main(String[] args) throws ClassNotFoundException, SQLException 8 { 9 try 109 110 Bölüm 7 10 { 11 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); 12 Connection dbConnection = DriverManager. getConnection(“jdbc:odbc:AlisverisSitesi”); 13 Statement staInsert = dbConnection. createStatement(); 14 staInsert.executeUpdate(“INSERT INTO Musteri(ad, soyad) VALUES (‘Yalçın’, ‘Kaya’)”); 15 staInsert.executeUpdate(“INSERT INTO Musteri(ad, soyad) VALUES (‘Erkut’, ‘Kutlar’)”); 16 staInsert.executeUpdate(“INSERT INTO Musteri(ad, soyad) VALUES (‘Hasan’, ‘Polat’)”); 17 staInsert.executeUpdate(“INSERT INTO Urun(urunAdi, fiyat) VALUES (‘1GB Ram’, 40)”); 18 staInsert.executeUpdate(“INSERT INTO Urun(urunAdi, fiyat) VALUES (‘3 GHz CPU’, 80)”); 19 staInsert.executeUpdate(“INSERT INTO Urun(urunAdi, fiyat) VALUES (‘2 GB USB Bellek’, 30)”); 20 staInsert.executeUpdate(“INSERT INTO Urun(urunAdi, fiyat) VALUES (‘256 MB Ekran Kartı’, 20)”); 21 staInsert.executeUpdate(“INSERT INTO Urun(urunAdi, fiyat) VALUES (‘DVD Writer’, 45)”); 22 staInsert.close(); 23 } 24 catch (Exception e) 25 { 26 27 } 28 } System.out.println(e.getLocalizedMessage()); 29 } Kodun açıklaması: JDBC sürücüsü çalışma zamanında bulunduktan sonra daha önce oluşturulan AlisverisSitesi isimli ODBC kaynağı aracılığıyla AlisverisSitesi veritabanı üzerinde insert ifadeleri çalıştırıldı. Statement nesnesinin işi bittikten sonra kapatılması, alınan sistem ve veritabanı kaynaklarının geri verilmesini sağlar. Uygulamanın çalışması aşamasında bir hata oluşmazsa arayüz üzerinde herhangi bir mesaj oluşmaz. Gerçek bir uygulamada ürün ve müşteri bilgileri Java kodunun içinde girilmez, web veya işletim sistemi ortamında çalışan bir kullanıcı arayüzü aracılığıyla kullanıcıdan alınır. İlerleyen sayfalarda, Swing görsel programlama kütüphanesini kullanarak benzer bir uygulama geliştireceğiz. Veri Sorgulama Komutları (Select) Select komutunun Java ortamından veritabanı sunucusu üzerinde çalıştırılması için benzer nesneler oluşturulur. İşletim sistemi üzerinde bulunan JDBC sürücüsüne ulaşıldıktan sonra ODBC kaynağına bağlanılır, bu bağlantı üzerinden Statement nesnesi oluşturulur ve Statement nesnesinin executeXxx komutlarından biri çalıştırılır. Veritabanından tek bir tam sayı değişkeni yerine birden fazla satır ve sütündan oluşabilen bir değer kümesi döndüğünden, select ifadelerinin çalıştırılması insert, update, delete ifadelerinin çalıştırılmasından farklıdır. executeXxx komutları select ifadelerini veritabanına göndermek için kullanıldığında geriye bir sonuç kümesi (result set) döndürür. Bu sonuç kümesi veri kaynağına bağlı olarak çalışan tek yönlü bir işaretçi JDBC yapısıdır ve ADO.Net yapısında bulunan xxxDataReader sınıflarının Java ortamındaki karşılığıdır. Sonuç kümesi alındıktan sonra kümenin sonuna gelene kadar okuma işlemi tekrar edilir ve bu işlem döngüler aracılığıyla gerçekleştirilir. AlisverisSitesi veritabanında bulunan Urun tablosundaki verileri okuyalım: Örnek Uygulama 7.4: 1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.sql.Statement; 6 public class Urunler 7 { 8 public static void main(String[] args) throws ClassNotFoundException, SQLException 9 { 10 try 11 { 12 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); 13 Connection dbConnection = DriverManager. getConnection(“jdbc:odbc:AlisverisSitesi”); 14 Statement staSelect = dbConnection. createStatement(); 15 ResultSet resultSet = staSelect. executeQuery(“SELECT urunID, urunAdi, fiyat FROM Urun”); 16 while(resultSet.next()) 17 { 18 getInt(“urunID”)); System.out.print(resultSet. 19 System.out.print(“\t” + resultSet. getString(“urunAdi”) + “\t\t”); 20 getDouble(“fiyat”)); System.out.println(resultSet. 21 } 22 staSelect.close(); 23 } catch (Exception e) { } } System.out.println(e.getLocalizedMessage()); } Kodun açıklaması: Daha önceki uygulamada yaptığımız gibi JDBC sürücüsünü bulduktan sonra ODBC veri kaynağına bağlantıyı kurduk ve bağlantı üzerinden Statement nesnesini oluşturduk. executeUpdate() metodu yerine executeQuery() metodunu yazdığımızda geri dönüş tipinin ResultSet olduğunu görürür. Bu durum, veritabanı sunucusu üzerinde çalışan ifadenin bir değer kümesi döndürebileceğini gösterir. executeQuery metoduna parametre olarak “SELECT 111 112 Bölüm 7 urunID, urunAdi, fiyat from Urun” şeklindeki SQL ifadesini yazdığımızdan dolayı executeQuery metodu Urun tablosundaki bütün verileri sorgulayarak ResultSet cinsinden bir nesne oluşturur ve bu nesnenin veri kaynağına doğrudan bağlı olmasını sağlar. Bu noktadan sonra veri kaynağına bağlı durumda olan ResultSet nesnesi ile veriler üzerine tek yönlü ve salt okunur bir biçimde gezebilirir. Uygulama çalıştığı zaman Örnek Uygulama 7.5’teki gibi bir çıktı alınır: Örnek Uygulama 7.5: 11GB Ram 40.0 23 GHz CPU 80.0 30.0 4256 MB Ekran Karti 20.0 5DVD Writer 45.0 32 GB USB Bellek Parametreli SQL İfadelerinin Çalıştırılması JDBC kütüphanesi komutlarını kullanarak parametreli SQL ifadeleri de çalıştırılabilir. ADO.Net kütüphanesi kullanılarak çalıştırılan SQL sorgularında “@” karakteri ile başlayan parametrelerin tanımlanması gibi JDBC mimarisinde “?” karakteri ile parametreler oluşturulur. ADO.Net’te olduğu gibi parametre adının belirtilmesine gerek yoktur, parametreler sırayla eklenirse sorgu doğru şekilde çalışır. Parametresiz SQL ifadelerinin çalıştırılması için Statement nesnesi kullanılmasına karşın parametreli SQL ifadelerinin çalıştırılması için PreparedStatement nesnesi kullanılır. Daha önce bahsettiğimiz gibi veritabanına gönderilen ve sorguların sınırlandırılmasını sağlayan veriler kullanıcı arayüzünden alınabilir. Kullanıcı tarafından veri girişinin yapıldığı durumlarda kötü niyetli kullanıcıların sisteme zarar vermesini engellemek için parametrik sorguların oluştulması gerekir. Bir SQL ifadesinin parametreli olarak Java ortamında çalıştırılmasını ve sorgu sonucunda dönen değer kümesinin ekranda gösterilmesini sağlayan kod Örnek Uygulama-7.6’da gösterilmiştir. Örnek Uygulama 7.6: 1 import java.sql.Connection; 2 import java.sql.DriverManager; 3 import java.sql.PreparedStatement; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 public class ParametreliSqlSorgusu 7 { 8 public static void main(String[] args) throws ClassNotFoundException, SQLException 9 { 9 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); 10 Connection dbConnection = DriverManager.getConnection(“jdbc: odbc:AlisverisSitesi”); 11 PreparedStatement statement = dbConnection. prepareStatement(“SELECT * FROM Urun WHERE fiyat > ? AND fiyat < ?”); 12 statement.setDouble(1, 20); 13 statement.setDouble(2, 60); JDBC 14 ResultSet resultSet = statement.executeQuery(); 15 while(resultSet.next()) 16 { 17 System.out.println(resultSet.getInt(“urunID”) + “ “ + resultSet.getString(“urunAdi”) + “ “ + resultSet. getDouble(“fiyat”)); 18 } 19 statement.close(); 20 } 21 } Kodun Açıklaması: Select ifadesi iki tane parametre ile sınırlandırılarak belirli bir fiyat aralığındaki ürünlerin getirilmesi sağlanmıştır. PreparedStatement nesnesi, Connection nesnesi tarafından oluşturulur. Select ifadesinin içinde bulunan “?” karakterleri parametrelerin bulunacağı yerlerdir ve bu parametre değerleri PreparedStatement nesnesinin setXxx metodları ile verilir. Örneğimizde iki tane fiyat parametresine değer vermek amacıyla PreparedStatement nesnesinin setDouble metodunu kullandık. setXxx metodlarının ilk parametresi, veritabanına gönderilecek sorgunun içinde bulunan parametresinin indeksini belirtir. “SELECT * FROM Urun WHERE fiyat > ? AND fiyat < ?” sorgusunda ilk parametre değerini 20, ikinci parametre değerini 60 olarak verdik. Bu aşamadan sonra, parametresiz ifadelerin çalıştırılmasında yaptığımız gibi PreparedStatement nesnesinin executeXxx metodlarından birini çalıştırarak çalıştırılacak ifadeyi veritabanı sunucusuna göndeririz. Örnekte Select ifadesi kullandığımız için, dönen veri kümesini ResultSet nesnesi aracılığıyla aldık ve ekrana bastık. Uygulama çalıştırıldığında oluşan ekran çıktısı Örnek Uygulama 7.7’deki gibidir: Örnek Uygulama 7.7: 1 1GB Ram 40.0 3 2 GB USB Bellek 30.0 5 DVD Writer 45.0 Stored Procedure’lerin Çalıştırılması Stored procedure’ler veritabanı sunucuları üzerinde bulunan ve farklı veritabanı yönetim sistemi yazılımlarında farklı biçimlerde oluşturulan prosedürel programlama yapılarıdır. Genelde iş mantığının bir bölümünün uygulamadan alınıp veritabanı sunucusuna aktarılması amacıyla kullanılırlar. Stored procedure yapıları parametre kullanılarak çalıştırıldığı için ve yetkilendirme seviyeleri veritabanı tablolarından farklı olarak ayarlanabildiği için uygulama güvenliğinin arttırılmasını da sağlar. JDBC kütüphanesi ile stored procedure’leri çalıştırmak için Connection nesnesi üzerinden bir CallableStatement nesnesi oluşturulur. Örnek veritabanımızda bulunan ürün ve müşteri tablolarına veri eklemek amacıyla kullanmak için iki tane stored procedure oluşturalım. Örnek Uygulama 7.8: 1 CREATE PROC insUrun 2 @urunAdi VARCHAR(20), 3 @fiyat MONEY 4 AS 5 INSERT INTO Urun (urunAdi, fiyat) 6 VALUES (@urunAdi, @fiyat) 7 GO 113 114 Bölüm 7 8 CREATE PROC insMusteri 9 @ad VARCHAR(20), 10 @soyad VARCHAR(30) 11 AS 12 INSERT INTO Musteri (ad, soyad) 13 VALUES (@ad, @soyad) 14 GO Örnek Uygulama 7.8’de verdiğimiz SQL ifadeleri ile oluşturulan stored procedure’leri çalıştırmak için aşağıdaki gibi bir Java uygulaması oluşturabiliriz. Örnek Uygulama 7.9: 1 import java.sql.CallableStatement; 2 import java.sql.Connection; 3 import java.sql.DriverManager; 4 import java.sql.SQLException; 5 public class StoredProcedure 6 { 7 public static void main(String[] args) throws ClassNotFoundException, SQLException 8 { 9 try 10 { 11 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); 12 Connection dbConnection = DriverManager. getConnection(“jdbc:odbc:AlisverisSitesi”); 13 CallableStatement staInsertUrun = dbConnection.prepareCall(“{call insUrun(?, ?)}”); 14 Notebook”); staInsertUrun.setString(1, “Toshiba M70 15 staInsertUrun.setDouble(2, 1200); 16 int rowCount = staInsertUrun.executeUpdate(); 17 if(rowCount == 0) 18 { 19 20 } 21 else 22 { 23 24 } System.out.println(“Ürün eklenemedi”); System.out.println(“Ürün eklendi”); 25 CallableStatement staInsertMusteri = dbConnection.prepareCall(“{call insMusteri(?, ?)}”); 26 staInsertMusteri.setString(1, “Ahmet”); 27 staInsertMusteri.setString(2, “Balaban”); 28 rowCount = staInsertUrun.executeUpdate(); 29 if(rowCount == 0) JDBC 30 { 31 32 } 33 else 34 { 35 System.out.println(“Müşteri eklenemedi”); System.out.println(“Müşteri eklendi”); 36 } 37 staInsertUrun.close(); 38 staInsertMusteri.close(); 39 } 40 catch(Exception e) 41 { 42 43 44 System.out.println(e.getMessage()); } } 45 } Kodun Açıklaması: Veritabanı bağlantısının sağlanması için gereken ayarları yaptıktan sonra stored procedure çalıştırmak için Connection nesnesi üzerinden iki tane CallableStatement nesnesi oluşturduk. Bu nesnelerden biri insUrun stored procedure’ünün çalıştırılması için, diğeri insMusteri stored procedure’ünün çalıştırılması için gerekli. Stored procedure çağrısının yapılması için “{call sp_name(?, ..)}” ifadesi kullanılır. Bu ifadede sp_name metni yerine çalıştırılacak stored procedure’ün adı yazılır. Stored procedure’ü çalıştırmak için gereken parametreler daha önce yaptığımız gibi Statement nesnelerinin setXxx metodları kullanılarak verilir. insMusteri prosedürünü çalıştırmak için müşterinin ad ve soyad değerlerini, insUrun prosedürünü çalıştırmak için ürünün ad ve fiyat değerlerini parametre olarak verdikten sonra ifadeleri executeUpdate metodu ile çalıştırdık ve etkilenen kayıt sayısını kontrol ederek ürün ve müşteri verilerinin veritabanına eklenip eklenmediği bilgisini ekrana bastık. Uygulama çalıştırıldığı zaman Örnek Uygulama 7.10’da olduğu gibi bir çıktı alınırsa ürün ve müşteri bilgilerini başarıyla ekledik demektir. Örnek Uygulama 7.10: 1 Ürün eklendi 2 Müşteri eklendi Veritabanı Erişimi için Bileşen Oluşturulması: Veritabanı sunucuları üzerinde çalıştırılacak metodlar merkezi bir package altında toplanırsa, veritabanı sunucusu üzerinde herhangi bir değişiklik olması durumunda karşılaşılacak bakım maliyeti düşürülebilir. Bunun yanında uygulamanın geliştirilmesi aşamasında veritabanı konfigürasyonu ve çeşitli komutların veritabanı sunucusu üzerinde çalıştırılması daha kolay bir şekilde gerçekleştirilebilir. Bu amaçla kullanılabilecek temel bir veri erişim bileşeni oluşturalım. 115 116 Bölüm 7 Şimdiye kadar kullandığımız veritabanı erişim yöntemlerini bir sınıf altında toplayabiliriz. Oluşturacağımız bileşenin farklı veritabanı tabloları ile kullanılabilmesi ve geri dönüş değerlerini temel veri tipleri şeklinde döndürebilmesi gerekir. Alt yapıda kullanılan veritabanı sunucusunun değişmesi riskinin olduğu senaryolarda, veritabanı sistemine bağımlı olmayan veri tiplerinin kullanılması büyük avantaj sağlayacaktır. Veri erişim bileşeninin kodu aşağıda verilmiştir. Bileşen ayrı bir Java paketi (package) şeklinde oluşturulmuştur ve bu bileşeni başka bir yerde kullanmak için import anahtar kelimesi ile uygulamaya eklemek gerekir. Örnek Uygulama 7.11: 1 package dbAccess; 2 import java.sql.Connection; 3 import java.sql.DriverManager; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.ResultSetMetaData; 7 import java.sql.SQLException; 8 import java.sql.Statement; 9 import java.util.ArrayList; 10 public class DbConnection 11 { 12 private Connection connection; 13 public DbConnection() throws SQLException, ClassNotFoundException 14 { 15 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); 16 connection = DriverManager.getConnection(“jdbc:odbc: AlisverisSitesi”); 17 } 18 public int NonQuery(String commandText) throws SQLException 19 { 20 Statement stNonQuery = connection.createStatement(); 21 ; int rowCount = stNonQuery.executeUpdate(commandText) 22 stNonQuery.close(); 23 return rowCount; 24 } 25 public int NonQuery(String commandText, Object[] parameterValues) throws SQLException 26 { 27 PreparedStatement pstNonQuery = connection.prepareSt atement(commandText); 28 for (int parameterIndex = 0; parameterIndex < parameterValues.length; parameterIndex++) 29 { JDBC 30 pstNonQuery.setObject(parameterIndex + 1, parameterValues[parameterIndex]); 31 } 32 int rowCount = pstNonQuery.executeUpdate(); 33 pstNonQuery.close(); 34 return rowCount; 35 } 36 public ArrayList ReturnArrayList(String commandText) throws SQLException 37 { 38 ArrayList result = new ArrayList(); 39 Statement stQuery = connection.createStatement(); 40 xt); ResultSet resultSet = stQuery.executeQuery(commandTe 41 ResultSetMetaData metaData = resultSet. getMetaData(); 42 int columnCount = metaData.getColumnCount(); 43 while(resultSet.next()) 44 { 45 String[] row = new String[columnCount]; 46 for(int columnIndex = 0; columnIndex < columnCount; columnIndex++) 47 { 48 row[columnIndex] = resultSet. getString(columnIndex + 1).toString(); 49 } 50 result.add(row); 51 } 52 stQuery.close(); 53 return result; 54 } 55 public ArrayList ReturnArrayList(String commandText, Object[] parameterValues) throws SQLException 56 { 57 ArrayList result = new ArrayList(); 58 PreparedStatement pstQuery = connection.prepareState ment(commandText); 59 for (int parameterIndex = 0; parameterIndex < parameterValues.length; parameterIndex++) 60 { 61 pstQuery.setObject(parameterIndex + 1, paramet erValues[parameterIndex]); 62 63 } ResultSet resultSet = pstQuery.executeQuery(); 64 ResultSetMetaData metaData = resultSet. getMetaData(); 117 118 Bölüm 7 65 int columnCount = metaData.getColumnCount(); 66 while(resultSet.next()) 67 { 68 String[] row = new String[columnCount]; 69 for(int columnIndex = 0; columnIndex < columnCount; columnIndex++) 70 { 71 row[columnIndex] = resultSet. getString(columnIndex + 1).toString(); 71 } result.add(row); 72 73 } 74 pstQuery.close(); 75 return result; 76 } 77 } Kodun açıklaması: Veri erişim mantığını uygulamadan ayırmak için dbAccess isimli bir package içinde DbConnection isimli bir sınıf oluşturduk. Geri dönüşsüz sorguları çalıştırmak için iki tane metod ve değer kümesi döndüren sorguları çalıştırmak için iki tane metod yazdık. İki işlev için de ikişer tane metod yazmamızın sebebi SQL ifadelerini çalıştırırken parametre eklenmesine olan sağlamaktır. Sınıfta bulunan nitelikleri ve metodları kısaca açıklayalım: Connection connection: Veritabanına bağlanmak amacıyla kullanılan nesne. Bütün Statement nesneleri Connection nesnesi tarafından oluşturulur. DbConnection(): DbConnection sınıfının yapıcı metodu. JDBC sürücüsünü bulmak ve Connection nesnesine ilk değerini vermek için kullanılır. int NonQuery(String commandText): Parametresiz ve veri kümesi döndürmeyen SQL ifadelerinin çalıştırılması amacıyla kullanılır. commandText parametresi ile, çalıştırılacak olan SQL ifadesi alınır. Statement nesnesi oluşturulduktan sonra executeUpdate komutu çalıştırılacak işlemden etkilenen kayıt sayısı veritabanı sunucusundan alınır ve döndürülür. int NonQuery(String commandText, Object[] parameterValues): Veritabanı üzerinde çalıştırılacak olan SQL ifadesi commandText parametresi ile, sorgunun içinde bulunan parametrelerin değerleri Object dizisi cinsinden parameterValues parametresiyle alınır. Connection nesnesi üzerinden PreparedStatement nesnesi oluşturulduktan sonra parameterValues dizisinin eleman sayısı kadar parametre setValue(Object) metodu ile PreparedStatement nesnesine eklenir . SQL ifadesi çalıştırıldıktan sonra veritabanında etkilenen kayıt sayısı geri döndürülür. ArrayList ReturnArrayList(String commandText): Veritabanı üzerine çalıştırılan Select ifadesinin sonucunda dönen değer kümesi ResultSet nesnesi üzerinden alınır. ResultSetMetaData nesnesi, veritabanından dönen sonuç kümesinin sütun sayını almak için kullanılır. Veritabanından dönen değerleri uygulamaya göndermek için ArrayList nesnesi kullanılmıştır. ArrayList nesnesinin her bir kaydı, veritabanından gelen bir satır veriyi içerir. Bir veri satırını tutmak için String dizisi kullanılmıştır ve String dizisinin eleman sayısı ResultSetMetaData nesnesinden alınan sütun sayısına göre belirlenir. ResultSet nesnesinin içindeki kayıt sayısı kadar String dizisi oluşturulur ve ArrayList’e eklenir. resultSet.getString() metodu columnIndex+1 parametresiyle çalıştırılır. Bunun sebebi, .Net ortamından farklı olarak parametre ve sütun indekslerinin 1 değerinden başlamasıdır. JDBC ArrayList ReturnArrayList(String commandText, Object[] paramaterValues): Parametreli select ifadelerinin çalıştırılması için bu metod kullanılır. parameterValues Object dizisi aracılığıyla select ifadesinin parametre değerleri alınır ve PreparedStatement nesnesine setObject() metodu yardımıyla eklenir. Bir önceki metod yapısında olduğu gibi ArrayList nesnesi oluşturulur. ArrayList nesnesinin her bir kaydı veritabanından gelen bir satırı tutan String dizisinden oluşur. Örnek Uygulama 7.12: 1 import java.sql.SQLException; 2 import java.util.ArrayList; 3 import dbAccess.DbConnection; 4 public class VeriErisimi 5 { 6 public static void main(String[] args) throws ClassNotFoundException, SQLException 7 { 8 9 DbConnection connection = new DbConnection(); // Yeni bir ürün veritabanına kaydediliyor 10 int rowCount = connection.NonQuery(“INSERT INTO Urun (urunAdi, fiyat) VALUES (‘HP 870cxi Yazıcı’, ‘70’)”); 11 if(rowCount != 0) 12 { 13 14 } 15 else 16 { 17 18 } System.out.println(“Ürün kaydedildi”); System.out.println(“Ürün kaydedilemedi”); 19 // Yeni bir müşteri veritabanına kaydediliyor 20 Object[] musteriInsertParameters = new Object[2]; 21 musteriInsertParameters[0] = “Veli”; 22 musteriInsertParameters[1] = “Karaman”; 23 rowCount = connection.NonQuery(“INSERT INTO Musteri (ad, soyad) VALUES (?, ?)”, musteriInsertParameters); 24 if(rowCount != 0) 25 { 26 27 } 28 else 29 { 30 System.out.println(“Müşteri kaydedildi”); System.out.println(“Müşteri kaydedilemedi”); 31 } 32 System.out.println(); 33 // Veritabanından belirli bir fiyat aralığındaki ürün bilgileri çekilip ekrana basıldı 34 Object[] urunSelectParameters = new Object[2]; 119 120 Bölüm 7 35 urunSelectParameters[0] = 30; 36 urunSelectParameters[1] = 60; 37 ArrayList arrUrun = connection. ReturnArrayList(“SELECT * FROM Urun WHERE Fiyat BETWEEN ? AND ?”, urunSelectParameters); 38 for (Object object : arrUrun) 39 { 40 String[] urun = (String[])object; 41 for(int i = 0; i < urun.length; i++) 42 { 43 44 } 45 System.out.println(); 46 } 47 System.out.println(); System.out.print(urun[i] + “\t”); 48 // Veritabanından bütün müşteri bilgileri çekilip ekrana basıldı 49 ArrayList arrMusteri = connection. ReturnArrayList(“SELECT * FROM Musteri”); 50 for (Object object : arrMusteri) 51 { 52 String[] musteri = (String[])object; 53 for(int i = 0; i < musteri.length; i++) 54 { 55 System.out.print(musteri[i] + “\t”); 56 } 57 System.out.println(); 58 } 59 } 60 } Uygulamada; müşteri kaydı eklenmesi için parametreli NonQuery metodu, ürün kaydı eklenmesi için parametresiz NonQuery metodu, belirli bir fiyat aralığındaki ürünlerin çekilmesi için parametreli ReturnArrayList metodu, bütün müşteri bilgilerinin çekilmesi için parametresiz ReturnArrayList metodu kullanılmıştır. Uygulama çalıştırıldığında aşağıdaki gibi bir ekran çıktısı alınır. Örnek Uygulama 7.13: Ürün kaydedildi Müşteri kaydedildi 11GB Ram 40.0000 22 GB USB Bellek 30.0000 3DVD Writer 45.0000 1Yalçin 2Erkut 3Hasan 4Veli Kaya Kutlar Polat Karaman 8 JSP 8 JSP • JSP Teknolojisinde Kullanılan Etiketler • JSP Direktifleri • JSP Script Etiketleri • Form Verilerinin Kullanılması • Durum Yönetimi JSP Java platformunu kullanarak web uygulamaları geliştirmek için JSP (Java Server Pages) teknolojisi kullanılır. JSP, .Net ortamının web geliştirme teknolojisi olan ASP.Net mimarisinin Java ortamındaki karşılığıdır. ASP.Net uygulamaları geliştirirken kullanılan code-behind mimarisini JSP ortamında doğrudan bir karşılığı olmadığından Java kodları ile HTML kodları aynı sayfanın içinde bulunur. JSP uygulamalarında iş mantığını web sayfalarından ayırmak ve uygulamaları bileşenlere bölmek için Java tabanlı Servlet mimarisi kullanılılır. Bu bölümde JSP uygulaması geliştirme ve Servlet bileşenleri oluşturmayı öğreneceğiz. JSP uygulamalarını ve Servlet bileşenlerini çalıştırmak için aşağıdaki adresten Apache Tomcat sunucusunun işletim sisteminize uygun olan versiyonunu indirebilirsiniz. http://tomcat.apache. org/download-60.cgi http://apache.karegen.com/tomcat/tomcat-6/v6.0.14/bin/apache-tomcat-6.0.14.exe Windows binary formatında indirilen Tomcat sunucusunun kurulumu aşağıda adım adım gösterilmiştir. Kurulum standart Windows uygulaması kurulum biçimindedir. Uygulamayı kurmadan önce sözleşmenin kabul edilmesi gerekir. 124 Bölüm 8 Examples seçeneği işaretlenirse JSP ve Servlet bileşenleriyle ilgili örnek uygulamalar yerel ağ üzerinde çalıştırılabilir. Kurulum için standart dizinden farklı bir dizin de seçilebilir. Uygulamalarda kullanılan Tomcat, standart dizine kurulmuştur. Kurulumun bu aşamasında Tomcat sunucusunun çalışacağı port numarasının belirlenir ve admin kullanıcısı için şifre oluşturulur. Bilgisayarınızın 8080 numaralı portunu kullanan başka bir uygulama yoksa Tomcat sunucusunun 8080 portunu kullanmasını sağlayabilirsiniz. Uygulamalarda kullanılan Tomcat sunucusu 8080 portu üzerinde çalışmaktadır. Admin kullanıcısının şifresinin boş bırakılması genel olarak tavsiye edilmez. Özellikle üzerinde aktif bir biçimde kullanılan uygulamalar çalışan bir sunucunun admin şifresinin boş olması uygulamalarda ve sistemde ciddi güvenlik açıklarına neden olur. JSP Tomcat sunucusu çalışmak Java platformunun 5.0 sürümüne ihtiyaç duyduğundan JRE 5.0 uygulamasının yeri belirtilmelidir. Java motoru standart olarak kurulmuşsa bu dizin otomatik olarak belirlenir, aksi takdirde bilgisayar üzerindeki yerinin manuel olarak belirtilmesi gerekir. Tomcat sunucusunun kurulumu tamamlandı. “Run Apache Tomcat” seçeneği işaretli olarak bırakırsa Tomcat sunucusuna ait Windows servisi çalışır. 125 126 Bölüm 8 Tomcat kurulumu başarılı olduysa ve ilişkili Windows servisi çalıştırıldıysa bilgisayarın sağ alt köşesinde Tomcat’e ait ikon belirir. Tomcat Windows servisi ile ilgili ayarlar bu ikona çift tıklandıktan sonra çıkan pencerede yapılabilir. Tomcat Windows servisi varsayılan olarak manuel çalıştırılacak şekilde ayarlanır. İşletim sisteminin her açılışında Tomcat servisinin otomatik olarak başlaması için servisin Şekil 8’deki gibi ayarlanması gerekir. Tomcat servisinin bilgisayarın her açılışında otomatik olarak çalışması için “Startup Type” seçeneği “Automatic” olarak değiştirilir. Tomcat sunucusunun kurulumu sırasında farklı bir dizin belirtmediyseniz C:\Program Files\Apache Software Foundation\Tomcat 6.0 dizinine kurulacaktır. Bu dizinin altındaki webapps dizininin altında bulunan ROOT dizini JSP uygulamalarının barındırıldığı standart dizindir. Tomcat konfigürasyonunu değiştirmek için Tomcat ana dizininin altındaki conf dizininde bulunan konfigürasyon dosyaları kullanılabilir. Webapps dizininin altında bulunan ROOT dizininin içinde yeni bir dizin oluşturalım ve adını IlkOrnek olarak değiştirelim. Tomcat sunucumuza http://localhost:8080 adresinden, IlkOrnek adını verdiğimiz web uygulamamıza ise http://localhost:8080/IlkOrnek adresi üzerinden ulaşabiliriz. Uygulamamız henüz bir JSP sayfasına sahip olmadığı için web tarayacısı üzerinden bu adrese ulaşmaya çalışırsak hata mesajı alırız. Notepad veya benzer bir metin editörü kullanarak IlkOrnek dizininin içinde boş bir metin dosyası oluşturalım ve adını ilkornek.jsp olarak değiştirelim. Dosyanın uzantısının .jsp olması gerektiğini unutmamalıyız. Örnek Uygulama 8.1’deki kod bloğunu bu dosyanın içine yazarak dosyayı kaydedelim. Örnek Uygulama 8.1: 1 <% 2 out.println(“Ilk Ornek”); 3 %> Tomcat kurulumu başarılı bir şekilde tamamlandıysa web tarayıcısının adres kısmına http://localhost:8080/IlkOrnek/ilkornek.jsp yazıp enter tuşuna bastığımızda Şekil-9’daki gibi bir sayfanın açılması gerekir. JSP Şekil 9 Erişmeye çalıştığımız JSP sayfasının adının küçük-büyük harf duyarlı olduğuna dikkat edelim. http://localhost:8080/IlkOrnek/ilkornek.jsp sayfası yerine http://localhost:8080/IlkOrnek/IlkOrnek. jsp sayfasına ulaşmaya çalışırsak Şekil 10’daki gibi bir hata mesajı alırız. Şekil 10: Küçük-büyük harf duyarlılığından kaynaklanan sayfa bulunamadı hatası. Oluşturduğumuz JSP sayfasının koduna bir göz atarsak <% ve %> etiketlerini görürüz. Bu etiketlerin arasına yazdığımız bütün kod blokları Java kodu olarak yorumlanır JSP script’leri yazmak için kullanılır. İşletim sistemi üzerinde çalışan Java uygulamalarında kullanılan System.out. println() komutu JSP ortamında out.println() şeklini alır ve istemciye (client) metin çıktısı vermek için kullanılır. ROOT dizininin altında JspOrnekleri adında bir dizin oluşturalım ve bundan sonraki örnekleri bu dizinin içine kaydedelim. Java dilinde standart olarak bulunan komutlar JSP sayfaları oluşturulurken de kullanılır. Az önce yazdığımız metni beş defa yazan bir programı for döngüsü kullanarak oluşturalım. Dongu.jsp adında bir jsp sayfasını Notepad kullanarak JspOrnekleri dizinine kaydedelim. Dongu.jsp sayfasını oluşturmak için kullanılan kod aşağıdaki gibidir. Örnek Uygulama 8.2: 1<% 2 for(int i = 0; i < 5; i++) 3 out.println(“Ilk Ornek<br>”); 4%> Kodu incelersek, JSP sayfalarında Java ve HTML kodlarının bir arada bulunaibildiğini görürüz. out.println() komutunun sonunda kullandığımız <br> etiketi çıktının bir alt satıra geçmesini 127 128 Bölüm 8 sağlar ve standart HTML etiketlerindendir. Döngü yapısının Java dilinin standart for döngüsü olduğunu görüyoruz. http://localhost:8080/JspOrnekleri/Dongu.jsp adresi üzerinden sayfayı çalıştırdığımızda Şekil 11’deki gibi bir görünüm elde ederiz. Şekil 11: Döngü kullanımı. JSP ortamı; ASP, PHP, ASP.Net, vb. teknolojileri gibi sunucu tarafında çalışır. Bunun anlamı, JSP kodunun sunucunun üzerinde çalışması ve istemci tarafında HTML kodunun gönderilmesidir. JSP sayfalarına http://Adres/SayfaAdi.jsp şeklinde ulaşılsa da istemci üzerinde çalışan web tarayıcısına saf HTML ve gerekirse Javascript kodu gelir. Web ortamında çalışan bütün sunucu tabanlı teknolojilerin web tarayıcıları tarafından yorumlanabilmesinin sebebi de budur. Dongu. jsp sayfasına sağ tıkladığımızda karşımıza çıkan menüden “view source” komutunu çalıştırırsak bahsedilen özellik daha iyi anlaşılır. İstemci tarafına gelen HTML kodu aşağıdaki gibidir. Ilk Ornek<br> Ilk Ornek<br> Ilk Ornek<br> Ilk Ornek<br> Ilk Ornek<br> Karşımıza çıkan HTML kodunda aynı ifade beş kere yazılmıştır ve ifadeler arasında bulunan <br> etiketinden dolayı çıktı beş satır halinde görünmektedir. Sunucu tarafında çalışan JSP sayfası üzerinde kullanılan döngü ve “<%”, “%>” etiketleri istemci tarafında görünmez. JSP Teknolojisinde Kullanılan Etiketler JSP Direktifleri JSP sayfalarının özelliklerini değiştirmek amacıyla kullanılan yapılardır ve başlarında “@” karakteri bulunur. JSP direktifleri istemciye çıktı göndermez. En çok “Page” direktifi kullanılır. Page direktifi ile sayfanın script dili, durum yönetimi, buffer gibi özellikleri ile ilgili ayarlar yapılır, harici kütüphanelerin sayfa tarafından kullanılması sağlanır. Örneğin; <%@ page session=”true” import=”java.util.*” %> direktifi ile sayfada durum yönetimi etkin hale getirilir ve java.util paketinin içisnde bulunan bileşenler sayfaya eklenerek bu pakette bulunan sınıfların sayfa tarafından kullanılması sağlanır. session=”true” yazmasak bile varsayılan olarak sayfanın session desteği açıktır, kapatmak için session=”false” yazmamız gerekir. JSP JSP Script Etiketleri Script etiketleri, JSP sayfalarında asıl işi yapan etiketlerdir. Sayfada bulunacak Java kodları script etiketleri içine yazılır, sunucu bu etiketler içinde bulunan kodları Java kodu olarak algılar. JSP’de üç çeşit script etiketi bulunur. Tanımlama etiketleri: Java değişkenlerini tanımlamak için kullanılır ve <%! ... oluşturulur. %> şeklinde Scriptlet etiketleri: Java ifadelerinden oluşan kod parçalarını çalıştırmak için kullanılır ve <% ... %> şeklinde oluşturulur. İfade etiketleri: Bütün Java ifadelerini çalıştırmak için kullanılır ve <%= ... %> şeklinde oluşturulur. Bu etiketlerin dışında, JSP sayfaları içinde yorum satırı oluşturmak için <%-- ... --% etiketi kullanılır. Form Verilerinin Kullanılması JSP uygulamalarında sayfalar arasında veri taşımak için HTML standartlarının içinde bulunan POST veya GET metodları kullanılabilir. Herhangi bir HTML veya JSP sayfasının içinde oluşturulan form alanının içinde bulunan kontrollerin verileri GET veya POST işlemi sırasında üzerinde çalışılan sayfaya veya farklı bir sayfaya gönderilir. Basit bir HTML sayfasından bir JSP sayfasına POST metodu aracılığıyla verilerin gönderilmesi işlemi aşağıdaki gibi yapılabilir. FormOrnegi.html sayfasını oluşturan HTML kodu aşağıdaki gibidir. 1<html> 2<body> 3 <form method=”post” action=”FormData.jsp”> 4 <table> 5 <tr> 6 <td>Ad:</td> 7 <td><input type=”text” name=”txtAd” /> </td> 8 </tr> 9 <tr> 10 <td>Soyad:</td> 11 /> </td> <td><input type=”text” name=”txtSoyad” 12 </tr> 13 <tr> 14 <td><input type=”submit” name=”btnGonder” value=”Gonder” /><td> 15 </tr> 16 </table> 17 </form> 18 </body> 19 </html> HTML sayfasında, <body> etiketinin içinde bir form oluşturduk ve POST metodunu kullanacağımızı belirttik. Formun içine eklenen “submit” butonunun tetiklenmesi sonrasında form verileri 129 130 Bölüm 8 FormData.jsp sayfasına gönderilir. Bu işlem action=”FormData.jsp” satırıyla gerçeklenir. Kullanıcının ad ve soyad değerlerini girmesi forma iki tane “text” tipinden input ekledik. Kontrollerin arayüz üzerinde düzgün bir şekilde görüntülenmesi için standart HTML yapılarından biri olan <table> etiketini kullandık. HTML sayfası aşağıdaki gibi görünür. Şekil 12 Kullanıcı tarafından veri girişi yapıldıktan sonra Gönder butonuna tıklandığında form verileri FormData.jsp sayfasına gönderilir. FormData.jsp adında bir JSP sayfası oluşturarak gönderilen verileri alıp işleyebiliriz. 1<%@ page import=”java.util.*” %> 2<% 3 out.print(“Ad:” + request.getParameter(“txtAd”)); 4 ; out.print(“<br>Soyad:” + request.getParameter(“txtSoyad”)) 5%> Request.getParameter metodu, herhangi bir form üzerinden FormData.jsp sayfasına gönderilen verileri almak için kullanılır. txtAd ve txtSoyad anahtarları, FormOrnegi.html sayfasında bulunan text türünden veri giriş elemanlarının isim (name) değeridir. Gönder butonuna tıklandıktan sonra FormData.jsp sayfası yüklenir ve aşağıdaki gibi bir görünüm elde edilir. Şekil 13 HTML sayfasında verileri göndermek için POST yerine GET metodunu kullansak da FormData. jsp sayfasından verilere aynı şekilde ulaşabilir. Bu durumda veriler gizli olarak değil URL üzerinden gönderilecektir. Önemli verilerin GET metodu ile gönderilmesi güvenlik açısından sakıncalıdır. GET metodu kullanıldığında FormData.jsp sayfası aşağıdaki gibi görünür: Adres çubuğunda görünen URL değerine dikkat edersek bütün form verilerinin açık bir şekilde ve saf metin formatında aktarıldığını görebiliriz. JSP Durum Yönetimi HTTP protokolü durum özelliğine sahip değildir. Durum mekanizması, bir kullanıcı sisteme bağlı kaldığı sürece verilerinin birden fazla sayfa arasında taşınabilmesine olanak verir. JSP de diğer sunucu tabanlı web teknolojileri gibi kendine özel bir durum yönetimi mekanizmasına sahiptir. JSP’de durum yönetimi session nesnesiyle sağlanır. Session nesnesine request.getSession() metodu aracılığıyla ulaşılır. Basit bir sisteme giriş sayfasıyla kullanıcının bilgilerini session nesnesine kaydeden ve uygulama içindeki farklı bir sayfadan bu bilgilere ulaşan bir JSP uygulaması yazalım: 1<%--------- KullaniciGirisi.jsp ---------%> 2<html> 3<body> 4<% 5 out.print(“Session ID: “ + session.getId()); 6%> 7<form method=”post” action=”KullaniciGirisi.jsp”> 8 <table> 9 <tr> 10 <td>Ad:</td> 11 <td><input type=”text” name=”txtAd” /> </td> 12 </tr> 13 <tr> 14 <td>Sifre:</td> 15 </td> <td><input type=”password” name=”txtSifre” /> 16 </tr> 17 <tr> 18 <td><input type=”submit” name=”btnGonder” value=”Gonder” /><td> 19 </tr> 20 </table> 21 </form> 22 <% 23 String ad = request.getParameter(“txtAd”); 24 String sifre = request.getParameter(“txtSifre”); 25 if(ad != null && sifre != null) 26 { 131 132 Bölüm 8 27 equals(“1234”)) if(ad.equals(“Yalcin”) && sifre. 28 { 29 session.putValue(“kullaniciAdi”, ad); 30 response.sendRedirect(“SifreDogru.jsp”); 31 } 32 else 33 { 34 hatali”); 35 } 36 } 37 %> 38 </body> 39 </html> out.println(“Kullanici adi veya sifre Kullanıcının kullanıcı adı ve şifre bilgilerini girmesi için KullaniciGirisi.jsp adında bir JSP sayfası oluşturduk. Bir önceki örnekten farklı olarak kullanıcıdan gelecek bilgileri HTML sayfası yerine JSP sayfası üzerinden almayı tercih ettik. Bunun sebebi, kullanıcı giriş sayfasında da JSP kullanmayı planlamamız. Sayfanın başında kullandığımız <% out.print(“Session ID: “ + session.getId()); %> komutuyla o anda geçerli olan oturumun ID değerini kullanıcıya gösterdik. Form etiketinin action özelliğinin KullaniciGirisi.jsp sayfasını gösterdiğine dikkat edelim. Kullanıcı bilgilerini aldığımız sayfayla bu bilgilerin geçerliliğini kontrol ettiğimiz sayfa aynı. Form etiketinden sonra kullanıcının girdiği bilgilerin geçerliliğini kontrol etmek için request.getParameter() metodu ile form verilerini aldık. Kullanıcı adı ve şifre bilgilerini sayfanın içinde kontrol ettik; kullanıcı adı olarak “Yalcin”, şifre olark “1234” değerleri girilmişse sisteme giriş yapılabildiğini varsaydık. Gerçek bir uygulamada kullanıcıdan girilen değerler bir veri kaynağı üzerinden doğrulanır. Eğer kullanıcı sisteme girmeyi başarabilirse kullanıcı adı değeri session nesnesine ekleniyor ve kullanıcı SifreDogru. jsp sayfasına yönlendiriliyor. Sisteme giriş işlemi başarısız olursa “Kullanıcı adı veya şifre hatalı” mesajı veriliyor ve yönlendirme yapılmıyor. KullaniciGirisi.jsp sayfası ilk açıldığında ve şifre yanlış girildiğinde aşağıdaki gibi görünür: Şekil 15: KullaniciGirisi.jsp sayfası ilk defa yüklendi. Şifre yanlış girildi. Sayfanın yeniden yüklenmesi sırasında Session nesnesinin ID değerinin değişmediği görülüyor. JSP Kullanıcı adı ve şifre değerlerinin doğru girilmesi durumunda kullanıcının yönlendirileceği SifreDogru.jsp sayfasının kodu aşağıda verilmiştir: <% out.print(“Kullanici adi: “ + session.getValue(“kullaniciAdi”)); out.print(“<br>Session ID: “ + session.getId()); %> Session nesnesine kaydedilmiş olan kullanıcı adı değeri alınıyor ve ekrana basılıyor. Session değişkeninin ID değeri de aynı şekilde ekrana basılıyor. Kullanıcı SifreDogru.jsp sayfasına yönlendirildiğinde aşağıdaki gibi bir sayfayla karşılaşır: Kullanıcı adı ve şifre doğru girildi, session nesnesinde bulunan kullanıcı adı değeri ve session ID değeri görüntülendi. Bu sayfadaki session ID değeriyle daha önceki sayfalardaki session ID değerinin aynı olduğunu görüyoruz. Session bilgileri bütün oturum boyunca ve uygulamanın bütün sayfaları arasında geçerli olduğundan ID değeri de aynıdır. JSP Uygulamalarında JDBC Kullanımı: JSP uygulamalarının veritabanı sistemleriyle iletişim kurması için Java platformunun JDBC kütüphanesi kullanılır. JDBC kütüphanesi kullanarak bir ürün ekleme sayfası oluşturalım. 1<%@ page import=”java.util.*” %> 2<%@ page import=”java.sql.Connection” %> 3<%@ page import=”java.sql.DriverManager” %> 4<%@ page import=”java.sql.PreparedStatement” %> 5<%@ page import=”java.sql.SQLException” %> 6 7<html> 8<body> 133 134 Bölüm 8 9 <form method=”post” action=”UrunEkle.jsp”> 10 <table> 11 <tr> 12 <td>Urun adi:</td> 13 /> </td> <td><input type=”text” name=”txtUrunAd” 14 </tr> 15 <tr> 16 <td>Fiyat:</td> 17 <td><input type=”text” name=”txtUrunFiyat” /> </td> 18 </tr> 19 <tr> 20 <td><input type=”submit” name=”btnUrunKaydet” value=”Kaydet” /><td> 21 </tr> 22 </table> 23 </form> 24 <% 25 Ad”); String urunAdi = request.getParameter(“txtUrun 26 unFiyat”); String urunFiyat = request.getParameter(“txtUr 27 if(urunAdi != null && urunFiyat != null) 28 { 29 try 30 { 31 valueOf(urunFiyat); double fiyat = Double. 32 JdbcOdbcDriver”); Class.forName(“sun.jdbc.odbc. 33 Connection dbConnection = DriverManager.getConnection(“jdbc:odbc:AlisverisSitesi”); 34 PreparedStatement statement = dbConnection.prepareStatement(“INSERT INTO Urun (urunAdi, fiyat) VALUES (?, ?)”); 35 statement.setString(1, urunAdi); 36 statement.setDouble(2, fiyat); 37 executeUpdate(); int rowCount = statement. 38 statement.close(); 39 if(rowCount == 0) 40 { 41 kaydedilemedi”); 42 } System.out.println(“Ürün JSP 43 } 44 catch(Exception e) 45 { 46 degeri girin<br>”); 47 } 48 } 49 %> 50 </body> 51 </html> out.print(“Fiyat alanina sayi Ürün bilgilerini veritabanına kaydetmek için JDBC kütüphanesini kullandık. Veritabanı üzerinde çalıştırılacak olan sorgunun parametreleri kullanıcıdan alındığından PreparedStatement sınıfı cinsinden bir nesne oluşturarak parametrelerini verdik. Ürün fiyatı alanının cinsi double olması gerektiği için fiyat alanına girilen değeri double’a çevirmeye çalıştık. Uygulamanın akışına göre, fiyat alanının değeri double’a çevrilebilirse sorgu çalıştırılıyor, çevrilemezse kullanıcıya hata mesajı veriliyor. Bir önceki örnekte olduğu gibi formun action değeri, yani post edildiğinde verilerini göndereceği sayfa, yine formun kendisi. UrunEkle.jsp sayfası çalıştırılmadan önce ve hatalı veri girildiğinde aşağıdaki gibi görünür. Şekil 18 Şekil 19: Fiyat alanına double tipine çevrilemeyen veri girildi. Veritabanını sorgulayarak sipariş bilgilerini getirmek için JSP sayfalarımızı oluşturalım. Öncelikle veritabanı sunucumuzun arayüz programını açarak sipariş ve sipariş detayı bilgilerini ekleyelim. INSERT INTO Siparis (musteriID, siparisTarihi) VALUES (1, ‘12/2/2007’) INSERT INTO Siparis (musteriID, siparisTarihi) VALUES (1, ‘10/8/2007’) 135 136 Bölüm 8 INSERT INTO Siparis (musteriID, siparisTarihi) VALUES (2, ‘5/3/2007’) INSERT INTO Siparis (musteriID, siparisTarihi) VALUES (3, ‘5/8/2007’) INSERT INTO SiparisDetay (siparisID, urunID, miktar) VALUES (1, 1, 1) INSERT INTO SiparisDetay (siparisID, urunID, miktar) VALUES (1, 3, 2) INSERT INTO SiparisDetay (siparisID, urunID, miktar) VALUES (2, 7, 1) INSERT INTO SiparisDetay (siparisID, urunID, miktar) VALUES (3, 5, 1) INSERT INTO SiparisDetay (siparisID, urunID, miktar) VALUES (3, 8, 3) INSERT INTO SiparisDetay (siparisID, urunID, miktar) VALUES (4, 3, 1) Verileri veritabanına ekledikten sonra JSP sayfalarımızı oluşturmaya başlayabiliriz. Öncelikle verilen siparişleri görüntülemek için Siparis.jsp adında bir JSP sayfası oluşturalım. 1<%@ page import=”java.util.*” %> 2<%@ page import=”java.sql.Connection” %> 3<%@ page import=”java.sql.DriverManager” %> 4<%@ page import=”java.sql.ResultSet” %> 5<%@ page import=”java.sql.Statement” %> 6<html> 7<body> 8<% 9 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); 10 Connection dbConnection = DriverManager. getConnection(“jdbc:odbc:AlisverisSitesi”); 11 Statement statement = dbConnection. createStatement(); 12 ResultSet resultSet = statement.executeQuery(“SELECT siparisID, siparisTarihi, ad, soyad FROM siparis s JOIN Musteri m ON (s.musteriID = m.musteriID)”); 13 out.println(“Detay bilgilerini ogrenmek istediginiz siparisin tarihine tiklayin<br><br>”); 14 %> 15 <table> 16 <% 17 while(resultSet.next()) 18 { 19 %> 20 <tr> 21 <td width=”100”><a href=”SiparisDetay. jsp?id=<%out.println(resultSet.getInt(“siparisID”)); JSP 22 %>”> 23 <% 24 out.println(resultSet.getDate(“siparisTarihi”). toString()); 25 %> 26 </a></td> 27 28 <% 29 30 %> 31 <td width=”60”> out.println(resultSet.getString(“ad”)); 28 </td> 32 <td width=”60”><% out.println(resultSet. getString(“soyad”)); 33 %> 34 </td> 35 36 <% 27 38 %> 39 40 <% 41 statement.close(); 42 %> 43 </body> 44 </html> </tr> } </table> Kodun açıklaması: Veritabanı bağlantısı oluşturulduktan sonra sipariş bilgilerini çekmek amacıyla bir select sorgusu veritabanı üzerinde çalıştırılır ve geri dönen sonuç kümesi ResultSet nesnesi aracılığıyla alınır. Verilerin düzgün bir biçimde görüntülenebilmesi için <table> yapısı kullanılmıştır. ResultSet nesnesinin her bir kaydı için tabloda bir satır oluşturulmuştur. Bir satır üç sütundan oluşur: İlk sütün siparişin verildiği tarihi içerir ve SiparisDetay.jsp sayfasına bağlantı kurmak amacıyla <a href> etiketinden oluşur. SiparisDetay.jsp sayfasına siparis numarası URL aracılığıyla taşınacağından her bir kaydın siparisID değeri ile <a href> etiketinin URL alanı belirlenir. Bu alanın değeri SiparisDetay.jsp?id=... şeklinde olup “...” ile ifade edilen yere sipariş numarası gelir. Sayfa yüklendiği zaman aşağıdaki gibi görünür. Şekil 20 137 138 Bölüm 8 Sipariş tarihi alanına tıklandığında açılacak olan SiparisDetay.jsp sayfasında seçilen siparişin detay bilgileri veritabanından çekildikten sonra görüntülenecektir. SiparisDetay.jsp sayfasını oluşturan JSP kodu aşağıdaki gibidir. 1<%@ page import=”java.util.*” %> 2<%@ page import=”java.sql.Connection” %> 3<%@ page import=”java.sql.DriverManager” %> 4<%@ page import=”java.sql.ResultSet” %> 5<%@ page import=”java.sql.Statement” %> 6<%@ page import=”java.sql.PreparedStatement” %> 7<html> 8<body> 9<% 10 if(request.getParameter(“id”) == null) 11 12 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); response.sendRedirect(“Siparis.jsp”); 13 Connection dbConnection = DriverManager. getConnection(“jdbc:odbc:AlisverisSitesi”); 14 PreparedStatement statement = dbConnection. prepareStatement(“SELECT urunAdi, fiyat, miktar FROM SiparisDetay sd JOIN Urun u ON (sd.urunID = u.urunID) WHERE siparisID = ?”); 15 statement.setString(1, request.getParameter(“id”)); 16 ResultSet resultSet = statement.executeQuery(); 17 %> 18 <table> 19 <% 20 while(resultSet.next()) 21 { 22 %> 23 <tr> 24 25 <% 26 27 %> 28 </td> 29 30 <% 31 32 %> 33 </td> 34 35 <% 36 37 %> 38 </td> <td width=”200”> out.println(resultSet.getString(“urunAdi”)); <td width=”100”> out.println(resultSet.getDouble(“fiyat”)); <td width=”100”> out.println(resultSet.getInt(“miktar”)); JSP 39 40 <% 41 42 %> 43 44 <% 45 46 %> 47 </body> 48 </html> </tr> } </table> statement.close(); Kodun açıklaması: Sayfanın ilk yüklenmesi sırasında URL aracılığıyla “id” değerinin gelip gelmediği kontrol edilir. Eğer “id” değeri yoksa kullanıcı Siparis.jsp sayfasına yönlendirilir. “id” değeri varsa veritabanı bağlantısı kurulduktan sonra parametreli bir select ifadesi çalıştırmak üzere PreparedStatement nesnesi oluşturulur ve sipariş numarası URL aracılığıyla gelen siparişin detay bilgileri veritabanından çekilir. Geri dönen sonuç kümsi ResultSet nesnesi aracılığıyla bir HTML tablosunda kullanıcıya gösterilir. SiparisDetay.jsp sayfası açıldığında “id” alanının değeri boş değilse aşağıdaki gibi bir sayfa görüntülenir. Şekil 21 139 9 JAVA I/O 9 JAVA I/O • Dosyalar ve Dizinler • FilterInputStream Sınıfları • FileInputStream ve FileOutputStream Sınıflarının Kullanımı • Reader ve Writer Sınıfları: • Standart Giriş-Çıkış Biriminin Kullanılması: • Java ile Ağ Programlama JAVA I/O .NET platformunda olduğu gibi Java platformunda da giriş-çıkış işlemleri Stream adı verilen veri yapıları aracılığıyla yapılır. Veri gönderme ve alma kabiliyetine sahip bütün nesneler Stream olarak tanımlanabilir. Stream yapısının kullanımıyla, giriş-çıkış birimlerinin içinde bulunan veri yazılım ortamından soyutlanmış olur. Java ortamındaki Stream okuma amacıyla kullanılan sınıflar InputStream sınıfından, Stream yazma amacıyla kullanılan sınıflar OutputStream sınıfından türetilirler. InputStream türünden sınıflarda bulunan Read() metodu ile byte bazında okuma yapılabilir. Benzer bir şekilde OutputStream türünden sınıflarda bulunan sınıfların sahip olduğu Write() metodu ile byte bazında yazma işlemi yapılabilir. Stream yapılarını kullanmak için oluşturulmuş olan InputStream ve OutputStream sınıfları temel olarak byte veri tipinden verilerle çalışsa da Java ortamında bulunan farklı sınıflar kullanılarak char veri tipi ile de işlem yapılabilir. Aşağıdaki veri kaynakları kullanılarak Stream üzerinden okuma veya yazma işlemi gerçekleştirilebilir: Byte dizileri Dosyalar String nesneleri Ağ (network) kaynakları Stream yapısının detaylarına girmeden önce dosya sistemi üzerinde duralım. Dosyalar ve Dizinler Okuma ve yazma işlemleri için disk üzerinde bulunan dosyalara erişilmek istendiğinde File sınıfından oluşturulan nesneler kullanılır. File kelimesi sadece dosya kavramını temsil ediyor gibi görünse de dizin kavramını da içerir. Disk üzerinde bulunan dizinlere ve dosyalara erişmek için File nesneleri kullanılır. File nesnesinin bir dosyayı temsil etmesi istendiğinde yapıcı metoduna parametre olarak bu dosyanın yeri, adı ve uzantısı String formatında verilir. Benzer şekilde, File nesnesinin yapıcı metoduna parametre olarak temsil edilecek dizinin yeri ve adı da verilebilir. File nesnesi kullanılarak C:\Program Files\Java\jre1.6.0_02 dizininin içinde bulunan bütün dosya ve dizinler aşağıdaki gibi görüntülenebilir: 1import java.io.File; 2public class Giris 3{ 4 public static void main(String[] args) 5 { 6 File kaynakDizin = new File(“C:\\Program Files\\ Java\\jre1.6.0_02”); 7 String[] dosyalarVeDizinler = kaynakDizin.list(); 8 for (String dosyaVeyaDizin : dosyalarVeDizinler) 9 { 10 11 } 12 } 13 } System.out.println(dosyaVeyaDizin); Kodun açıklaması: File nesnesi oluşturularak C:\Program Files\Java\jre1.6.0_02 dizininin Java programında temsil edilmesi sağlanır. File nesnesinin list() metodu, dizin içinde bulunan bütün dosya ve dizinlerin adını bir String dizisi şeklinde döndürür. 144 Bölüm 9 Uygulama çalıştırıldığında aşağıdaki gibi bir çıktı alınır: bin COPYRIGHT lib LICENSE PATCH.ERR README.txt THIRDPARTYLICENSEREADME.txt Welcome.html Bilgisayarınızda bu dizin bulunmuyorsa File sınıfının yapıcı metoduna başka bir dizin adını parametre olarak verebilirsiniz. C:\Program Files\Java\jre1.6.0_02 dizininin altında bulunan bütün dosya ve klasörlerin görüntülendiğine ve verilen dizin altında bulunan dizinlerin içindeki dosya ve klasörlerin listelenmediğine dikkat edelim. Sadece dosyaların görüntülenmesi için aşağıdaki kod bloğu kullanılabilir: 1import java.io.File; 2public class Giris 3{ 4 public static void main(String[] args) 5 { 6 File kaynakDizin = new File(“C:\\Program Files\\ Java\\jre1.6.0_02”); 7 File[] dosyalarVeDizinler = kaynakDizin.listFiles(); 8 for (File dosyaVeyaDizin : dosyalarVeDizinler) 9 { 10 if(dosyaVeyaDizin.isFile()) 11 { 12 getName()); 13 } 14 } 15 } 16 } System.out.println(dosyaVeyaDizin. Kodun açıklaması: Bir önceki örnekten farklı olarak File nesnesinin listFiles() metodunu kullandık. Bu metod String dizisi yerine File dizisi döndürür. Böylece verilen dizinin altında bulunan bütün dosya ve dizinler birer tane File nesnesiyle temsil edilmiş olur. Döngü içinde bulunan File nesnesinin isFile() metodu ile temsil edilen yapının dosya olup olmadığını kontrol ediyoruz, eğer dosyaysa dosyanın adını ekrana basıyoruz. Sadece dizinleri görüntülemek isteseydik File nesnesinin isDirectory() metodunu kullanabilirdik. Uygulama çalıştırıldığında aşağıdaki gibi bir çıktı alınır: COPYRIGHT LICENSE PATCH.ERR README.txt JAVA I/O THIRDPARTYLICENSEREADME.txt Welcome.html InputStream Çeşitli veri kaynaklarından veri okumak amacıyla aşağıdaki sınıflardan oluşturulan nesneler kullanılabilir. Bu sınıflar InputStream sınıfından türetilmiştir. ByteArrayInputStream: Bellek üzerinde bulunan tampon bölgelerden veri alınması amacıyla kullanılır. StringBufferInputStream: Bir String nesnesinin InputStream nesnesi olarak kullanılmasını sağlar. FileInputStream: Disk üzerinde bulunan dosyaların okunması için kullanılır. SequenceInputStream: Birden fazla InputStream nesnesinin tek bir InputStream nesnesine dönüştürülmesini sağlar. PipedInputStream: PipedOutputStream nesnesine yazılan verileri biçimlendirmek için kullanılır. OutputStream Çeşitli veri kaynaklarına veri yazmak amacıyla aşağıdaki sınıflardan oluşturulan nesneler kullanılabilir. Bu sınıflar OutputStream sınıfından türetilmiştir. ByteArrayOutputStream: Bellek üzerinde buffer bölgesi oluşturulması amacıyla kullanılır. ByteArrayOutputStream üzerine yazılan bütün verilen bellekte bulunan tampon bölgeye yazılır. FileOutputStream: Disk üzerinde bulunan dosyalara veri yazılması için kullanılır. PipedOutputStream: PipedInputStream nesnesine gönderilecek verileri hazırlamak için kullanılır. Bahsedilen Stream sınıflarından başka, InputStream türevi sınıflardan oluşan nesneler aracılığıyla yapılan okuma işleminin veya OutputStream türevi sınıflardan oluşan nesneler aracılığıyla yapılan yazma işleminin özelliklerinin belirlenmesi amacıyla FilterInputStream ve FilterOutputStream sınıfları kullanılır. Byte veri tipinden farklı veri tipleri ile okuma veya yazma yapma, okunacak veya yazılacak verilerin bir tampon bölgede tutulması veya tutulmaması gibi özellikler Filter sınıfları aracılığıyla sağlanır. FilterInputStream ve FilterOutputStream sınıfları soyut olarak oluşturulmuştur, bu sınıflardan türetilen ve yerleşik olarak Java ortamında bulunan sınıflar yardımıyla Stream nesnelerine çeşitli özellikler kazandırılabilir. FilterInputStream Sınıfları Giriş kaynaklarını biçimlendirmek amacıyla kullanılan sınıfların bir kısmı şunlardır: DataInputStream: Farklı veri tiplerinde verilerin okunması amacıyla kullanılır. BufferedInputStream: Her okumada fiziksel kaynağa erişilmesini engeller, okunan verilerin bellekte bulunan bir tampon bölgeye aktarılmasını sağlar. LineNumberInputStream: Giriş kaynağına gelen satır sayısını saklar. Bu sınıfın kullanılması tavsiye edilmez, satır sayısını elde etmek amacıyla kullanılan LineNumberReader sınıfı ileride açıklanacaktır. Bu sınıfların yapıcı metodları parametre olarak InputStream sınıfından türemiş bir sınıftan oluşan bir nesne alır. 145 146 Bölüm 9 FilterOutputStream Sınıfları Çıkış kaynaklarını biçimlendirmek amacıyla kullanılan sınıfların bir kısmı şunlardır: DataOutputStream: Farklı veri tiplerinde verilerin yazılması amacıyla kullanılır. BufferedOutputStream: Her yazma işleminde fiziksel kaynağa erişilmesini engeller, yazılacak verilerin bellekte bulunan bir tampon bölgeye aktarılmasını sağlar. PrintOutputStream: Çıkış kaynağına gönderilen verilerin görsel olarak biçimlendirilmesini sağlar. Bu sınıfların yapıcı metodları parametre olarak OutputStream sınıfından türemiş bir sınıftan oluşan bir nesne alır. FileInputStream ve FileOutputStream Sınıflarının Kullanımı Disk üzerinde bulunan bir dosyanın okunarak içeriğinin disk üzerindeki başka bir dosyaya yazılmasını aşağıdaki kod ile gerçekleştirebiliriz. 1import java.io.FileInputStream; 2import java.io.FileOutputStream; 3import java.io.IOException; 4public class DosyaIslemleri 5{ 6 public static void main(String[] args) throws IOException 7 { 8 FileInputStream okuma = new FileInputStream(“c:\\ okunan.txt”); 9 FileOutputStream yazma = new FileOutputStream(“c:\\ yazilan.txt”); 10 int okunanKarakter; 11 while((okunanKarakter = okuma.read()) != -1) 12 { 13 System.out.print((char)okunanKarakter); 14 yazma.write(okunanKarakter); 15 } 16 okuma.close(); 17 yazma.close(); 18 } 19 } Kodun açıklaması: C sürücüsünde oluşturduğumuz okunan.txt dosyasını okuyarak içeriğini C sürücüsündeki “yazilan.txt” dosyasında kopyalamak istiyoruz. okunan.txt dosyasını aşağıdaki gibi oluşturabilirsiniz: Java ile dosyadan okuma islemi yaptık Dosyadaki verileri okumak için FileInputStream nesnesi, dosyaya yazmak için FileOutputStream nesnesi oluşturduk. FileInputStream nesnemizin read() metodu döngünün her dönüşünde bir byte değerini tam sayı olarak okur, ekranda görüntüler ve yazmak için açtığımız dosyaya yazar. JAVA I/O yazilan.txt isimli dosya disk üzerinde bulunmuyorsa FileOutputStream nesnesi tarafından oluşturulur, bulunuyorsa içi boşaltılır. Uygulama çalıştırıldığında ekranda aşağıdaki gibi bir görüntü oluşur. Türkçe karakterlerin düzgün görünmemesinin sebebi, okunan değerlerin unicode desteğine sahip olmamasıdır. Java ile dosyadan okuma i?lemi yapt?k “yazilan.txt” dosyasının içeriği ise aşağıdaki gibi olur. Türkçe karakterlerin “yazilan.txt” dosyasına düzgün bir şekilde aktarılmıştır. Java ile dosyadan okuma işlemi yaptık Dosyadan okunan karakteri ekrana basmak için tip dönüşümü işlemini kullanarak okunan tam sayı cinsinden verileri karakter tipine çevirdik. Tip dönüşümü yapmasaydık veriler ekrana tam sayı olarak basılırdı. Aşağıdaki veriler, dosyadan okunan karakterlerin tam sayı cinsinden karşılığıdır. Println() metodunu kullanırken araya boşluk bırakmadığımız için bütün sayılar aralıksız gösterildi. Bunun sebebi, boşluk karakterinin de tam sayı cinsinden bir karşılığa sahip olmasıdır. 74971189732105108101321001111151219710097110321111071171099713101 052541081011091053212197112116253107 Okuma veya yazma işlemini tek bir tamsayı ile sınırlandırmak yerine byte blokları şeklinde de yapabiliriz. Bunun için bir tane byte dizisi tanımlanarak read() ve write() metodlarına parametre olarak verilir. Okunan veriler byte dizisine yazılır, yazma işlemi yapılacaksa byte dizisi kullanılarak yapılır. Örnek kod aşağıda verilmiştir. 1import java.io.FileInputStream; 2import java.io.FileOutputStream; 3import java.io.IOException; 4public class DosyaIslemleri 5{ 6 public static void main(String[] args) throws IOException 7 { 8 FileInputStream okuma = new FileInputStream(“c:\\ okunan.txt”); 9 FileOutputStream yazma = new FileOutputStream(“c:\\ yazilan.txt”); 10 byte[] veri = new byte[1000]; 11 int dosyaBoyutu = okuma.read(veri); 12 yazma.write(veri); 13 okuma.close(); 14 yazma.close(); 15 } 16 } 147 148 Bölüm 9 FileInputStream ve FileOutputStream Sınıflarının File Sınıfıyla Birlikte Kullanımı: Duruma göre FileInputStream ve FileOutputStream nesnelerinin yapıcı metodlarına parametre olarak dosya adı yerin File nesneleri de verebiliriz. Uygulamanın kodunun aşağıdaki gibi olması durumunda da ekran çıktısı ve yeni oluşan dosyanın içeriği değişmeyecektir: 1import java.io.File; 2import java.io.FileInputStream; 3import java.io.FileOutputStream; 4import java.io.IOException; 5public class DosyaIslemleri 6{ 7 public static void main(String[] args) throws IOException 8 { 9 File okunanDosya = new File(“c:\\okunan.txt”); 10 txt”); File yazilanDosya = new File(“c:\\yazilan. 11 unanDosya); FileInputStream okuma = new FileInputStream(ok 12 yazilanDosya); FileOutputStream yazma = new FileOutputStream( 13 int okunanKarakter; 14 while((okunanKarakter = okuma.read()) != -1) 15 { 16 System.out.print((char)okunanKarakter); 17 yazma.write(okunanKarakter); 18 } 19 okuma.close(); 20 yazma.close(); 21 } 22 } DataInputStream ve DataOutputStream Sınıflarının Kullanımı: DataInputStream ve DataOutputStream sınıflarından oluşturulan nesneler kullanılarak çeşitli veri tipleri ile okuma ve yazma işlemi yapılabilir. Bu sınıflardan oluşan nesneler ile UTF formatında veri okunabilir ve yazılabilir. UTF formatında veri yazma kabiliyeti, farklı platformlar arasında veri aktarımı yapılacağında kullanılır. Aşağıdaki örnekte, bir DataInputStream ile readChar() metodunu kullanarak karakter bazında okuma yaptıktan sonra DataOutputStream nesnesini kullanarak bu karakterleri dosyaya yazdıracağız. 1import java.io.DataInputStream; 2import java.io.DataOutputStream; 3import java.io.FileInputStream; 4import java.io.FileNotFoundException; 5import java.io.FileOutputStream; 6import java.io.IOException; JAVA I/O 7public class DosyaIslemleri 8{ 9 public static void main(String[] args) throws FileNotFoundException, IOException 10 { 11 FileInputStream okuma = new FileInputStream(“c:\\okunan.txt”); 12 FileOutputStream yazma = new FileOutputStream(“c:\\yazilan.txt”); 13 DataInputStream ozellesmisOkuma = new DataInputStream(okuma); 14 DataOutputStream ozellesmisYazma = new DataOutputStream(yazma); 15 while(ozellesmisOkuma.available() != 0) 16 { 17 readChar(); char okunanKarakter = ozellesmisOkuma. 18 ); ozellesmisYazma.writeChar(okunanKarakter 19 } 20 ozellesmisOkuma.close(); 21 ozellesmisYazma.close(); 22 okuma.close(); 23 yazma.close(); 24 } 25 } Kodun açıklaması: Bir önceki örnekte yaptığımız gibi FileInputStream ve FileOutputStream nesnelerini oluşturduk. Farklı veri tipleri kullanarak okuma yapmak için DataInputStream sınıfını, farklı veri tipleri kullanarak yazma yapmak için DataOutputStream sınıfını kullanacağımızı söylemiştik. DataInputStream sınıfının yapıcı metodu parametre olarak bir FileInputStream nesnesi, DataOutputStream sınıfının yapıcı metodu parametre olarak bir FileOutputStream nesnesi alır. Dosyanın sonuna gelinip gelinmediğini, DataInputStream nesnesinin available() metodu ile anlayabiliriz. Bu metod, DataInputStream nesnesinin bulunduğu yerden dosyanın sonuna kadar okunabilecek byte sayısını döndürür. Bu metodun döndürdüğü değer sıfır olduğunda dosyanın sonuna gelinmiş demektir. Uygulamayı çalıştırdığımızda, “yazilan.txt” dosyasının içeriğinin aşağıdaki gibi olduğunu görürüz. Uygulamayı çalıştırdığımızda, “yazilan.txt” dosyasının içeriğinin aşağıdaki gibi olduğunu görürüz. Java ile dosyadan okuma işlemi yaptık DataInputStream sınıfında bulunan readChar() metodu ile iki byte uzunluğunda okuma yapılır. Aynı şekilde DataOutputStream sınıfında bulunan writeChar() metodu ile iki byte uzunluğunda yazma gerçekleştirlir. Bu durumda dolayı; DataInputStream sınıfından oluşan bir nesne üzerinden okunan verileri FileOutputStream sınıfından oluşturulan bir nesne ile başka bir dosyaya yazarsak veri kaybıyla karşılaşırız. Az önce yazdığımız örneği aşağıdaki gibi değiştirelim: 149 150 Bölüm 9 1import java.io.DataInputStream; 2import java.io.FileInputStream; 3import java.io.FileNotFoundException; 4import java.io.FileOutputStream; 5import java.io.IOException; 6public class DosyaIslemleri 7{ 8 public static void main(String[] args) throws FileNotFoundException, IOException 9 { 10 FileInputStream okuma = new FileInputStream(“c:\\okunan.txt”); 11 FileOutputStream yazma = new FileOutputStream(“c:\\yazilan.txt”); 12 DataInputStream ozellesmisOkuma = new DataInputStream(okuma); 13 while(ozellesmisOkuma.available() != 0) 14 { 15 readChar(); char okunanKarakter = ozellesmisOkuma. 16 yazma.write(okunanKarakter); 17 } 18 ozellesmisOkuma.close(); 19 okuma.close(); 20 yazma.close(); 21 } 22 } Okuma işlemini DataInputStream nesnesi ile, yazma işlemini FileOutputStream nesnesi ile yaptık. Uygulamayı çalıştırdığımızda oluşan “yazilan.txt” dosyasının içeriği aşağıdaki gibidir. aaiedsaa km ilm atk Dikkat edersek, “okunan.txt” dosyasında bulunan verilen birer karakter atlanarak “yazilan.txt” dosyasına yazılmış olduğunu görürüz. Reader ve Writer Sınıfları: Java 1.1 sürümünden itibaren platforma Stream’ler üzerinde okuma ve yazma işlemlerini daha işlevsel hale getirmek için Reader ve Writer sınıfları eklenmiştir. Bu sınıflar byte veri tipi yerine unicode karakter tipini kullanırlar. Karakter tipinden verilerin kullanılması yazılımcının işini birçok durumda kolaylaştırdığı gibi desteklenen karakter formatının unicode olması yazılımın farklı dilleri desteklemesini de sağlar. InputStream ve OutputStream sınıfları aracılığıyla okunan ve yazılan veriler 8 bitlik byte tipi verilerden oluştuğu halde, Reader ve Writer sınıfları 16 bitlik veri desteğine sahiptir. Reader ve Writer sınıfları birçok durumda InputStream ve OutputStream sınıflarının yerine kullanılabilse de byte bazında işlemlerin yapılması gereken senaryolar da olabilir; bu gibi durumlarda InputStream ve OutputStream sınıflarının ve bu sınıfların türevlerinin kullanılması gerekir. Writer türevi sınıflarla Stream yapıları üzerinde append() işlemi yapılabilir. JAVA I/O Stream sınıflarının Reader ve Writer karşılıkları aşağıdaki tabloda gösterilmiştir. Stream sınıfları Reader ve Writer sınıfları InputStream Reader OutputStream Writer ByteArrayInputStream CharArrayReader ByteArrayOutputStream CharArrayWriter FileInputStream FileReader FileOutputStream FileWriter PipedInputStream PipedReader PipedOutputStream PipedWriter StringBufferInputStream StringReader Verileri ve veri aktarma şekillerini biçimlendirmek amacıyla Stream sınıflarının biçimlendirilmesinde kullanılan Filter sınıflarına benzer sınıflar da vardır. Bu sınıflar aşağıdaki tabloda gösterilmiştir. Stream sınıfları Reader ve Writer sınıfları FilterInputStream FilterReader FilterOutputStream FilterWriter BufferedInputStream BufferedReader BufferedOutputStream BufferedWriter DataInputStream - PrintStream PrintWriter LineNumberInputStream LineNumberReader FileReader ve FileWriter Sınıflarının Kullanılması: FileReader ve FileWriter sınıflarının kullanımı, FileInputStream ve FileOutputStream sınıflarının kullanımıyla benzerdir. Aşağıdaki örnekte FileReader nesnesi ile dosya içeriği okunmakta ve FileWriter nesnesi ile diğer dosyaya yazılmaktadır. Okuma işlemi için daha önce oluşturduğumuz “okunan.txt” dosyası kullanılmıştır. 1import java.io.FileNotFoundException; 2import java.io.FileReader; 3import java.io.FileWriter; 4import java.io.IOException; 5public class DosyaIslemleri 6{ 7 public static void main(String[] args) throws FileNotFoundException, IOException 8 { 9 FileReader okuma = new FileReader(“c:\\okunan.txt”); 10 yazilan.txt”); FileWriter yazma = new FileWriter(“c:\\ 11 int okunanKarakter; 12 while((okunanKarakter = okuma.read()) != -1) 151 152 Bölüm 9 13 { 14 System.out.print((char)okunanKarakter); 15 yazma.write(okunanKarakter); 16 } 17 okuma.close(); 18 yazma.close(); 19 } 20 } Uygulama çalıştırıldığında aşağıdaki gibi bir çıktı alınır. FileInputStream sınıfının kullanımında Türkçe karakterler bozuk göründüğü halde FileReader sınıfı kullanıldığında Türkçe karakterler düzgün bir şekilde görünür. “yazilan.txt” dosyası da aynı verileri içerir. Java ile dosyadan okuma işlemi yaptık FileInputStream sınıfından oluşan bir nesne ile bir dosyadaki verileri byte dizisi şeklinde okuyabiliyorduk. FileReader sınıfından oluşan bir nesne ile veriler karakter dizisi şeklinde okunabilir. Örnek kullanım aşağıdaki gibidir: 1import java.io.FileNotFoundException; 2import java.io.FileReader; 3import java.io.FileWriter; 4import java.io.IOException; 5public class DosyaIslemleri 6{ 7 public static void main(String[] args) throws FileNotFoundException, IOException 8 { 9 FileReader okuma = new FileReader(“c:\\okunan.txt”); 10 yazilan.txt”); FileWriter yazma = new FileWriter(“c:\\ 11 char[] veri = new char[1000]; 12 okuma.read(veri); 13 yazma.write(veri); 14 okuma.close(); 15 yazma.close(); 16 } 17 } Uygulama çalıştırıldığında, “yazilan.txt” dosyasının içeriğinin “okunan.txt” dosyasının içeriğiyle aynı olduğu görülür. BufferedReader Sınıfının Kullanımı: BufferedReader sınıfını kullanarak verileri satır bazında okuyabilir ve yazabiliriz. Örnek kullanım aşağıda verilmiştir. 1import java.io.BufferedReader; 2import java.io.FileNotFoundException; JAVA I/O 3import java.io.FileReader; 4import java.io.FileWriter; 5import java.io.IOException; 6public class DosyaIslemleri 7{ 8 public static void main(String[] args) throws FileNotFoundException, IOException 9 { 10 txt”); 11 yazilan.txt”); FileReader okuma = new FileReader(“c:\\okunan. FileWriter yazma = new FileWriter(“c:\\ 12 BufferedReader tamponOkuma = new BufferedReader(okuma); 13 String satir = new String(); 14 String butunVeri = new String(); 15 null) while((satir = tamponOkuma.readLine()) != 16 { 17 18 } 19 System.out.println(butunVeri); 20 yazma.write(butunVeri); 21 tamponOkuma.close(); 22 okuma.close(); 23 yazma.close(); 24 } 25 } butunVeri += satir + “\n”; Daha önceki örneklerden farklı olarak, dosyada bulunan veriler satır satır okunmuştur. BufferedReader nesnesinin readLine() metodu kullanılarak okunan satırlar satir adındaki String değişkenine aktarılır, her satır butunVeri isimli String değişkenine eklenir. butunVeri değişkeni dosyadan okunan bütün metni saklamak amacıyla kullanılmıştır. Uygulama çalıştırıldığında ekranda ve “yazilan.txt” dosyasında aşağıdaki gibi bir görünüm oluşur. Java ile dosyadan okuma işlemi yaptık Okunan satır değerinin butunVeri değişkenine eklenmesi sırasında “\n” karakteri kullanılmasaydı bütün satırlar ekran çıktısında yan yana görünür ve “yazilan.txt” dosyasına yan yana yazılırdı. Bunun sebebi, readLine() metodunun satırı okuduktan sonraki “\n” değerini almamasıdır. “\n” karakterinin kullanımaması durumunda dosyadaki veri aşağıdaki gibi olur: Java ile dosyadan okumaişlemi yaptık 153 154 Bölüm 9 Standart Giriş-Çıkış Biriminin Kullanılması: Konsol üzerinden veri okuma ve veri yazma işlemlerinin yapılması için standart giriş-çıkış birimini oluşturan System.in, System.out ve System.err sınıfları kullanılır. Bu sınıflar, .Net platformundaki Console sınıfının karşılığıdır. Genelde konsole üzerinden yapılan readLine() işleminin tampon bellek bölgesi ile kullanımı uygundur. Konsoldan girilen verileri okuyan ve standart çıkışa yazan bir Java programı aşağıda verilmiştir. Kullanıcı boş satır girdiğinde uygulama sonlanır. 1import java.io.BufferedReader; 2import java.io.IOException; 3import java.io.InputStreamReader; 4public class Konsol 5{ 6 public static void main(String[] args) throws IOException 7 { 8 InputStreamReader giris = new InputStreamReader(System.in); 9 BufferedReader tampon = new BufferedReader(giris); 10 String satir; 11 System.out.println(“Uygulamadan çıkmak için enter tuşuna basın”); 12 System.out.print(“Veri girin: “); 13 satir = tampon.readLine(); 14 while(satir.length() != 0) 15 { 16 satir); System.out.println(“Girdiğiniz veri: “ + 17 System.out.print(“Veri girin: “); 18 satir = tampon.readLine(); 19 } 20 } 21 } Uygulama çalıştırıldığında kullanıcıdan sürekli veri girmesini bekler, kullanıcı Enter tuşuna bastığında uygulama sonlanır. Veri girin metninin doğru yerde görüntülenmesini sağlamak için döngüden önce readLine() metodu bir kere çalıştırılmıştır. Örnek girişlerle aşağıdaki gibi bir çıkış üretilir: Uygulamadan çıkmak için enter tuşuna basın Veri girin: Java ile Girdiğiniz veri: Java ile Veri girin: Giriş-Çıkış Girdiğiniz veri: Giriş-Çıkış Veri girin: İşlemleri Girdiğiniz veri: İşlemleri Veri girin: JAVA I/O Java ile Ağ Programlama: Ağ üzerinde bulunan farklı bilgisayarlar üzerinde çalışan programların haberleşmesi Socket yapıları ile sağlanır. Socket’ler, uygulamaların haberleşmesini sağlayan iletişim noktalarıdır. Sunucu tarafında tanımlanan ServerSocket nesnesi ile herhangi bir istemcinin sunucuya belirli bir port üzerinden bağlanması beklenir, istemci bağlandığında sıradaki işlemler gerçekleştirilir. Sunucu ile istemci arasındaki veri aktarımı Socket arabirimleri üzerinden, Stream yapıları kullanılarak gerçekleştirilir. ServerSocket sınıfı adından dolayı özelleşmiş bir Socket yapısı gibi görünse de, temel işlevi sunucunun istemciden gelen isteği beklemesini ve istek geldiğinde bu isteği kabul etmesini sağlamaktır. İstemci sunucuya bağlandığı zaman ServerSocket nesnesi bir Socket nesnesi oluşturarak döndürür. ServerSocket ve Socket nesneleri kullanılarak ağ üzerinden sunucu-istemci arasında veri aktarımı yapan Java dosyaları aşağıda verilmiştir. Sunucu ve istemci için birer tane Java sınıfı oluşturulmuştur. 1// Sunucu Uygulaması 2import java.io.BufferedReader; 3import java.io.BufferedWriter; 4import java.io.IOException; 5import java.io.InputStreamReader; 6import java.io.OutputStreamWriter; 7import java.net.ServerSocket; 8import java.net.Socket; 9public class Sunucu 10 { 11 IOException public static void main(String[] args) throws 12 { 13 ServerSocket serverSocket = new ServerSocket(60000); 14 bekleniyor”); System.out.println(“İstemcinin bağlanması 15 Socket clientSocket = serverSocket.accept(); 16 kuruldu”); System.out.println(“İstemci ile bağlantı 17 InputStreamReader okuma = new InputStreamReader(System.in); 18 BufferedReader tamponOkuma = new BufferedReader(okuma); 19 OutputStreamWriter yazma = new OutputStreamWriter( clientSocket.getOutputStream()); 20 BufferedWriter tamponYazma = new BufferedWriter(yazma); 21 String veri = new String(); 22 != 0) while((veri = tamponOkuma.readLine()).length() 23 { 24 tamponYazma.write(veri); 155 156 Bölüm 9 25 tamponYazma.write(“\n”); 26 tamponYazma.flush(); 27 veri); System.out.println(“Gönderilen veri: “ + 28 } 29 System.out.println(“Bağlantı kesildi”); 30 serverSocket.close(); 31 clientSocket.close(); 32 okuma.close(); 33 yazma.close(); 34 tamponOkuma.close(); 35 tamponYazma.close(); 36 } 37 } Kodun açıklaması: Sunucu üzerinde tanımlanan ServerSocket nesnesi, daha önce anlatıldığı gibi istemcinin bağlanmasını bekler ve bir istemci bağlanana kadar uygulamanın çalışmasını elinde tutar. İstemci sunucuya bağlandıktan sonra ServerSocket nesnesinin accept() metodu Socket cinsinden bir nesne döndürür. Bu nesne ile sunucu istemciyle iletişim kurar. “okuma” isimli InputStreamReader nesnesi, standart giriş-çıkış biriminden veri almak için kullanılır. Standart giriş-çıkış biriminden alınan verileri satır bazında okumak için BufferedReader nesnesi kullanılmıştır. Benzer şekilde Socket nesnesi üzerinden istemciye veri göndermek için OutputStreamWriter nesnesi oluşturulmuştur. OutputStreamWriter sınıfının yapıcı metoduna parametre olarak Socket nesnesinin getOutputStream() metodundan dönen OutputStream nesnesi verilir. Verileri istemciye satır bazında göndermek için BufferedWriter nesnesi kullanılmıştır. Bir döngü içinde standart giriş-çıkış biriminden okunan veriler BufferedWriter nesnesi aracılığıyla istemciye gönderilir. BufferedWriter nesnesine veriyi yazdıktan sonra flush() komutu çalıştırılır. Bunun sebebi satır bazında gönderilen verinin hemen Stream’e yazılmasını sağlamaktır. Kullanıcı boş satır girdiğinde döngü sonlanır ve bağlantı sunucu tarafından kesilir. 1// İstemci Uygulaması 2import java.io.BufferedReader; 3import java.io.IOException; 4import java.io.InputStreamReader; 5import java.net.Socket; 6public class Istemci 7{ 8 public static void main(String[] args) throws IOException 9 { 10 Socket socket; 11 try 12 { 13 14 } 15 catch(IOException ex) 16 { socket = new Socket(“127.0.0.1”, 60000); JAVA I/O 17 kurulamadı”); System.out.println(“Bağlantı 18 return; 19 } 20 InputStreamReader okuma = new InputStreamReader(socket.getInputStream()); 21 BufferedReader tamponOkuma = new BufferedReader(okuma); 22 kuruldu”); System.out.println(“Sunucu ile bağlantı 23 String veri = new String(); 24 try 25 { 26 null) while((veri = tamponOkuma.readLine()) != 27 { 28 + veri); 29 } 30 System.out.println(“Gelen veri: “ System.out.println(“Bağlantı kesildi”); 31 } 32 catch(IOException ex) 33 { 34 System.out.println(“Bağlantı hatası”); 35 } 36 finally 37 { 38 okuma.close(); 39 tamponOkuma.close(); 40 socket.close(); 41 } 42 } 43 } Kodun açıklaması: İstemci tarafında, sunucu ile iletişim kurmak için bir Socket nesnesi tanımlanır. İstemci üzerinde çalışan uygulamada ServerSocket nesnesi kullanılmamıştır, bunun sebebi istemcinin herhangi bir port üzerinden bağlantı beklememesidir. İstemci sadece sunucuya bağlanmak ve veri almak için tasarlanmıştır. Verilen IP adresine belirtilen port üzerinden bağlantı kurulamazsa IOException oluşur ve program sonlanır. Sunucu çalıştırılmadan istemci çalıştırılırsa bahsedilen hata mesajı alınır. Sunucu uygulamadan Stream üzerinden gelen verileri almak için InputStreamReader nesnesi, gelen verileri satır bazında işleyebilmek içinse BufferedReader nesnesi kullanılmıştır. Sunucu üzerinde çalışan uygulamada olduğu gibi istemcide de InputStreamReader sınıfının yapıcı metoduna parametre olarak Socket nesnesinin getInputStream() metodundan dönen InputStream nesnesi verilir. Sunucuyla bağlantı kurulduktan sonra bir sonsuz döngü içinde sunucudan gelen mesajlar BufferedReader nesnesinin readLine() metodu ile satır bazında alınır ve standart çıkışa yazılır. Bu noktadan sonra IOException oluşursa sunucunun kapandığı anlaşılır ve “bağlantı kesildi” mesajı verilir. 157 158 Bölüm 9 Her iki uygulamada da Stream ve Socket nesnelerini kapatmayı unutmamalıyız. Sunucu ve istemci uygulamalarının farklı şekillerde çalıştırılmaları aşağıdaki gibi çıktılar verir. 1.Sunucu çalıştırılmadan istemci çalıştırıldığında: Bağlantı kurulamadı 2.Sunucu çalıştırıldıktan sonra istemcinin bağlanmasını beklerken: İstemcinin bağlanması bekleniyor 3.İstemci ile sunucu arasında bağlantı kurulduğunda: Sunucu tarafı: İstemcinin bağlanması bekleniyor İstemci ile bağlantı kuruldu İstemci tarafı: Sunucu ile bağlantı kuruldu 4.Sunucudan istemciye mesaj gönderildiğinde: Sunucu tarafı: İstemcinin bağlanması bekleniyor İstemci ile bağlantı kuruldu Java ile Gönderilen veri: Java ile Ağ Programlama Gönderilen veri: Ağ Programlama İstemci tarafı: Sunucu ile bağlantı kuruldu Gelen veri: Java ile Gelen veri: Ağ Programlama 5.Bağlantı kesildiğinde: Sunucu tarafı: İstemcinin bağlanması bekleniyor İstemci ile bağlantı kuruldu Java ile Gönderilen veri: Java ile Ağ Programlama Gönderilen veri: Ağ Programlama Bağlantı kesildi İstemci tarafı: Sunucu ile bağlantı kuruldu Gelen veri: Java ile Gelen veri: Ağ Programlama Bağlantı kesildi 10 Oracle Veritabanı Ailesi 10 Oracle Veritabanı Ailesi • Oracle Üzerinde Uygulama Geliştirme • Oracle Veri Tabanı Sisteminin Kurulması • Tablespace • Undo Tablespace • Oracle’da Veri Tabanı Oluşturmak • Temel Veri Tipleri Oracle Veritabanı Ailesi Oracle Personal Edition: Microsoft SQL Server 2005’teki Express Edition gibi düşünülebilir. Kişisel veritabanıdır. Uygulama geliştirme için kullanılır. Oracle Standard Edition: Giriş seviyesinde çok kullanıcılı sürümdür. Microsoft SQL Server 2005 Standard Edition’a karşılık olarak düşünülebilir. Oracle Enterprise Edition: En geniş kapsamlı sürümdür. Microsoft SQL Server 2005 Enterprise Edition’a karşılık olarak düşünülebilir. Oracle Lite: Mobil uygulamalar için kullanılır. Oracle Üzerinde Uygulama Geliştirme Oracle üzerinde uygulama geliştirme için kullanılacak çeşitli diller ve ortamlar mevcuttur. Bunlar SQL, PL/SQL, Java olarak sayılabilir. Bunların dışında .NET, C, C++ gibi dil ve ortamlardan erişim yapılabilir. PL/SQL: Microsoft SQL Server’daki T-SQL’e karşılık olarak düşünülebilecek olan prosedürel SQL genişletmesidir. Oracle üzerinde PL/SQL kullanılarak SP (stored procedure), trigger gibi nesneler oluşturulabilir. T-SQL’deki gibi değişkenler, döngüler ve koşullar PL/SQL’de de mevcuttur. SQL Plus arayüzü kullanılarak ya da TOAD ile PL/SQL yazılabilir. Java: Microsot SQL Server üzerine .NET ile nesnelerin yazılmasına karşılık olarak, bunun için Oracle tarafında Java kullanılır. Oracle Veri Tabanı Sisteminin Kurulması 1� Öncelikle, eğer değilse, bilgisayarınızın bölgesel ayarlarını İngilizce olarak değiştirmeniz gerekir. Bunun için, Start > Control Panel > Regional and Language Options’ tan (Başlat > Denetim Masası > Bölge ve Dil Seçenekleri) English (United States) (İngilizce (A.B.D.)) olarak belirleyin. 162 Bölüm 10 2. Daha sonra, Advanced (Gelişmiş) sekmesinde kullanılan dil seçeneği olarak yine English (United States) (İngilizce (A.B.D))‘yi seçin ve tamam butonuna tıklayın. 3� Tamam butonuna tıkladığınızda, gerekli dosyaların bilgisayarınızda yüklü olduğunu ve Setup’ın (Kur) dosyaları buradan kurabileceğini belirten bir uyarı ile karşılaşabilirsiniz. Bu durumda, Yes (Evet) seçeneğini tıklayın. Ardından Windows bilgisayarınızı yeniden başlatmak isteyebilir. Bu durumda yine Yes (Evet) seçeneğini tıklayarak bilgisayarınızı yeniden başlatın. 4. Ardından Oracle10g kurulu dosyalarının bulunduğu klasöre giderek setup.exe’yi çalıştırın. Karşınıza ilk olarak daha önce yüklenmiş bir Oracle ürünü olup olmadığını kontrol eden bir işlem penceresi gelecektir. Oracle Veritabanı 5. Kontol işleminin ardından gelen ekranda yükleme seçeneği olarak Basic Installation ve Advanced Installation olmak üzere iki yöntem karşınıza çıkacaktır. Bunlardan Basic Installation seçeneğini seçin. Böylelikle birçok ayarın otomatik olarak yapılması sağlanacaktır. Oracle Home Location: Veritabanının kurulacağı klasörün yerini belirtir. Installation Type: Kurulum tipini belirtir. Standart ya da Enterprise Edition olarak belirlenir. Create Starter Database: Bu seçenek işaretlendiğinde kurulum işlemi gerçekleştikten sonra veritabanı da oluşturulur. Aksi halde sadece Oracle 10g kurulumu gerçekleştirilecek, veritabanı yaratmak için SQL-Plus altından Create Database komutu ile veritabanı oluşturmak gerekecektir. Bu seçeneğin işaretli olmasına dikkat edin. Global Database Name: Veritabanının adı girilir. Database Password: Veritabanı şifresi girilir. Bu bölümde girilen şifre kesinlikle unutulmamalıdır. Oracle’da en üst seviyedeki admin kullanıcısı “sys” olarak hazır gelmekte ve burada belirlenen şifre bu kullanıcının şifresi olarak belirlenmektedir. Confirm Password: Girilen şifre tekrar teyit amacıyla bu bölüme girilir. Girişleri tamamladıkan sonra Next butonuna tıklayın. 1� Karşınıza gelen pencerede, kurulum sırasında yaptığınız seçimleri özet olarak görebilirsiniz. Değişiklik yapmak isterseniz Back butonuna tıklayarak geri dönebilirsiniz. Daha sonra Install butonuna tıklayarak devam edin. 163 164 Bölüm 10 2. Kurulum işlemi otomatik olarak başlayacaktır. 3� Kurulum sırasında belirlenen ayarlar çerçevesinde konfigürasyon işlemleri otomatik olarak gerçekleştirildikten sonra işlemlerin özeti görüntülenir. Yine Next butonuna tıklayın. Oracle Veritabanı 4. Veritabanı otomatik olarak oluşturulur. 5. Daha sonra, oluşturulan veritabanı ile ilgili bilgilerin özetlendiği ve şifre ayarlarının yapılabildiği pencere gelir. 165 166 Bölüm 10 6. Özet penceresinde yer alan Password Management butonuna tıklandığında açılan ekrandan, varsayılan kullanıcılara ilişkin şifre değişiklikleri ve kilitli/kilitsiz ayarları yapılabilir. 7. Belirtilen ayarların yapılmasından sonra, kurulum tamamlanır. Exit butonuna tıklayarak çıkın. 8. Açılan browserdan Oracle Enterprise Manager’a kullanıcı adı ve şifre ile giriş yapabilirsiniz. Oracle Veritabanı Tablespace Microsoft SQL Server’da data file olarak geçen MDF ve NDF uzantılı dosyalar Oracle’da tablespace’lerde tutulur. system Tablespace system tablespace’i Oracle’ın veri sözlüğünü barındırır. Bu da Microsoft SQL Server’daki master veritabanındaki metadata tutan sys ile başlayan tablolar gibi düşünülebilir. sysaux Tablespace Eskiden system tablespace’te yer alan birtakım veriler ve özellikler 10g ile birlikte sysaux tablespace’ine alınmıştır. Oracle sisteminin kullandığı bir tablespace olarak düşünülmelidir. Default Temporary Tablespace Default Temporary Tablespace ilgili veritabanında çalıştırılan sorgu için bellek alanı yetersiz geliyorsa disk üzerinde bellek alanı gibi kullanılır. Windows ve Linux gibi işletim sistemlerinde kullanılan swap alanı gibi düşünülebilir. Undo Tablespace Microsoft SQL Server’daki Transaction Log Space gibi düşünülebilir. Herhangi bir transaction başladığında rollback ya da commit edilene kadar undo tablespace’te eski veri tutulur. Arka Planda Çalışan Processler Veritabanı sisteminin düzenli çalışabilmesi için arka planda çalışan birtakım process’lere ihtiyaç vardır. Oracle’da çalışan temel arka plan process’leri şunlardır: dbwr – Database Writer Process: Tampon bölgelere alınan verilerin veritabanına yazılmasından sorumludur. lgwr – Log Writer Process: Online redo logları bellekte geçici olarak tutulur. Bu log’ların veritabanına aktarılmasından log writer process sorumludur. ckpt – Chekpoint Process: Checkpoint işlemi bellekteki verilerin veritabanı üzerinde uygun bölümlere yazılmasından sorumludur. Checkpoint process database writer ve log writer’ı tetikleyebilir. 167 168 Bölüm 10 smon – System Monitor Process: System monitor process veritabanlarının bütünlüğü ve tutarlılığından sorumludur. pmon – Process Monitor: .NET ortamındaki garbage collector gibi düşünülebilir. Genel olarak kaynak yönetimi yaptığını söyleyebiliriz. Oracle’da Veri Tabanı Oluşturmak Oracle’da veri tabanı oluşturmak için kullanılabilecek en kolay yol Oracle Database Configuration Assistant (DBCA) kullanmaktır. DBCA’e Start > Programs > Oracle Oracle Home > Configuration and Migration Tools > Database Configuration Assistant yoluyla ulaşılır. Program açılınca bir sihirbaz aracılığıyla veri tabanını yapılandırabiliriz. Şekil 1: DBCA açılış ekranı. Sihirbazın ilk adımı sadece bilgilendirici bir ekrandır. Bu adımda Next butonuna tıklayarak devam edebiliriz. Bu adımdan sonra veri tabanını oluşturmak için önümüze çıkan kavramları, opsiyonlarla birlikte değerlendireceğiz. Sihirbazın ikinci adımında veri tabanı yönetiminde ne yapacağımızı belitmemiz gerekir. Burada ilk seçenek olan Create a Database (Veri tabanı oluştur) seçeneğini seçeceğiz. Diğer opsiyonlar ve açıklamaları da aşağıdaki gibidir; fakat sihirbazı bu seçeneklere göre ilerletmeyeceğiz. Opsiyon Açıklama Create a Database Sihirbazla adım adım veritabanı oluşturmak için kullanılır. Varolan veri tabanı şablonları kullanılabildiği gibi şablonlardan farklılaştırarak da veri tabanı oluşturulabilir. Configure Database Options Var olan bir veri tabanının dedicated (adanmış) bir sunucudan Shared (paylaşılan) bir sunucuya taşınması için gerekli işlerin yapılmasını sağlar. Delete a Database Var olan bir veri tabanının tamamen silinmesini sağlar. Manage Templates Veritabanı şablonlarının yönetilmesini sağlar. Şablonlar yerel diskte XML formatında saklanmaktadır. Mevcut şablonlarda değişiklik yapılabilir ya da yeni bir şablon oluşturulabilir. Oracle Veritabanı Şekil 2: DBCA ilk kurulum ekranı. Şekil 3: DBCA veri tabanı şablonu seçme ekranı. Şekil 3’deki veri tabanı şablonu seçim ekranında yeni oluşturulacak veri tabanının yapısı belirlenir. Herhangi bir şablonu seçerek sağ alt köşedeki Shoe Details butonuna tıklayarak şablonun alt yapısı hakkında bilgi alabilirsiniz, ancak bu aşamada kavramlar karmaşık gelebilir. Her şablon için genel veri tabanı özellikleri, başlangıç parametreleri, karakter setleri, veri dosyaları (tablespace’ler), kontrol dosyaları ve redo log grupları hakkında bilgileri bu pop-up’tan görebilirsiniz; ancak mevcut şablonların yapıları üzerinde değişiklik yapamazsınız. Burada hazır olarak göreceğiniz şablonlar Data Warehouse, General Purpose ve Transaction Processing’tir. Burada General Purpose seçeneğini seçip Next butonuna tıklayarak devam edebiliriz. 169 170 Bölüm 10 Şekil 4: DBCA veri tabanı isimlendirme ekranı. Sihirbazın 3. adımında Şekil 4’te gördüğümüz isimlendirme ekranı gelir. Bu ekranda veri tabanı adı ve SID (system identification)’si tanımlanır. Veri tabanı adı yazarken SID’de de aynı karakterlerin yazıldığını göreceksiniz. Bir sistemde aynı isimde birden fazla veri tabanı olabilirken aynı SID’ye sahip birden fazla veri tabanı bulunamaz. Aynı zamanda SID’nin maksimum uzunluğu 8 karakterdir. SID kısmına 8 karakterden daha uzun bir ifade yazsanız da SID 8 karaktere kısaltılacaktır. Bu ekranda veri tabanı adına ve SID’ye “ornek” yazıp Next butonuna tıklayarak devam edebiliriz. Şekil 5: DBCA yönetim seçenekleri ekranı. Oracle Veritabanı Şekil 5’teki yönetim seçenekleri ekranında veri tabanının Enterprise Manager (EM) ile kontrol edilip edilemeyeceği seçeneği ile birlikte uyarıların e-mail ile gönderilmesi ve backup seçenekleri tanımlanır. Bu ekranda Configure the Database with Enterprise Manager seçeneğeni işaretli olarak bırakalım. Eğer makinenizde grid kurulumu varsa ilk opsiyon olan Use Grid Control for Database Management opsiyonu da açık olacaktır. Diğer opsiyon olan Use Database Control for Database Management seçeneği mecburen seçili olarak kalarak devam edecektir. Enable Email Notifications ve Enable Daily Backup seçeneklerini işaretlemeden devam edebiliriz. Şekil 6: DBCA şifre tanımlamaları ekranı. Şekil 6’daki şifre tanımlama ekranında SYS, SYSTEM, DBSNMP ve SYSMAN kullanıcıları için şifre tanımlamaları yapmamız gerekir. Burada 2 seçenek mevcuttur. Birincisi varsayılan olarak seçili gelen Use the Same Password for All Accounts seçeneğidir. Bu seçenekte yukarıda adı geçen 4 sistem kullanıcısı için de yazılan aynı şifre geçerli olur. Eğer veri tabanını yönetirken bu 4 hesabı tek kişi kullanıyorsa hepsi için tek şifre tanımlamak daha uygun olacaktır. Diğer seçenek olan Use Different Password seçilirse 4 kullanıcının hepsi için farklı farklı şifreler tanımlanabilir. Bu noktada adı geçen kullanıcıların özelliklerini listeleyelim: Kullanıcı Özellik SYS SYS kullanıcısı veri sözlüğünü oluşturan tüm iç Oracle tablolarının sahibidir. SYS kullanıcısıyla hiçbir işlem yapılmaması için bu hesabın kilitlenmesi tercih edilmelidir. SYS kullanıcısının sahibi olduğu nesnelerde de değişiklik yapılmamalıdır. SYSTEM SYSTEM kullanıcısı birtakım yönetimsel tabloların ve view’ların sahibidir. Yetkisiz kullanım ihtimaline karşı kilitlenmesi ve kullanılmaması tercih edilmelidir. DBSNMP Veri tabanı hakkında performans istatistiklerini toplamak ve görüntülemek amacıyla Enterprise Manager tarafından kullanılan kullanıcıdır. SYSMAN Enterprise Manager’da SYS kullanıcısıyla aynı haklara sahiptir. Bu ekranda Use the Same Password for All Accounts seçiliyken şifreyi 2 defa yazıp Next butonuna tıklayarak devam edebiliriz. Şekil 7’deki saklama seçenekleri ekranında 3 seçenek söz konusudur. Bu ekranda dikkat edeceğimiz bir nokta da bu aşamada Finish butonuna tıklayarak veri tabanı oluşturma işlemini tamamlayabilecek olmamızdır. Bundan sonraki sihirbaz ekranı buradaki seçime göre değişir. Bu seçeneklerin özellikleri şu şekildedir: 171 172 Bölüm 10 Şekil 7: DBCA Diskte saklama seçenekleri ekranı. Saklama Seçenekleri Özellik File System En çok kullanılan seçenektir. Veri tabanının disk üzerinde normal bir veri tabanı gibi oluşturulmasını sağlar. ASM Storage ASM (Automatic Storage Management) için öncelikle CSS (Oracle Cluster Synchronization Service) kurulumu yapılması gerekir. Disk yönetimini File System seçeneğine göre kolaylaştırır. Birçok işi veri tabanı yöneticisi yerine Oracle kendisi yapar. Raw Devices Veri tabanı dosyalarını işletim sistemi yerine donanım aracılığıyla doğrudan Oracle yönetir. RAC (Real Application Cluster) veri tabanları için paylaşımlı alan olarak kullanılabilir. Bu adımda File System seçeneğini işaretleyip Next butonuna tıklayarak devam edebiliriz. Şekil 8’deki veri tabanı dosyaları ekranında tanımladığımız veri tabanına ait fiziksel dosyaların nereye konulacağı belirtilir. Bu adımda 3 seçenek mevcuttur. Bu seçeneklerin açıklamaları şu şekilde özetlenebilir: Seçenek Açıklama Use Database File Locations From Template Daha önce seçilen şablona göre veri tabanı dosyalarının yerleri belirlenir, sonradan değiştirilebilir. Use Common Location fo All Database Files Veri tabanı dosyalarının yeri için farklı bir klasör tanımlamak için kullanılır, belirlenen klasör sonradan değiştirilebilir. Use Oracle-Managed Files Diskte saklama seçeneklerinde ASM seçildiyse bu seçenek seçilmelidir. Bu seçenek seçildiğinde dosyaların yerleri veya adları ile ilgili sonradan yapılamaz. Oracle Veritabanı Şekil 8: DBCA dosya konumlandırma ekranı. Bu adımda Use Database File Locations From Template seçeneğini seçip Next butonuna tıklayarak devam edebiliriz. Şekil 9: DBCA kurtarma yapılandırması ekranı. Şekil 9’daki kurtarma yapılandırası ekranı backup ve kurtarma işlemlerinin yapılandırılması için kullanılır. Bu aşamada 2 seçenek mevcuttur ve her ikisi bir arada da kullanılabilir seçeneklerdir. İlk seçenek olan Specify Flash Recovery Area seçildiğinde ilgili dosyaların nerede saklanacağı ve bu alanın MB cinsinde büyüklüğü tanımlanır. Bu alan tercihen fiziksel olarak veri tabanı dosyalarının bulunduğu yerden farklı olmalıdır. Diğer seçenek olan Enable Archiving seçeneği seçili olduğunda arşiv loglaması yapacağımızı ifade eder. DBCA kurtarma yapılandırması ekranında 173 174 Bölüm 10 sadece Specify Flash Recovery Area seçeneğini işaretleyip Next butonuna tıklayarak devam edebiliriz. Şekil 10: DBCA veri tabanı içeriği ekranı, Database Components sekmesi. Şekil 10’daki veri tabanı içeriği ekranının Database Components sekmesinde, Oracle Data Mining, Oracle Text, Oracle OLAP gibi Oracle veritabanı bileşenlerinin hangilerinin kurulan veritabanı için kullanılacağı ve hangi tablespace üzerine yerleştirileceği belirlenir. Bu sekmede sample Schemas seçeneğini işaretlemeden Custom Scripts sekmesine geçelim. Şekil 11: DBCA veri tabanı içeriği ekranı, Custom Scripts sekmesi. Veri tabanı içeriği Custom Scripts sekmesinde, veri tabanı oluşturulduktan sonra üzerinde çalışmasını istediğimiz bir SQL script dosyası varsa bu dosyayı seçerek birtakım kurulumların oto- Oracle Veritabanı matik olarak yapılmasını sağlayabiliriz. Bu ekranda No scripts to run seçeneğini işaretleyip Next butonuna tıklayarak devam edebiliriz. Şekil 12: DBCA başlangıç parametreleri, Memory sekmesi. Şekil 12’deki başlangıç parametreleri, Memory sekmesinde oluşturduğumuz veritabanı için fiziksel bellek alanı üzerinde ne kadar yer kullanılacağını tanımlayabiliriz. Typical– Allocate memory as a percentage of total physical memory seçeneği ve Custom seçeneği bulunur. Eğer 1.seçenek seçilirse bellek yönetimini Oracle kendisi yapar. Diğer seçenekte ise bellek yönetiminde SGA (System Global Area) ve PGA (Process Global Area) için bellekte ne kadar yer ayıracağımızı belirtebiliriz. Bu sekmede 1.seçeneği işaretleyerek Sizing sekmesine geçelim. Şekil 13: DBCA başlangıç parametreleri, Sizing sekmesi. 175 176 Bölüm 10 Şekil 13’deki başlangıç parametreleri, Sizing sekmesinde Microsoft SQL Server’daki veri sayfasına (data page) karşılık gelen blok boyutu girilebilir. Transactional veri tabanları için bu boyut genellikle 8KB’dir. Data warehouse için 16KB ve üstü düşünülmelidir. Processes’de belirtilen sayı işletim sisteminden bu veritabanına açılabilecek maksimum eş zamanlı process sayısını belirtir. En başta belirtilen arka planda çalışan process’ler için en az 6 olarak tanımlanmalıdır. Burada belirtilen sayı büyüdükçe bellekte SGA olarak bu veri tabanı için ayrılacak alan büyüyecektir. Buradaki sayıyı da 150’de bırakarak Character Sets sekmesine geçelim. Şekil 14: DBCA başlangıç parametreleri, Character Sets sekmesi. Şekil 14’teki başlangıç parametreleri, Character Sets sekmesinde yerelleştirme ayarları yapılır. Bu ayarları Microsoft SQL Server’daki collation yapılandırması gibi düşünebiliriz. Use the default seçeneği işletim sisteminde o andaki seçili karakter setine göre, Use Unicode, Unicode’a göre, Choose from the list of character sets’de seçeceğimiz karakter setine göre veri tabanını yapılandırır. Bu 3’lü seçimin altında bulunan National Character Set Unicode olarak tanımlanmayan karakter setine alternatif Unicode karakter setinin eklenmesini sağlar. Default Language, tarih, AM, PM gibi yerel ayarlar ve ORDER BY ile sıralama yapısının hangi dile göre yapılacağını belirler. Default Date Format ise tarih verilerinin hangi ülkeye, dile göre gösterileceğini tanımlar. Bu sekmede sadece Use the Default seçeneğini seçip, diğer hiçbir seçeneği değiştirmeden Connection Mode sekmesine geçelim. Şekil 15’teki başlangıç parametreleri, Connection Mode sekmesinde Oracle veri tabanı sunucusuna bağlantı şekli belirtilir. Burada 2 seçenek mevcuttur. Dedicated Server Mode seçildiğinde veri tabanına bağlanan her istemci için ayrı bir kaynak ayrılır. Eğer veri tabanına bağlanan kullanıcı sayısı çok değişken değil ve az sayıdaysa ve bağlanan kullanıcılar çok kısa sürede bağlantılarını sonlandırmıyorlarsa bu seçenek seçilmelidir. 2. seçenek olan Shared Server Mode ise bir önceki seçenek için tanımlanan durumların dışında kalan uygulamalar için idealdir. Bu seçenekte kaynakların ortak kullanımı söz konusu olduğundan ne kadar ortak server process’inin oluşturulacağı tanımlanmalıdır. Genellikle çoğu OLTP veri tabanı için Shared Server Mode, çoğu OLAP veri tabanı için de Dedicated Server Mode daha doğru seçim olacaktır. Bu sekmede Shared Server Mode seçeneğini işaretleyip, Shared Server sayısını 1 bırakıp (bu veri tabanı oluşturma işlemi örnek olduğu için 1’de bıraktık, bu sayı veri tabanının kullanım yoğunluğuna ve gelen istemlerin büyüklüklerine göre ayarlanmalıdır) Next butonuna tıklayarak devam edebiliriz. Oracle Veritabanı Şekil 15: DBCA başlangıç parametreleri, Connection Mode sekmesi. Şekil 16: DBCA veri tabanı saklama yapısı ekranı. Şekil 16’daki veri tabanı saklama yapısı ekranından daha önce belirlediğimiz (örnek şablona göre seçtiğimiz için tek tek tanımlamadık) veri tabanına yazılacak dosyaların yerleri ve isimleriyle ilgili değişiklikleri yapabilmemizi sağlar. Bu ekrandan soldaki ağaç yapısından kontrol dosyaları, veri dosyaları ve log dosyalarının yerlerini ve yapıları görüntülenip değiştirilebilir. Burada herhangi bir değişiklik yapmadan Next butonuna tıklayarak devam edelim. Şekil 17’deki veri tabanı oluşturma ekranı DBCA sihirbazının son ekranıdır. Bu ekranda 3 seçenek mevcuttur. Create Database seçeneği tanımladığımız veri tabanının fiziksel olarak oluşturulmasını sağlar. Save as a Database Template seçeneği yapılandırdığımız veri tabanı yapısının şablon olarak saklanmasını sağlar. Generate Database Creation Scripts seçeneği de yapılandırdığımız 177 178 Bölüm 10 veri tabanı yapısnın script’inin oluşturulmasını sağlar. Bu ekrandaki seçeneklerini herhangi birini, ikisini ya da hepsini seçebiliriz. Sadece Create Database seçeneğini seçerek Finish butonuna tıklayarak tanımladığımız veri tabanını oluşturalım. Şekil 17: DBCA veri tabanı oluşturma ekranı. Sihirbaz veri tabanını oluşturmadan önce tüm parametreleri özet şeklinde çıkarır. Bu ekran üzerinden değişiklik yapılamaz. Save as an HTML file seçeneğiyle bilgi olarak saklamak faydalı olabilir. OK butonuna tıkladığımızda veri tabanı oluşturulmaya başlayacaktır. Şekil 18: Veri tabanı parametreleri ekranı. Oracle Veritabanı Şekil 19: Veri tabanı oluşturma ekranı. Şekil 19’da veri tabanının oluşturulma sürecini görebilirsiniz. Böylece Oracle üzerinde örnek veri tabanı oluşturma işlemini tamamlamış olduk. Temel Veri Tipleri varchar2: 4000 taneye kadar karakter tutabilen veri tipidir. Microsoft SQL Server’daki varchar veri tipi gibi düşünülebilir. Eğer saklanacak verinin başında ya da sonunda boşluk (space) karakteri varsa bunları silerek tutar. nvarchar2: 4000 taneye kadar Unicode karakter tutabilen veri tipidir. Microsoft SQL Server’daki nvarchar veri tipi gibi düşünülebilir. Eğer saklanacak verinin başında ya da sonunda boşluk (space) karakteri varsa bunları silerek tutar. char: 2000 taneye kadar sabit uzunluklu karakter tutabilen veri tipidir. Microsoft SQL Server’daki char veri tipi gibi düşünülebilir. nchar: 2000 taneye kadar sabit uzunluklu Unicode karakter tutabilen veri tipidir. Microsoft SQL Server’daki nchar veri tipi gibi düşünülebilir. number: Her türlü sayısal verilerin tutulabileceği veri tipidir. Microsoft SQL Server’daki int, float gibi veri tiplerine karşılık olarak düşünülebilir. Dikkat edilmesi gereken nokta Microsoft SQL Server’daki decimal gibi tanımlanmasıdır. date: Saniye bazında tarih ve zaman verisi tutar. Geçerli tarih aralığı MÖ 1 Ocak 4712’den MS 31 Aralık 9999’a kadardır. Microsoft SQL Server’daki smalldatetime veri tipi gibi düşünülebilir. timestamp: date tipine çok benzeyen bir veri tipidir. date tipine göre daha detaylı zaman bilgisi tutar. Microsoft SQL Server’daki datetime veri tipi gibi düşünülebilir. clob (character large object): 4 GB’a kadar karakter verisi tutar. varchar2’nin büyütülmüş bir tipi olarak düşünülebilir. Microsoft SQL Server 2005 ile birlikte gelen varchar(max) veri tipi gibi düşünülebilir. blob (binary large object): clob’a benzer bir veri tipi olmakla birlikte içinde tuttuğu veri tipi binary’dir. Maksimum büyüklüğü 4 GB’tır. Microsoft SQL Server’daki text, image tiplerine benzerdir. Oracle’da Kullanıcı Yönetimi ve Güvenlik Oracle sisteminde kullanıcı yönetimi ve güvenlik Microsoft SQL Server’a göre daha detaylıdır; çünkü Microsoft SQL Server sadece Microsoft Windows ailesi işletim sistemlerinde çalışırken 179 180 Bölüm 10 Oracle Microsoft Windows ailesi, UNIX türevleri ve Linux türevleri üzerinde de çalışmaktadır. Bu sebeple kullanıcı yapısı farklılıklar göstermektedir. Örneğin Microsoft SQL Server’da olan “Windows Authentication” Oracle için geçerli değildir; ancak bunu karşılayan farklı bir yapı mevcuttur. Ayrıca Oracle’da kullanıcı, hesap ve şema (schema) ifadeleri aynı anlamda kullanılabilir. Kimlik denetleme yöntemlerine geçmeden önce kimlik denetiminde kullanılan bazı kavramları tanımlayalım. Default Tablespace: Bir kullanıcının oluşturduğu/oluşturacağı nesnelerin (schema objects), nesne oluşturulurken nesnelerin hangi tablespace üzerinde tutulacağı belirtilmediğinde default olarak tutulduğu tablespace’tir. Kullanıcı tanımlanırken belirtilmezse, veri tabanı kendi default tablespace’ini kullanıcıya da default tablespace olarak atar. Ara İşlemler İçin Kullanılan (Temporary) Tablespace: Temporary tablespace üzerinde GROUP BY, ORDER BY, DISTINCT, JOIN ve index oluşturma gibi geçici alan ihtiyacı olan işlemlerde kullanılan tablespace’tir, aynı zamanda geçici tablolar için de kullanılır. Kullanıcı tanımlanırken belirtilmezse, veri tabanı kendi default tablespace’ini kullanıcıya da default tablespace olarak atar. Profiller: Kullanıcı yönetimini kolaylaştıran bir mekanizmadır. Her kullanıcı mutlaka bir profil altında tanımlıdır. Kaynakların kullanınımı profil tanımına göre kısıtlar ve şifre kurallarının tanımlanmasını sağlar. Kullanıcı tanımlanırken belirtilmezse, kullanıcı Oracle default profile atanır. Authentication (Kimlik Denetleme) Yöntemleri Oracle’da 3 kimlik denetleme yöntemi vardır. Bu yöntemler şu şekildedir: 1� Password Authentication 2. External Authentication 3� Global Authentication Password Authentication Microsoft SQL Server’daki SQL Authentication ile eşleniktir. Kullanıcılar Oracle veri tabanı üzerinde tanımlıdır. Oracle kullanıcıya ait şifreyi üzerinde şifreli olarak tutar. Password Authentication ile kullanılacak bir kullanıcı şöyle tanımlanır: CREATE USER kaya IDENTIFIED BY qwedcv89; Burada “kaya” kullanıcının adıyken “qwedcv89” bu kullanıcın şifresidir. Bu kullanıcıya ait bir default tablespace; yani oluşturacağı nesnelerin tutulacağı bir tablespace belirtmek istersek; CREATE USER kaya IDENTIFIED BY qwedcv89 DEFAULT TABLESPACE xyz; ifadesini kullandığımızda bu kullanıcının nesnelerinin yaratılacağı default tablespace xyz adlı tablespace olacaktır. Eğer “kaya” kullanıcısı daha önceden oluşturulduysa ALTER ifadesi ile de default tablespace tanımlayabiliriz. ALTER USER kaya DEFAULT TABLESPACE xyz; Bu kullanıcı için bir de geçici tablespace tanımlamak istersek; CREATE USER kaya IDENTIFIED BY qwedcv89 DEFAULT TABLESPACE xyz TEMPORARY TABLESPACE abc; ifadesini kullanabiliriz. Bu durumda “kaya” kullanıcısının çalıştırdığı büyük veri üzerinde işlem yapıp geçici alanlar kullanan sorgular, geçici tablespace olarak abc tablespace’ini kullanacaktır. Oracle Veritabanı Profil tanımlamak için de şu şekilde bir ifade kullanabilirsiniz: CREATE USER kaya IDENTIFIED BY qwedcv89 DEFAULT TABLESPACE xyz TEMPORARY TABLESPACE abc PROFILE prf1; External Authentication External authentication Microsoft SQL Server’daki Windows authentication’a çok benzerdir. Kullanıcılar yine veri tabanında tutulur; ancak kimlik denetlemesini işletim sistemi yapar. Microsoft SQL Server’daki Windows authentication’dan en önemli farkı sadece Microsoft Windows’la değil diğer işletim sistemleri ile de entegre çalışabilmesidir. Bu tip kullanıcılar OPS$ kullanıcıları olarka da adlandırılırlar. External authentication mekanizmasıyla bir kullanıcı tanımlamak için; CREATE USER ops$kaya IDENTIFIED EXTERNALLY; ifadesini kullanabiliriz. Bu durumda işletim sistemi üzerinde kullancı adı “kaya” olan kullanıcı, Oracle sistemine, ilgili işletim sistemi üzerinden onaylanarak girecektir. Bu işlem Linux ve UNIX türevleri için oldukça kolaydır; ancak Windows üzerinde bir miktar karmaşıktır. Linux ve UNIX türevlerinde /etc/passwd içinde kullanıcının tanımlanmış olması yeterlidir. Windows üzerindeki bir kullanıcıyı Oracle’da tanımlamak için yapmamız gerekenler şu şekildedir: 1� Windows işletim sistemi üzerinde kullanıcıyı tanımlayın. 2. Tanımladığınız kullanıcıyı Windows üzerinde ora_dba grubuna ekleyin. 3� Start > Programs > Oracle - OraDb10g_home1 > Configuration and Migration Tools > Administration Assistant for Windows üzerinden OS Database Administrators ve OS Database Operators gruplarına ekleyin. 4. SPFILE dosyasında OS_AUTHENT_PREFIX=OPS$ şeklinde tanımlana yapın. 5. CREATE USER OPS$kullanici IDENTIFIED EXTERNALLY; ifadesini SQL Plus üzerinde çalıştırın. Global Authentication Oracle üzerinde Microsoft SQL Server’dan farklı olarak bulunan bir authentication mekanizmasıdır. Authentication işlemi, gelişmiş güvenlik opsiyonu ile sağlanan bir servis aracılığıyla sağlanır ve password authentication’daki şifrelerin tutulması söz konusu değildir. Burada kullanılan mekanizmalara örnek olarak biyometrik yöntemler, Kerberos ve X.509 sertifikaları sayılabilir. Diğer mekanizmalara göre oldukça detaylı bir yöntemdir. 181 11 Oracle Üzerinde Programlama 11 Oracle Üzerinde Programlama • PL/SQL Nedir? • SQL Plus • TOAD • PL/SQL ile Programlama • PL/SQL’de Kontrol Yapıları • PL/SQL’de Alfanümerik Tipler • PL/SQL’de Mantıksal Tipler • PL/SQL’de Tarih ve Zaman Tipleri • Referans Tipleri • LOB (Large Object) Tipleri Oracle Üzerinde Programlama PL/SQL Nedir? PL/SQL’i (Procedural Language Extensions to the Structured Query Language) Microsoft SQL Server’daki T-SQL’in (Transact SQL) Oracle sistemindeki karşılığı olarak düşünebilirsiniz. T-SQL gibi PL/SQL de standart SQL dilinin, günün gereksinimleri bağlamında yetersiz kalması çerçevesinde ortaya çıkmıştır. PL/SQL ile stored procedure, trigger gibi veri tabanı nesneleri oluşturabilirsiniz. PL/SQL ifadeleri Oracle veri tabanı üzerinde çalışır. PL/SQL dil olarak ilk bakışta bir miktar yabancı gelebilir; ancak yeterli veri tabanı bilginiz varsa alışmanız çok zor olmayacaktır. Her dilde olduğu gibi bol bol pratik, PL/SQL’deki yetkinliklerinizi çabuk geliştirmeniz açısından çok faydalı olacaktır. T-SQL’den tanıdığınız standart SQL dilinin kapsadığı bütün ifadeleri burada da kullanabileceksiniz. Bildiğiniz 4 temel SQL ifadesi olan SELECT, INSERT, UPDATE, DELETE ifadelerini aynen kullanabilirsiniz. PL/SQL ifadelerini yazmak için kullanabileceğiniz temel ortam, Oracle yüklemesiyle birlikte gelen SQL Plus’tır. SQL Plus’ı Microsoft SQL Server 2000 ve öncesindeki Query Analyzer, Microsoft SQL Server 2005’te SQL Server Management Studio’daki Query Window’a karşılık gelen bir araç olarak düşünebilirsiniz; ancak her ikisine göre de daha ilkel bulabilirsiniz. SQL Plus SQL Plus’ı Start > Programs > Oracle – OraDb10g_home1 > Application Development > SQL Plus yoluyla çalıştırabilirsiniz. Çalıştırdığınızda karşınıza Şekil – 1’deki ekran görüntüsü gelecektir. Şekil 1: SQL Plus Açılış Ekranı. Programı ilk açtığınızda Log On ekranı karşınıza gelecektir. Bu ekranda User Name bölümüne tanımladığınız kullanıcılardan birini girmeniz gerekir. Veri tabanı oluştururken tanımlanan 4 kullanıcımız vardı, bunlardan SYSTEM kullanıcısını burada kullanabiliriz. Host String bölümüne de veri tabanımızın adını yazmalıyız. Host String’te kullanabileceğimiz veri tabanlarını tnsnames.ora dosyasından da bulabiliriz. Bunun için Windows Search ile tnsnames.ora dosyasını aratabilirsiniz. Başarılı bir şekilde login olduğunuzda karşınıza Şekil 2’deki gibi bir ekran gelecektir. 186 Bölüm 11 Şekil 2: SQL Plus Açılış Ekranı – 2. Önce SQL Plus üzerinden veri tabanımızda basit bir tablo oluşturalım. Bunun için standart SQL’den bildiğimiz CREATE TABLE ifadesini kullanacağız. Bu tablonun oluşturulmasını Şekil 3’de görebilirsiniz. Şekil 3: Tablo oluşturmak. Bu tabloyu oluşturduktan sonra tabloya veri girişi için; INSERT INTO Urun ( UrunId, UrunAdi) VALUES ( 1, ‘Çikolata’); yazalım. İfadelerin sonunda noktalı virgül “;” koymayı unutmayın. Microsoft SQL Server’da yazdığımız ifadelere göre ifadelerin sonunda “;” bulunması gerekir. Ayrıca bu ifadeyi ya da ifade bloğunu çalıştırmak için Enter’a basmanız yeterlidir. Benzer şekilde bir sorgu yapmak için de SQL Plus’ta SELECT * FROM Urun; yazarak Urun tablosunun içindeki verileri inceleyebiliriz. Yazdığımız bu ifadeleri SQL Plus ekranında Şekil 4’te görebilirsiniz. Oracle Üzerinde Programlama ve PL/SQL Şekil 4: SQL Plus’ta SQL İfadeleri. TOAD SQL Plus’a daha iyi bir alternatif olarak TOAD’u kullanabilirsiniz. http://www.toadsoft.com/lic_agree.html adresinden TOAD’un Oracle için ücretsiz (freeware) versiyonunu indirebilirsiniz. TOAD Oracle, Microsoft SQL Server, IBM DB2 ve mySQL gibi veri tabanları için kullanılabilen bir araçtır. TOAD’u Microsoft SQL Server 2005 Management Studio gibi düşünebilirsiniz. Hem yönetim hem de programlama arayüzlerini birlikte sunar. Şekil 5: TOAD Login Ekranı. Şekil 5’teki TOAD’un login ekranını Microsoft SQL Server Login ekranına benzer olarak düşünebilirsiniz. Bağlanacağınız veri tabanını, hangi kullanıcı ile hangi rolle ve hangi sunucuya bağlanacağınızı belirterek TOAD’un ana ekranına geçebilirsiniz. Dikkat edeceğiniz gibi Windows Authentication şeklinde bir seçenek bulunmaz. Oracle’da Windows Authentication şeklinde bir seçenek bulunmaz; ancak external authentication ile Windows kullanıcıları ile Oracle sistemine login olabilirsiniz. Bu opsiyonu kurmak Microsoft SQL Server kadar kolay ve düz değildir. Bu sebeple Oracle’ın kendi kullanıcı (schema) sistemini kullanarak devam edeceğiz. TOAD login ekranında bilgileri doğru olarak girip Connect butonuna tıkladığımızda ana ekrana geçebiliriz. 187 188 Bölüm 11 Şekil 6: TOAD ana ekranı. TOAD ana ekranında Şekil 6’dan gördüğünüz gibi pek çok işi yapabilirsiniz. Buraya standart SQL ve PL/SQL sorgularınızı yazarak çalıştırabilirsiniz. Sorgu yazarken, eğer sorguda nesne kullanıyorsak dikkat etmemiz gereken nokta nesne adından önce schema adını yazmamız gerekliliğidir. Schema’ları Microsoft SQL Server 2005’teki schema’lara denk olarak düşünebiliriz. SYSTEM kullanıcısını da Microsoft SQL Server 2005’deki dbo’ya karşılık gibi görebiliriz. Aslında birebir bu şekilde denkleştirmeler söz konusu olmasa da bu benzetmeleri bir kolaylık olarak görebilirsiniz. Şekil 7: TOAD’da sorgu. Oracle Üzerinde Programlama ve PL/SQL TOAD’da sorgu yazmayı Şekil 7’de görebilirsiniz. SQL ifadelerinin sonunda “;” kullanmanız ya da kullanmamanız sorgu sonucunu değiştirmez. Sorguda daha önce oluşturduğumuz Urun tablosunu SYSTEM.Urun olarak kullandığımıza dikkat edin. Bu şekilde yazmazsanız hata alırsınız. Sorguyu çalıştırmak için klavyeden F5 tuşunu kullanabilirsiniz. Bununla birlikte TOAD üzerindeki toolbar’dan da sorguları çalıştırabilirsiniz. Sorgu çalıştırabileceğiniz butonları Şekil 8’den görebilirsiniz. Şekil 8: TOAD sorgu çalıştırma butonları. PL/SQL ile Programlama PL/SQL ile programlamaya basit bir örnek ile başlayalım. T-SQL’dekine göre muhtemelen en çok zorlayacak durum PL/SQL içinde kullanılabilecek fonksiyonların çokluğu olacaktır. PL/SQL’de büyük ya da küçük harf kullanarak kod yazabilirsiniz. Uygulama 1: PL/SQL ile İlk Örnek Uygulama 1DECLARE 2 UrunSayisi INTEGER; 3 BEGIN 4 SELECT 5 COUNT(*) INTO UrunSayisi 6 FROM Urun; 7 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi: ‘ || UrunSayisi); 8 END; Uygulama 1’deki kod oldukça basit bir uygulamadır; ancak PL/SQL konusunda oldukça önemli bilgiler verir. Bu örnekte 1 ve 2 numaralı satırlar tanımlama (declaration) bloğudur. Altındaki kod bloğunda kullanılacak değişkenler, parametreler gibi yapılar burada tanımlanır. Örneğimizde 2 numaralı satırda INTEGER tipinde, UrunSayisi adında bir değişken tanımlanmıştır. 3 numaralı satırda çalıştırma bloğunun (execution block) başladığını gösteren BEGIN ifadesi bulunuyor. Örnekteki 8 numaralı satırda bulunan END ifadesine kadar tüm ifadeler çalıştırma bloğunun içindedir. 4, 5 ve 6 numaralı satırlarda Urun tablosu içindeki satır sayısını UrunSayisi değişkeninin içine atan basit bir sorguyu görebilirsiniz. 5 numaralı satırdaki INTO ifadesini T-SQL’de kullandığımız değişkene değer atamadaki “=” gibi düşünebilirsiniz. 7 numaralı satırda ise T-SQL’deki print ifadesine benzer bir iş yapan DBMS_OUTPUT.PUT_LINE fonksiyonu bulunuyor. DBMS_OUTPUT. PUT_LINE DBMS Output tamponuna yazma işlemi yapar. Bu fonksiyonun içinde kullandığımız || işaretini T-SQL’de alfanümerik ifadeleri birleştirmek için kullandığımız + işaretinin yerine kullanıyoruz. T-SQL’de print ifadesinde bu şekilde bir çıktı almak için ise tanımladığımız int değişkeni öncelikle alfanümerik bir tipe çevirmemiz gerekir, çünkü print 2 ifadeyi otomatik olarak alfanümeriğe dönüştürmez. Buradan da anlayacağımız gibi DBMS_OUTPUT.PUT_LINE fonksiyonu otomatik çevirme (implicit conversion) yapmaktadır. Burada T-SQL’de kullandığımız SELECT @UrunSayisi gibi bir ifade ile değişkenin taşıdığı değeri yazdıramayız. 8 numaralı satırda da çalışma bloğu sona ermektedir. Bu örneğin T-SQL’deki benzer karşılığını Uygulama 1.1’de inceleyebilirsiniz. 189 190 Bölüm 11 Uygulama 1.1: Uygulama – 1’deki PL/SQL Kodunun T-SQL’deki Eşleniği 1DECLARE @UrunSayisi int 2SELECT 3 @UrunSayisi = COUNT(*) 4 FROM Urun 5PRINT ‘Urun Sayisi: ’ + CONVERT( varchar, @UrunSayisi) Uygulama 1’deki örneği TOAD’da çalıştırdığınızda herhangi bir çıktı göremezsiniz. Çünkü DBMS_ OUTPUT.PUT_LINE fonksiyonu bir çalışma ekranında bir sonuç üretmez bir tampona yazar. TOAD ile bu tampon bölgeyi görüntüleyebiliriz. Bunun için örnek uygulamayı çalıştırmadan önce View menüsünden DBMS Output seçeneğini seçin. Karşınıza boş bir ekran gelecektir. Standart Windows uygulamalarında olduğu gibi, daha önceden kod yazdığınız pencereye dönmek için CtrlTab kullanabilirsiniz ya da TOAD’un Window menüsünden diğer pencereye geçiş yapabilirsiniz. Şekil 9: Uygulama 1’in çalışmasında sorgu ekranı. Şekil 9’da görebileceğiniz gibi burada satır sayısı ile ilgili herhangi bir sonuç göremiyoruz, sadece PL/SQL procedure successfully completed ifadesini görebilirsiniz. Şekil 10: TOAD DBMS Output penceresi. Oracle Üzerinde Programlama ve PL/SQL Şekil 10’da TOAD DBMS Output penceresini görebilirsiniz. Burada DBMS_OUTPUT.PUT_LINE ifadesinin sonucu olan Urun Sayisi: 2 string’i yazılmıştır. PL/SQL’de Kontrol Yapıları PL/SQL’de T-SQL’deki gibi IF-THEN-ELSE kontrol yapısı bulunur. Bununla birlikte T-SQL’de bulunmayan CASE yapısı da mevcuttur. CASE yapısı C#’taki ve Java’daki switch yapısı şeklinde çalışır. IF-THEN-ELSE Uygulama 2: PL/SQL ile IF-THEN-ELSE 1DECLARE 2 UrunSayisi INTEGER; 3 BEGIN 4 SELECT 5 COUNT(*) INTO UrunSayisi 6 FROM Urun; 7 IF UrunSayisi <= 2 8 THEN 9 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den az ya da 2’); 10 ELSE 11 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den fazla...’); 12 13 END; END IF; Uygulama 2’deki kodun 7, 8, 9, 10, 11 ve 12 numaralı satırlarda IF-THEN-ELSE yapısını görebilirsiniz. PL/SQL’deki IF-THEN-ELSE yapısı T-SQL’deki IF-ELSE bloğu ile aynı şekilde çalışır. Uygulama 2.1’de Uygulama 1’deki PL/SQL kodunun T-SQL eşleniğini inceleyebilirsiniz. Uygulama 2.1: Uygulama 1’deki Kodun T-SQL Eşleniği 1DECLARE @UrunSayisi int 2SELECT 3 @UrunSayisi = COUNT(*) 4 FROM Urun 5IF @UrunSayisi <= 2 6 PRINT ‘Urun sayisi 2’ + char(39) + ‘den az ya da 2’ 7ELSE 8 PRINT ‘Urun sayisi 2’ + char(39) + ‘den fazla...’ IF-THEN-ELSIF-ELSE IF yapısının en detaylı şekli ELSIF ile kullanılan şeklidir. ELSIF (ELSE IF’in kısaltması) yapısı T-SQL’deki ELSE IF ile aynı şekilde çalışır. ELSIF, üstündeki IF ya da ELSIF bloğundan false ile çıkıldığında çalışır ve tanımladığı koşul true sonuç veriyorsa bloğu çalıştırır; aksi takdirde kendinden sonra gelen bloğa geçer. 191 192 Bölüm 11 Uygulama 3: PL/SQL ile ELSIF 1DECLARE 2 UrunSayisi INTEGER; 3 BEGIN 4 SELECT 5 COUNT(*) INTO UrunSayisi 6 FROM Urun; 7 IF UrunSayisi < 2 8 THEN 9 || ‘den az...’); DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ 10 ELSIF UrunSayisi > 2 11 THEN 12 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den fazla...’); 13 ELSE 14 15 END IF; 16 END; DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘); Uygulama 3’de 7 numaralı satırda eğer UrunSayisi 2’den küçük değilse 10 numaralı satırdaki ELSIF çalışır. Eğer UrunSayisi 2’den büyükse 12 numaralı satır çalışır. Eğer UrunSayisi 2’den de büyük değilse 2’ye eşit demektir. Bu durumda da 13 numaralı satırdan dolayı 14 numaralı satır çalışır. Eğer IF yapısı içerisinde bir blok oluşturumak istersek bunun için T-SQL’de olduğu gibi BEGINEND blok yapılandırıcıları ile bloğu tanımlamalıyız. Uygulama 3.1: PL/SQL ile ELSIF 1DECLARE 2 UrunSayisi INTEGER; 3 BEGIN 4 SELECT 5 COUNT(*) INTO UrunSayisi 6 FROM Urun; 7 IF UrunSayisi < 2 8 THEN 9 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den az...’); 10 ELSIF UrunSayisi > 2 11 THEN 12 BEGIN 13 DBMS_OUTPUT.PUT_LINE(‘ELSIF blogu...’); 14 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den fazla...’); 15 END; 16 ELSE Oracle Üzerinde Programlama ve PL/SQL 17 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘); 18 END IF; 19 END; Uygulama 3.1’de 10 numaralı satırdan başlayan ELSIF bloğu 15 numaralı satıra kadar devam eder. Eğer UrunSayisi 2’den büyükse ELSIF içinde tanımlanan her 2 DBMS_OUTPUT. PUT_LINE ifadesi de çalışacaktır. Her ne kadar yazdığımız BEGIN-END blok yapılandırıcılarını kullanmak zorunda olmasak da kodun okunurluğunu artırmak açısından blok yapılandırıcılarını kullanmak faydalıdır. İç İçe IF Yapıları PL/SQL’de iç içe IF yapıları da kullanılabilir. Çalışma şekli C#, Java ya da T-SQL’den farklı değildir. Uygulama 4: PL/SQL ile İç İçe IF Yapısı 1DECLARE 2 UrunSayisi INTEGER; 3 UrunSayisiB INTEGER; 4 BEGIN 5 SELECT 6 COUNT(*) INTO UrunSayisi 7 FROM Urun; 8 SELECT 9 COUNT(*) INTO UrunSayisiB 10 FROM Urun 11 WHERE UrunAdi LIKE ‘B%’; 12 IF UrunSayisi < 2 13 THEN 14 ‘’’’ || ‘den az...’); DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || 15 IF UrunSayisiB = 1 16 THEN 17 DBMS_OUTPUT.PUT_LINE(‘B ile baslayan urun sayisi: ’ || UrunSayisiB); 18 END IF; 19 ELSIF UrunSayisi > 2 20 THEN 21 BEGIN 22 DBMS_OUTPUT.PUT_LINE(‘ELSIF blogu...’); 23 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den fazla...’); 24 END; 25 ELSE 26 27 END IF; 28 END; DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘); 193 194 Bölüm 11 Uygulama 4’te 8 ile 11 numaralı satırlardaki SELECT ifadesinde UrunSayisiB değişkenine B harfiyle başlayan ürünlerin sayısı atanacaktır. Eğer UrunSayisi 2’den küçükse 15 numaralı satırdaki iç IF yapısı da çalışacaktır. Eğer UrunSayisiB 1’e eşitse de 17 numaralı satır çalışacaktır. IF Yapısı ile İlgili Dikkat Edilmesi Gereken Noktalar 1� Her IF için mutlaka bir END IF bulunmalıdır. 2. Her END IF kendinden önceki ilk IF ifadesine aittir. 3� END IF birleşik yazılmaz. 4. END IF’in sonunda mutlaka “;” bulunmalıdır. 5. ELSIF yazarken ELSE’in son “E” harfi yoktur. CASE CASE yapısı C# ve Java’daki switch yapısı gibi çalışır. IF-THEN-ELSIF-ELSE yapısını daha kolay ve okunaklı hale getirir. PL/SQL’deki CASE yapısını T-SQL’de SELECT ifadesinin içinde kullanılan CASE ifadesiyle karıştırmamak gerekir. T-SQL’deki CASE yapısı result set’in içindeki tüm kayıtlar için tek tek çalışırken PL/SQL’de kayıtlar bazında çalışmaz. Kod yazımı olarak her ikisi de birbirine çok benzerdir. Uygulama 5: PL/SQL’de CASE Örneği 1 1DECLARE 2 UrunSayisi INTEGER; 3 BEGIN 4 SELECT 5 COUNT(*) INTO UrunSayisi 6 FROM Urun; 7 CASE UrunSayisi 8 WHEN 2 THEN 9 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘); 10 WHEN 3 THEN 11 12 ELSE DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘); 13 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 3‘ || ‘’’’ || ‘ten fazla...’); 14 15 END; END CASE; Uygulama 4’te 7 – 14 numaralı satırlar CASE bloğunu oluşturur. 7 numaralı satırdaki CASE UrunSayisi ifadesi UrunSayisi değişkeninin karşılaştırmada kullanılacağını gösterir. 8 numaralı satır eğer UrunSayisi değişkeninin 2’ye eşitliğini kontrol eder. Eğer 2’ye eşitse 9 numaralı satıra geçer, eşit değilse 10 numaralı satır ve UrunSayisi değişkeninin 3’e eşit olup olmadığı kontrol eder. 3’e eşit olması halinde 11 numaralı satır çalışr; aksi takdirde 12 numaralı satırdaki ELSE ifadesine gelir. PL/SQL’de CASE yapısında ELSE ifadesi C# ve Java’daki switch ifadesindeki default gibi çalışır. Eğer ELSE’ten önce gelen WHEN ifadelerinin hiçbirinde true oluşmazsa ELSE bloğu çalışır. WHEN bloklarının sonunda C# ve Java’daki break ifadesinin yerine kullanılması gereken herhangi bir ifade yoktur. Uygulama 5’teki yapıyı IF-ELSIF-ELSE ile kurmak daha doğru olacaktır. Uygulama 3’deki gibi bir IF yapısını CASE ile yazmak istersek PL/SQL bu konuda bize esneklik sağlamaktadır. Oracle Üzerinde Programlama ve PL/SQL Uygulama 6: PL/SQL’de CASE Örneği 2 1DECLARE 2 UrunSayisi INTEGER; 3 BEGIN 4 SELECT 5 COUNT(*) INTO UrunSayisi 6 FROM Urun; 7 CASE TRUE 8 WHEN UrunSayisi < 2 THEN 9 || ‘den az...’); DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ 10 WHEN UrunSayisi > 2 THEN 11 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den fazla...’); 12 ELSE 13 14 END CASE; 15 END; DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘); Uygulama 6’da 7 numaralı satır her durumda çalışacaktır. 8 numaralı satırda UrunSayisi değişkeninin değerinin 2’den küçük olup olmadığı kontrol edilir. Eğer 2’den küçükse 9 numaralı satır çalışır; aksi takdirde 10 numaralı satırda UrunSayisi değişkeninin değerinin 2’den büyük olup olmadığı kontrol edilir. Eğer 2’den büyükse 11 numaralı satır çalışır; aksi takdirde 12 numaralı satırdaki ELSE ifadesi çalışır. Bu noktadan sonraki çalışma Uygulama 5’teki ELSE bloğu ile aynı şekildedir. Uygulama 6’daki örneğe benzer olarak CASE ifadesinini farklı bir kullanımı daha vardır. CASE’in bu kullanımı esnekliği daha da artırır ve özellikle karmaşık mantıksal işlemler için idealdir. Uygulama 7: PL/SQL’de CASE Örneği 3 1DECLARE 2 UrunSayisi INTEGER; 3 UrunSayisiB INTEGER; 4 BEGIN 5 SELECT 6 COUNT(*) INTO UrunSayisi 7 FROM Urun; 8 SELECT 9 COUNT(*) INTO UrunSayisi 10 FROM Urun 11 WHERE UrunAdi LIKE ‘B%’; 12 CASE 13 WHEN UrunSayisi < 2 AND UrunSayisiB = 1 THEN 14 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den az...’); 15 WHEN UrunSayisi > 2 AND UrunSayisiB < 1 THEN 195 196 Bölüm 11 16 DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘ || ‘’’’ || ‘den fazla...’); 17 ELSE 18 19 END CASE; 20 END; DBMS_OUTPUT.PUT_LINE(‘Urun Sayisi 2‘); Uygulama 7’de 12 numaralı satırdaki CASE ifadesi ne olursa olsun çalışacaktır ve CASE ile 1’den fazla sayıda değişken kontrol edilebilecektir. Şu ana kadar yaptığımız örnekleri TOAD yerine SQL Plus ile yapmak istediğinizde yazarken dikkat etmeniz gereken birkaç nokta vardır. DBMS_OUTPUT.PUT_LINE fonksiyonunun çalışması için SET SERVEROUTPUT ON ifadesini bir kere çalıştırmalısınız. Bu ifadeyi Microsoft SQL Server’daki direktiflere benzetebilirsiniz. SET SERVEROUTPUT ON ifadesi TOAD’daki View menüsündeki DBMS Output öğesinin açılması işlevini görür. SET SERVEROUTPUT ON ifadesini çalıştırdıktan sonra PL/SQL bloğunu yazabiliriz. Bloğun sonunda “/” işaretini kullandığımızda yazdığımız blok çalışacaktır. Şekil 11’de SQL Plus ile PL/SQL bloklarının nasıl çalıştırıldığına yönelik ekran çıktısını görebilirsiniz. Şekil 11: SQL Plus ile PL/SQL bloklarının çalıştırılması. PL/SQL’de Değişkenler PL/SQL’de veri tipleri 4 ana gruba ayrılır. Bu gruplar: 1� Basit (skaler) Tipler: T-SQL’deki basit tiplere ya da Java ve C#’taki değer tipli değişkenlere benzetilebilir. Herhangi bir anda doğrudan tek değer taşırlar, bellekte doğrudan tutulurlar. 2. Bütünleşik (kompozit) Tipler: Dizi gibi kendi içinde tek tek işlenebilecek, T-SQL’de eksikliğini hissettiğimiz tiplerdir. Java ve C#’taki dizilere benzetebiliriz; ancak Java ve C#’ta dizilerin referans tipli olduğunu unutmamak gerekir. 3� Referans Tipleri: Bir işaretçi (pointer) aracılığıyla taşıdığı değerlere erişilebilen tiplerdir. TSQL’de doğrudan karşılığı yoktur. Java ve C#’taki referans tipli değişkenlerle benzer mantıkta çalışırlar. 4. LOB (Large Object) Tipleri: Şekil, metin gibi büyük verileri tutmak için geliştirilmiş tiplerdir. TSQL’deki text, image gibi düşünülebilir. Özünde referans tipli yapılara çok benzerdirler; çünkü taşıdıkları değer resmin ya da metnin başlangıcını gösteren bir işaretçidir. Oracle Üzerinde Programlama ve PL/SQL PL/SQL’de bir değişkeni tanımlarken önce değişkenin adını sonra da tipini yazarız. DECLARE <değişken adı> <tip adı>; Herhangi bir değişkene değer ataması yaparken “:=” işaretini kullanırız. <değişken adı> := <değer> BASİT TİPLER BÜTÜNLEŞİK TİPLER REFERANS TİPLERİ LOB TİPLERİ BINARY_DOUBLE RECORD REF CURSOR BFILE BINARY_FLOAT TABLE REF object_type BLOB BINARY_INTEGER VARRAY DEC DECIMAL DOUBLE PRECISION FLOAT INT INTEGER NATURAL NATURALN NUMBER NUMERIC PLS_INTEGER POSITIVE POSITIVEN REAL SIGNTYPE SMALLINT CHAR CAHARACTER LONG LONG RAW NCHAR NVARCHAR2 RAW ROWID STRING UROWID VARCHAR VARCHAR2 BOOLEAN DATE TIMESTAMP CLOB NCLOB 197 198 Bölüm 11 PL/SQL’de Sayısal Tipler Sayısal tipler adlarından anlaşılabileceği gibi matematiksel işlemlerde kullanılabilecek tiplerdir. Tam sayıları, reel ve ondalıklı sayıları tutabileceğiniz pek çok tip PL/SQL’de mevcuttur. BINARY_INTEGER 32 bitlik (4 byte) işaretli (signed), tam sayısal tiptir. -231 ile 231 – 1 aralığında değerleri tutar. PLS_ INTEGER tipine benzer bir tiptir. NUMBER tipine göre işlem performanı avantajı vardır ve NUMBER tipine göre daha az yer kaplar. NATURAL, NATURALN, POSITIVE, POSITIVEN, SIGNTYPE tipleri BINARY_INTEGER tipinin alt tipleridir. NATURAL ve POSITIVE tipleri sadece pozitif sayıları taşırlar. Dolayısıyla işaretsiz tiplerdir diyebiliriz. NATURAL tipinin POSITIVE tipinden tek farkı 0 (sıfır) değerini de alabilmesidir. NATURALN ve POSITIVEN tiplerinin NATURAL ve POSITIVE tiplerinden tek farkı null değer taşıyamamalarıdır. NATURAL, POSITIVE, NATURALN ve POSITIVEN tiplerinin taşıyabileceği en büyük değer 231 – 1’dir. SIGNTYPE ise sadece -1, 0 ve 1 değerlerini taşıyabilir. Örnek 1: BINARY_INTEGER Tanımlama 1DECLARE 2 bix BINARY_INTEGER; 3 nx NATURAL; 4 nnx NATURALN; 5 px POSITIVE; 6 pnx POSITIVEN; 7 stx SIGNTYPE; 8BEGIN 9 bix := -55; 10 nx := null; 11 nnx := 0; 12 px := 7; 13 pnx := 88; 14 stx = -1; 15 END; BINARY_FLOAT ve BINARY_DOUBLE Java ve C#’taki IEEE 754 standardındaki ondalıklı tiplerdir. BINARY_FLOAT bir değer sonuna eklenen ‘f’ harfiyle (1.004f), BINARY_DOUBLE bir değer sonuna eklenen ‘d’ harfiyle (3,05478d) ayrıştırılır. Örnek 2: BINARY_FLOAT ve BINARY_DOUBLE Tanımlama 1DECLARE 2 bfx BINARY_FLOAT; 3 bdx BINARY_DOUBLE; 4BEGIN 5 bfx := 10.054f; 6 bdx := 10.054d; 7 IF bfx = bdx Oracle Üzerinde Programlama ve PL/SQL 8 THEN 9 DBMS_OUTPUT.PUT_LINE(‘Esit’); 10 ELSE 11 12 END IF; 13 END; DBMS_OUTPUT.PUT_LINE(‘Esit degil’); Örnek 2’deki 7 numaralı satırdaki kontrolden FALSE sonucu çıkacaktır. Bu sebeple akış 8 numaralı satırdan değil 10 numaralı satırdan devam edecektir ve DBMS Output penceresine “Esit degil” yazacaktır. NUMBER PL/SQL’deki en genel sayısal tiptir. Hem tam sayıları hem de ondalıklı sayıları tutabilir. Değer aralığı 1E-130 ile 10E125’tir. NUMBER tipinde bir değişkeni sadece NUMBER olarak tanımlayabileceğimiz gibi hassasiyet (precission) ve ölçek (scale) değerleriyle birlikte de tanımlayabiliriz. Hassasiyet sayının toplam hane (dijit) sayısıdır, ölçek ise noktadan sonraki hane sayısıdır. Dolayısıyla hassasiyet için yazdığımız değer mutlaka ölçek için yazdığımız değerden büyük olmak zorundadır. NUMBER tipinde bir değişkeni sadece hassasiyet değeri ile de tanımlayabiliriz. Bunun anlamı ölçek değerinin yani ondalık hane sayısının 0 (sıfır) olmasıdır. En fazla 38 hanelik hassasiyet değeri ile tanımlanabilirken ölçek değeri -84 ile 127 arasında değişir. Ölçek değerinin negatif olması tam sayının yuvarlanması anlamına gelir. Örneğin ölçek değeri -2 olan bir NUMBER değişkene 257 değeri 200 olarak atanacaktır. DEC, DECIMAL, DOUBLE PRECISION, FLOAT, INTEGER, INT, NUMERIC, REAL ve SMALLINT tipleri NUMBER tipinin alt tipleridir. DEC, DECIMAL ve NUMERIC tipleri NUMBER gibi tanımlanırken alabileceği en büyük ölçek değeri 38’dir. DOUBLE PRECISION ve FLOAT tiplerinde ondalıklı kısım 126 bitte tutulur, bu da yaklaşık 38 ondalıklı haneye karşılık gelir. REAL tipinde ondalıklı kısım 63 bit’te tutulur, bu da yaklaşık 18 ondalıklı haneye karşılık gelir. INTEGER, INT ve SMALLINT tiplerinde de en fazla hassasiyet seviyesi 38’dir. T-SQL’deki tam sayısal tipler gibi düşünmek doğru değildir; çünkü PL/SQL’deki bu tiplerde ondalıklı kısım tanımlanabilir. Örnek 3: NUMBER Tanımlama 1DECLARE 2 nx1 NUMBER; 3 nx2 NUMBER(3); 4 nx3 NUMBER(3,2); 5 nx4 NUMBER(3,-1); 6 ix1 INTEGER; 7 ix2 INTEGER(3,1); 8 dpx DOUBLE PRECISION; 9 fx FLOAT; 10 BEGIN 11 nx1 := 10; 12 nx2 := 267; 199 200 Bölüm 11 13 nx3 := 1.25; 14 nx4 := 254; 15 ix1 := 8; 16 ix2 := 4.7; 17 dpx := 16.874; 18 fx := 6.8; 19 DBMS_OUTPUT.PUT_LINE(‘nx4 : ’ || nx4); 20 IF fx = dpx 21 THEN 22 23 ELSE 24 25 END IF; 26 IF nx4 = 250 27 THEN 28 29 ELSE 30 31 END IF; 32 END; DBMS_OUTPUT.PUT_LINE(‘Esit’); DBMS_OUTPUT.PUT_LINE(‘Esit degil’); DBMS_OUTPUT.PUT_LINE(‘nx4 = 250’); DBMS_OUTPUT.PUT_LINE(‘nx4 <> 250’); Örnek 3’deki kodun DBMS çıktısı; nx4 : 250 Esit nx4 = 250 şeklinde olacaktır. Bu örnekte nx4 değişkeninin tanımında ölçeğin -1 olmasından dolayı 254 değeri 250’ye dönüştürülecektir. Eğer ölçek -2 olsaydı 200’e dönüştürülecekti. 20 numaralı satırdaki FLOAT tipinde bir değişkenle DOUBLE PRECISION tipinde bir değişkenin karşılaştırılmasında da bu tipler arasında bir önceki örnekte kullandığımız BINARY_FLOAT ve BINARY_DOUBLE tiplerinden farklı olarak eşitlik olduğunu görebilirsiniz. PLS_INTEGER PLS_INTEGER tipinde de BINARY_INTEGER tipindeki gibi -231 ile 231 – 1 arasındaki tam sayılar tutulabilir. PLS_INTEGER tipi hem NUMBER tipinden hem de BINARY_INTEGER tipinden daha hızlı çalışır. Bunun NUMBER ve BINARY_INTEGER işlemleri yazılımsal olarak yaparken PLS_INTEGER tipinin donanımsal olarak yapmasıdır. BINARY_INTEGER tipi eski versiyonlardan bu yana gelen bir tiptir. Eğer yeni bir uygulama geliştiriyorsanız PLS_INTEGER tipini kullanmak daha iyi olacaktır. Örnek 4: PLS_INTEGER Tanımlama 1DECLARE 2 pix PLS_INTEGER; 3 bix BINARY_INTEGER; 4BEGIN 5 pix := 25; Oracle Üzerinde Programlama ve PL/SQL 6 bix := 25; 7 IF pix = bix 8 THEN 9 DBMS_OUTPUT.PUT_LINE(‘Esit’); 10 ELSE 11 12 END IF; 13 END; DBMS_OUTPUT.PUT_LINE(‘Esit degil’); Örnek 4’ü çalıştırdığınızda PLS_INTEGER tipinde taşınan bir değerle BINARY_INTEGER tipinde taşınan bir değer eğer aynıysa eşitlik sonucunu üreteceğini görebilirsiniz. PL/SQL’de Alfanümerik Tipler Alfanümerik tiplerde sayısal olarak yorumlanmayacak karakterler ve string tipler tutulur. CHAR Belli uzunlukta alfanümerik ifadeleri tutmak için kullanılır. En fazla uzunluğu 32767 karakterdir. Eğer bir uzunluk tanımlanmazsa tek karakter tutar. CHARACTER veri tipini CHAR veri tipiyle aynı şekilde kullanabilirsiniz. Örnek 5: CHAR Tanımlama 1DECLARE 2 cx1 CHAR; 3 cx2 CHAR; 4 cx3 CHAR(4); 5 cx4 CHARACTER; 6BEGIN 7 cx1 := ‘A’; 8 cx2 := ‘a’; 9 cx3 := ‘ABCD’; 10 cx4 := ‘A’; 11 IF cx1 = cx2 12 THEN 13 14 ELSE 15 16 END IF; 17 IF cx1 = cx4 18 THEN 19 20 ELSE 21 22 END IF; 23 IF cx1 = SUBSTR(cx3, 1, 1) 24 THEN 19 DBMS_OUTPUT.PUT_LINE(‘Esit’); DBMS_OUTPUT.PUT_LINE(‘Esit degil’); DBMS_OUTPUT.PUT_LINE(‘Esit’); DBMS_OUTPUT.PUT_LINE(‘Esit degil’); DBMS_OUTPUT.PUT_LINE(‘Esit’); 201 202 Bölüm 11 20 ELSE 21 22 END IF; 23 END; DBMS_OUTPUT.PUT_LINE(‘Esit degil’); Örnek 5’i çalıştırdığınızda şu şekilde bir DBMS çıktısı ile karşılaşırsınız: Esit degil Esit Esit Bu örneğin DBMS çıktısından anlaşılacağı gibi “A” harfi ve “a” harfi birbirine eşit değildir. 23 numaralı satırda kullandığımız SUBSTR() fonksiyonu, adından da anlaşılabileceği gibi alfanümerik bir yapıdan istediğimiz bir parçayı almak için kullanılır. SUBSTR fonksiyonunun kullanımı T-SQL’deki SUBSTRING() fonksiyonu gibidir. İlk argüman parça alınacak alfanümerik değer ya da değişkendir. İkinci argüman, bu değer ya da değişkenin kaçıncı karakterinden itibaren parça alınacağıdır. Üçüncü argüman, ikinci argümanda belirtilen karakter dahil olmak üzere kaç tane karakter alınacağını gösterir. Eğer üçüncü argüman girilmezse, ilk argümanda belirtilen değer ya da değişkenin, ikinci argümanda belirtilen karakterinden sonuna kadar tüm karakterleri alınır. LONG ve LONG RAW LONG ve LONG RAW tipleri T-SQL’deki LONG tipinden çok farklıdır. PL/SQL’de LONG ve LONG RAW 32760 byte’a kadar alfanümerik değer tutar. Dolayısıyla VARCHAR2 tipine benzerdirler. LONG ve LONG RAW tipleri arasında veri tabanına INSERT ve SELECT ile yapılan işlemlerde ve veri tutma biçimlerinde farklılıkları vardır. Örnek 6: LONG ve LONG RAW Tanımlama 1DECLARE 2 lx1 LONG; 3 lrx1 LONG RAW; 4 lx2 LONG; 5 lx3 LONG; 6 lrx2 LONG RAW; 7 lx4 LONG(3); 8 lrx3 LONG RAW(3); 9BEGIN 10 lx1 := ‘ABC’; 11 lrx1 := ‘ABC’; 12 lx3 := ‘ABC’; 13 lx4 := ‘ABC’; 14 lrx3 = ‘ABC’; 15 IF lx1 = lrx1 16 THEN 17 18 ELSE 19 20 END IF; 21 lx2 := CONCAT(lx1, lrx1); DBMS_OUTPUT.PUT_LINE(‘Esit’); DBMS_OUTPUT.PUT_LINE(‘Esit degil’); Oracle Üzerinde Programlama ve PL/SQL 22 DBMS_OUTPUT.PUT_LINE(lx2); 23 lx2 := CONCAT(lx1, lx3); 24 DBMS_OUTPUT.PUT_LINE(lx2); 25 lrx2 := CONCAT(lx1, lrx1); 26 DBMS_OUTPUT.PUT_LINE(lrx2); 27 END; Örnek 6’yı çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: Esit degil ABC0ABC ABCABC 0ABC0ABC 15 numaralı satırda LONG tipindeki, tuttuğu değer “ABC” olan lx1 değişkeniyle LONG RAW tipindeki, tuttuğu değer “ABC” olan lrx1 değişkeni karşılaştırıldığında FALSE sonucu oluşur ve 18 numaralı satırdaki ELSE bloğuna düşer. Dolayısıyla LONG ve LONG RAW değişkenleri doğrudan karşılaştırmak istediğimiz sonucu üretmez. 21 numaralı satırda LONG tipindeki lx1 ile lrx1 değişkenlerini CONCAT() fonksiyonuyla birleştirip LONG tipindeki lx2 değişkenine atadık. 22 numaralı satırda lx2 değişkeninin değerinin “ABC0ABC” olduğunu gördük. 23 numaralı satırda LONG tipindeki lx2 değişkenine LONG tipindeki lx1 ve lx3 değişkenlerinin birleştirilmesi sonucu oluşan değeri atadık ve 24 numaralı satırda lx2 değişkenininin değerinin “ABCABC” olduğunu gördük. Benzer şekilde 25 numaralı satırda bu sefer LONG RAW tipindeki lrx2 değişkeninin içine LONG tipindeki lx1 ve LONG RAW tipindeki lrx1 değişkenlerinin birleştirilmesi sonucu oluşan değeri atadık ve 26 numaralı satırda sonucun “0ABC0ABC” olduğunu gördük. Dolayısıyla LONG tipindeki değişkenlerle LONG RAW tipindeki değişkenleri bir arada kullanırken dikkatli olmakta fayda vardır. Örnek 7: LONG ve LONG RAW Tiplerinin CHAR ile Karşılaştırılması 1DECLARE 2 lx1 LONG(3); 3 lrx1 LONG RAW(3); 4 cx1 CHAR(3); 5BEGIN 6 lx1 := ‘ABC’; 7 lrx1 := ‘ABC’; 8 cx1 := ‘ABC’; 9 IF lx1 = lrx1 10 THEN 11 12 ELSE 13 14 END IF; 15 IF lx1 = cx1 16 THEN 17 18 ELSE 19 DBMS_OUTPUT.PUT_LINE(‘Esit’); DBMS_OUTPUT.PUT_LINE(‘Esit degil’); DBMS_OUTPUT.PUT_LINE(‘Esit’); DBMS_OUTPUT.PUT_LINE(‘Esit degil’); 203 204 Bölüm 11 20 END IF; 21 IF cx1 = lrx1 22 THEN 23 24 ELSE 25 26 END IF; 27 END; DBMS_OUTPUT.PUT_LINE(‘Esit’); DBMS_OUTPUT.PUT_LINE(‘Esit degil’); Örnek 7’yi çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: Esit degil Esit Esit degil Bu örnekten de anlaşılabileceği gibi CHAR tipindeki verilerle LONG tipindeki veriler karşılaştırılabilirken LONG RAW tipindeki verilerin doğrudan karşılaştırılması istediğimiz sonuçları vermeyebilir. ROWID ve UROWID T-SQL’de olmayan tiplerdir. ROWID ve UROWID herhangi bir tablodaki kaydın yeri hakkında bilgi tutarlar. Bu bilgi fiziksel ve mantıksal olmak üzere 2 şekildedir. Fiziksel rowid herhangi bir tabloda satır için ayırdedici bir özelliktir. Mantıksal rowid üzerinde index olan bir tablo için ayırdedicidir. UROWID hem fiziksel hem de mantıksal rowid tutabilir. Eğer yeni bir uygulama yazıyorsanız ve bu tip bir veriye ihtiyacınız varsa UROWID tipini tercih etmek daha iyi olacaktır. Örneğin ROWID ve UROWID tiplerini tüm değerleri aynı olan 2 satırdan birini silerken kullanabilirsiniz. Örnek 8: ROWID ve UROWID Tipleri 1DECLARE 2 rx1 ROWID; 3 urx1 UROWID; 4BEGIN 6 SELECT 7 rowid 8 INTO rx1 9 FROM Urun 10 11 SELECT 12 rowid 13 INTO urx1 14 FROM Urun 15 WHERE UrunId = 2; 16 DBMS_OUTPUT.PUT_LINE(rx1); 17 DBMS_OUTPUT.PUT_LINE(urx1); 18 IF rx1 = urx1 19 THEN 20 21 ELSE WHERE UrunId = 2; DBMS_OUTPUT.PUT_LINE(‘Esit’); Oracle Üzerinde Programlama ve PL/SQL 22 DBMS_OUTPUT.PUT_LINE(‘Esit degil’); 23 END IF; 24 END; Örnek 8’i çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: AAAM2eAABAAAO5aAAD AAAM2eAABAAAO5aAAD Esit Bu örnekten ROWID tipindeki rx1 ve UROWID tipindeki urx1 değerlerinin esit olduğunu görebiliriz. Bununla birlikte DBMS çıktısındaki “AAAM2eAABAAAO5aAAD” ifadeleri sizde farklı değerlerde olacaktır. Bu karakter dizisi bir anlam taşır. İlk 6 karakter (örneğimizde “AAAM2e”) veri tabanı segmentini ifade eder. Bu gruba nesne numarası (object number) denir. Sonraki 3 karakter (örneğimizde “AAB”) satırın bulunduğu fiziksel dosyayı ifade eder. Bu gruba dosya numarası (file number) denir. Sonraki 6 karakter (örneğimizde “AAAO5a”) veri dosyasının içindeki bloğu ifade eder. Aynı tablespace içinde olan fakat farklı veri dosyasında olan 2 satır aynı blok numarasına sahip olabilir. Bu gruba blok numarası (block number) denir. Son 3 karakter de (örneğimizde “AAD”) blok içindeki satır numarasını ifade eder. Bu gruba satır numarası (row number) denir. Örnek 9: ROWID ve UROWID Tipleri 1DECLARE 2 rx1 ROWID; 3 rx2 ROWID; 4BEGIN 6 SELECT 7 rowid 8 INTO rx1 9 FROM Urun 10 11 SELECT 12 rowid 13 INTO rx2 14 FROM Urun 15 WHERE UrunId = 3; 16 DBMS_OUTPUT.PUT_LINE(rx1); 17 DBMS_OUTPUT.PUT_LINE(rx2); 18 END; WHERE UrunId = 2; Örnek 9’u çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: AAAM2eAABAAAO5aAAD AAAM2eAABAAAO5aAAA Bu çıktıdan Urun tablosundaki UrunId değerleri 2 ve 3 olan satırların aynı segment’te, aynı veri dosyasında, aynı blok içindeki farklı yerlerde olduğunu görebiliriz. VARCHAR2 Değişken uzunluklu alfanümerik verileri tutmak için kullanılır. Verinin VARCHAR2 tipinde nasıl tutulduğu veri tabanının karakter setine göre değişiklik gösterir. En fazla 32767 karakter tutulabilir. 205 206 Bölüm 11 2000 byte’tan uzun değerlerde performans düşüşü yükselir. Bu sebeple 2000 byte’tan daha uzun verileri tutarken bu durumu dikkate almakta fayda vardır. STRING ve VARCHAR veri tipleri VARCHAR2 veri tipinin alt tipleridir. VARCHAR ile VARCHAR2 veri tipleri aynıdır. VARCHAR veri tipinin varlık sebebi ileri sürümlerde farklı şekilde kullanılabilecek ayrılmış bir tip yaratmaktır. Bu sebeple VARCHAR2 veri tipini kullanmak daha iyi olacaktır. Örnek 10: VARCHAR2 Tipi ve Diğer Alfanümerik Tiplerle Karşılaştırma 1DECLARE 2 vx1 VARCHAR2(10); 3 cx1 CHAR(10); 4 sx1 STRING(10); 5 lx1 LONG(10); 6BEGIN 7 vx1 := ‘ABC’; 8 cx1 := ‘ABC’; 9 sx1 := ‘ABC’; 10 lx1 := ‘ABC’; 11 IF vx1 != cx1 12 THEN 13 14 ELSE 15 16 END IF; 17 IF vx1 != sx1 18 THEN 19 20 ELSE 21 22 END IF; 23 IF vx1 != lx1 24 THEN 25 26 ELSE 27 28 END IF; 29 END; DBMS_OUTPUT.PUT_LINE(‘vx1 != cx1’); DBMS_OUTPUT.PUT_LINE(‘vx1 = cx1’); DBMS_OUTPUT.PUT_LINE(‘vx1 != sx1’); DBMS_OUTPUT.PUT_LINE(‘vx1 = sx1’); DBMS_OUTPUT.PUT_LINE(‘vx1 != lx1’); DBMS_OUTPUT.PUT_LINE(‘vx1 = lx1’); Örnek 10’u çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: vx1 != cx1 vx1 = sx1 vx1 = lx1 Bu örnekten görebileceğimiz gibi VARCHAR2 tipiyle CHAR tipini karşılaştırmak sakıncalıdır. Her ne kadar LONG tipiyle karşılaştırmak eşit sonucunu verse de LONG tipi ile VARCHAR2 tipinini kapasitelerinin farklı olması sebebiyle bu tip bir karşılaştırmayı tercih etmemekte fayda vardır. Oracle Üzerinde Programlama ve PL/SQL PL/SQL’de Mantıksal Tipler PL/SQL’de 1 tane mantıksal tip bulunmaktadır. Bu tip BOOLEAN tipidir. Mantıksal bir tip TRUE ya da FALSE değerlerinden birini tutar. BOOLEAN PL/SQL’de BOOLEAN veri tipi TRUE ya da FALSE değerlerini tutar; ancak dikkat edilmesi gereken nokta BOOLEAN bir veri tipini DBMS_OUTPUT.PUT_LINE() fonksiyonu ile yazdıramayız. Örnek 11: BOOLEAN Veri Tipi 1DECLARE 2 bx BOOLEAN; 3BEGIN 4 bx := TRUE; 5 IF bx = TRUE 6 THEN 7 8 ELSIF bx = FALSE 9 THEN 10 11 ELSE 12 13 END IF; 14 END; DBMS_OUTPUT.PUT_LINE(‘bx = TRUE’); DBMS_OUTPUT.PUT_LINE(‘bx = FALSE’); DBMS_OUTPUT.PUT_LINE(‘bx = null’); Örnek 10’u çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: bx = TRUE Bu örnekte 10 numaralı satırdaki ELSE bloğunu eklemimizin sebebi eğer bx değişkeni tanımlanmazsa ya da bx değişkenine null değeri atanırsa 11 numaralı satırdaki ELSE bloğuna girilecek ve DBMS çıktısı olarak “bx = null” yazılacaktır. PL/SQL’de Tarih ve Zaman Tipleri Tarih ve zaman tipleri herhangi bir tarihi ya da saati tutar. PL/SQL’de pek çok tarihsel gösterim şekli olmasına rağmen bu tipleri temel olarak inceleyeceğiz. DATE DATE veri tipi Saniye detayında tarih ve zaman verisi tutar. Örnek 12: DATE Veri Tipi 1DECLARE 2 dx DATE; 3BEGIN 4 dx := SYSDATE; 5 DBMS_OUTPUT.PUT_LINE(dx); 6END; Örnek 12’yi çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 02-DEC-07 207 208 Bölüm 11 SYSDATE() fonksiyonu T-SQL’deki GETDATE() fonksiyonuna karşılık gelmektedir ve sistem tarih ve zamanını getirir. Bu noktadan itibaren INTERVAL yapısına geçebiliriz. Örnek 13: INTERVAL YEAR TO MONTH 1DECLARE 2 BaslangicTarihi DATE; 3 BitisTarihi DATE; 4 ix INTERVAL YEAR(3) TO MONTH; 5 cYilFarki CHAR(3); 6 cAyFarki CHAR(2); 7 iYilFarki INT; 8 iAyFarki INT; 7BEGIN 8 BaslangicTarihi := ’01-JAN-00’; 9 BitisTarihi := SYSDATE; 10 DBMS_OUTPUT.PUT_LINE(‘Gun Farki: ’ || TO_ CHAR(BitisTarihi - BaslangicTarihi)); 11 365); iYilFarki := FLOOR((BitisTarihi - BaslangicTarihi) / 12 cYilFarki := TO_CHAR(iYilFarki); 13 iAyFarki := FLOOR(((BitisTarihi - BaslangicTarihi) – iYilFarki * 365) / 30); 14 cAyFarki := TO_CHAR(iAyFarki); 15 ix := CONCAT(CONCAT(TRIM(iYilFarki), ‘-‘), TRIM(iAyFarki)); 16 17 END; DBMS_OUTPUT.PUT_LINE(ix); Örnek 13’ü çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: Gun Farki: 2892 +007-11 Bu örnekten görebileceğimiz gibi uygulamanın çalıştığı tarih (2 Aralık 2007) ile 1 Ocak 2000 tarihi arasında 2892 gün vardır. 2892 gün de 7 yıl 11 aya karşılık gelmektedir. Bu örnekte TO_CHAR() fonksiyonu herhangi bir tipi CHAR tipine çeviren fonksiyondur. BOOLEAN tipini CHAR tipine çeviremez. TO_CHAR() fonksiyonunu T-SQL’deki CONVERT() fonksiyonunun özelleşmiş bir hali olarak düşünebiliriz. FLOOR() fonksiyonu burada, ondalıklı sayının ondalıklı kısmını atarak tam sayı kısmını almamızı sağlar. Bu örnekten de görebileceğiniz gibi INTERVAL YEAR TO MONTH tipine CHAR tipinde YIL-AY değerini alabilmektedir. Örnek 14: INTERVAL YEAR TO MONTH 1DECLARE 2 ix1 INTERVAL YEAR(3) TO MONTH; 3 ix2 INTERVAL YEAR(3) TO MONTH; 4 ix3 INTERVAL YEAR(3) TO MONTH; 5 ix4 INTERVAL YEAR(3) TO MONTH; 6 dx DATE; Oracle Üzerinde Programlama ve PL/SQL 7BEGIN 8 ix1 := INTERVAL ’10-7’ YEAR TO MONTH; 9 ix2 := INTERVAL ’10’ YEAR; 10 ix3 := INTERVAL ’7’ MONTH; 11 ix4 := ix2 + ix3; 12 DBMS_OUTPUT.PUT_LINE(ix4); 13 dx := SYSDATE; 14 dx := dx + ix4; 15 DBMS_OUTPUT.PUT_LINE(dx); 12 END; Örnek 14’ü çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: +010-07 02-JUL-18 Bu örnekten görülebileceği gibi 11 numaralı satırda 2 INTERVAL YEAR TO MONTH toplanabilmektedir. 14 numaralı satırda da INTERVAL YEAR TO MONTH tipinin DATE tipiyle toplanabildiğini görüyoruz. Bu örnekteki INTERVAL YEAR TO MONTH tipindeki değişkenler tanımlanırken YEAR ifadesinin yanındaki 3 rakamı en fazla 3 haneli yıl değeri taşıyabileceğini gösterir ve en fazla 4 olabilir. Benzer şekilde kullanabileceğimiz INTERVAL DAY TO SECOND tipi de mevcuttur. DATE ve INTERVAL İşlemleri 1. Argüman İşlem 2. Argüman Sonuç Tipi DATE + INTERVAL DATE DATE - INTERVAL DATE INTERVAL + DATE DATE DATE - DATE INTERVAL INTERVAL + INTERVAL INTERVAL INTERVAL - INTERVAL INTERVAL INTERVAL * NUMERIC INTERVAL NUMERIC * INTERVAL INTERVAL INTERVAL / NUMERIC INTERVAL TIMESTAMP TIMESTAMP veri tipi DATE veri tipinin bir varyasyonudur. DATE tipi gibi kullanılabilir. Örnek 15: TIMESTAMP Veri Tipi 1DECLARE 2 tx TIMESTAMP; 3BEGIN 4 tx := SYSDATE; 5 DBMS_OUTPUT.PUT_LINE(tx); 6 tx := ’05-JUL-07 10:42:35.313 PM’ 7END;4 209 210 Bölüm 11 Örnek 15’i çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 02-DEC-07 12.00.00.000000000 6 numaralı satırdaki gibi TIMESTAMP tipine değer ataması yapılabilir. Bütünleşik Tipler Bütünleşik tipler herhangi bir basit tipten, referans tipinden ya da LOB tipinden türetilebilen dizilerdir. Örnek 16: VARRAY Veri Tipi 1DECLARE 2 TYPE PIXA IS VARRAY(4) OF PLS_INTEGER; 3 px PIXA; 4BEGIN 5 px := PIXA(0, 0, 0, 0); 6 px(1) := 3; 7 px(2) := 4; 8 DBMS_OUTPUT.PUT_LINE(px(1)); 9 DBMS_OUTPUT.PUT_LINE(px(2)); 10 END; Örnek 16’yı çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 3 4 Bu örnekte göreceğiniz gibi bir diziyi kullanmadan önce bir tip olarak tanımlamamız gerekmektedir. 2 numaralı satırda bu tanım yapılmaktadır. En genel şekilde tip tanımlaması şu şekildedir: TYPE <yeni tip adı> IS VARRAY(<eleman sayısı>) OF <tip> Burada yeni tip adı oluşturduğumuz dizi tipine verdiğimiz addır, tip ise mevcut tiplerden biridir. Dizi indis değerleri 1’den başlar. Bu örnekte gördüğümüzün dışında bütünleşik tip tanımları da söz konusudur. Örnek 17: Bileşik Tipler 1DECLARE 2 TYPE TBLARRAY IS TABLE OF VARCHAR2(3); 3 TYPE TBLARRAY2 IS TABLE OF PLS_INTEGER INDEX BY PLS_ INTEGER; 4 TYPE TBLARRAY3 IS TABLE OF PLS_INTEGER INDEX BY VARCHAR(3); 5 tx1 TBLARRAY; 6 tx2 TBLARRAY2; 7 tx3 TBLARRAY3; 8BEGIN 9 tx1 := TBLARRAY(‘’, ‘’, ‘’); 10 tx1(1) := ‘ABC’; 11 tx1(2) := ‘DEF’; Oracle Üzerinde Programlama ve PL/SQL 12 DBMS_OUTPUT.PUT_LINE(tx1(1)); 13 DBMS_OUTPUT.PUT_LINE(tx1(2)); 14 tx2(1) := 10; 15 tx2(2) := 15; 16 DBMS_OUTPUT.PUT_LINE(tx2(1)); 17 DBMS_OUTPUT.PUT_LINE(tx2(2)); 18 tx3(‘abc’) := 20; 19 tx3(‘def’) := 25; 20 DBMS_OUTPUT.PUT_LINE(tx3(‘abc’)); 21 DBMS_OUTPUT.PUT_LINE(tx3(‘def’)); 22 END; Örnek 17’yi çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: ABC DEF 10 15 20 25 Bu örnekte göreceğiniz gibi PL/SQL, herhangi bir genel amaçlı programlama diline göre bile zengin sayılabilecek seçenekler sunmaktadır. 2 numaralı satırda gördüğümüz tanımlama Örnek 16’ya göre eleman sayısı tanımlamamaktadır. Böylece TBLARRAY tipinde tanımladığımız herhangi bir değişkenin eleman sayısı sınır lı olmaktan çıkmıştır; ancak hala Örnek 16’daki gibi 9 numaralı satırda TBLARRAY tipinde tanımladığımız değişkene ilk değerlerini vermek durumundayız. TBLARRAY tipi PLS_INTEGER tipindeki değerlerden oluşan bir dizi tipidir. 3 numaralı satırda, TBLARRAY tipine göre oluşturduğumuz farklılık INDEX BY ifadesidir. INDEX BY ifadesi dizi tipini tanımlarken indis değerlerinin hangi tipte olduğunu tanımlamaya yarar. TBLARRAY2 tipinde ilk PLS_INTEGER ifadesi, TBLARRAY2 tipinden oluşturulacak bir değişkenin PLS_INTEGER tipinde değerler tutacağını gösterirken, satırın sonundaki INDEX BY PLS_INTEGER ifadesi de değişkenin indis değerlerinin PLS_INTEGER ile gösterileceğini ifade eder. 14 ve 15 numaralı satırlarda gördüğünüz gibi tx2 tipinin indisleri de değerleri de PLS_INTEGER tipindedir. Ayrıca bu tanımla artık dizinin ilk değerlerini önceden vermek zorunluluğumuz ortadan kalktı. 4 numaralı satırdaki tip tanımında ise 3 numaralı satırdan sadece değişken indislerinin nasıl tutulacağı değişmiştir. TBLARRAY3 tipinde tanımlanan bir dizinin indisleri VARCHAR2(3) tipinde olacaktır ve içinde PLS_INTEGER tipinde değerler tutacaktır. Buna ait kullanımı da 18 ve 19 numaralı satırlarda görebilirsiniz. Herhangi bir dizide dizinin ilk elemanına erişmek için FIRST, son elemanına erişmek için de LAST ifadelerini kullanabiliriz. Bununla ilgili örneği Örnek 18’de görebilirsiniz: Örnek 18: FIRST ve LAST 1DECLARE 2 TYPE TBLARRAY IS TABLE OF VARCHAR2(3); 3 TYPE TBLARRAY2 IS TABLE OF PLS_INTEGER INDEX BY PLS_ INTEGER; 4 TYPE TBLARRAY3 IS TABLE OF PLS_INTEGER INDEX BY VARCHAR(3); 5 tx1 TBLARRAY; 6 tx2 TBLARRAY2; 211 212 Bölüm 11 7 tx3 TBLARRAY3; 8BEGIN 9 tx1 := TBLARRAY(‘’, ‘’, ‘’); 10 tx1(1) := ‘ABC’; 11 tx1(2) := ‘DEF’; 12 DBMS_OUTPUT.PUT_LINE(tx1(1)); 13 DBMS_OUTPUT.PUT_LINE(tx1(tx1.LAST)); 14 tx2(1) := 10; 15 tx2(2) := 15; 16 DBMS_OUTPUT.PUT_LINE(tx2(tx2.FIRST)); 17 DBMS_OUTPUT.PUT_LINE(tx2(2)); 18 tx3(‘abc’) := 20; 19 tx3(‘def’) := 25; 20 DBMS_OUTPUT.PUT_LINE(tx3(‘abc’)); 21 DBMS_OUTPUT.PUT_LINE(tx3(tx3.LAST)); 22 END; Örnek 18’i çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: ABC 10 15 20 25 Bu örnekte 13 numaralı satırın çalışmasından sonucu oluşmaktadır; çünkü 9 numaralı satırda dizinin 3. elemanını olarak tanımladık. RECORD RECORD tipi T-SQL’de olmayan bir tiptir. C#’taki struct gibi düşünülebilir. Örnek 19: RECORD Tanımlama 1DECLARE 2 TYPE REC_URUN IS RECORD(ID INTEGER, AD VARCHAR2(10)); 3 rUrun REC_URUN; 4BEGIN 5 SELECT; 6 UrunId, UrunAdi 7 INTO rUrun 8 FROM Urun 9 WHERE ROWNUM = 1; 10 DBMS_OUTPUT.PUT_LINE(‘Urun ID: ’ || rUrun.ID || ‘ Adi: ’ || rUrun.AD); 11 END; Oracle Üzerinde Programlama ve PL/SQL Örnek 19’u çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: Urun ID: 1 Adi: Bisküvi 2 numaralı satırda INTEGER tipinde bir değer ve VARCHAR2 tipinde bir değer alan REC_URUN adında bir RECORD oluşturduk. 3 numaralı satırda bu tipte bir değişken tanımladık. 5, 6, 7, 8 ve 9 numaralı satırlardan oluşan SELECT cümleciği ile rUrun değişkeninin içine ID ve AD bilgileri atadık. SELECT cümleciğinde T-SQL’de olmayan bir alan olan ROWNUM’ı kullanıdk. ROWNUM, SELECT ifadesinin sonucunda oluşan kümenin sıra numarasını alan bir değerdir. Dolayısıyla ROWNUM = 1 olduğu için ilk satır verilerini RECORD’ın içine atmış durumdayız. 10 numaralı satırda da bu değerleri tek tek yazdırdık. RECORD, CURSOR ile birlikte kullanıldığında daha güçlü bir yapıyla karşı karşıya kalacağız. Referans Tipleri Referans tipleri bir işaretçi aracılığıyla bellekte asıl veri bloğunun tutulduğu adresi gösteren veri tipidir. Esnek tipler olışturabilmek açısından oldukça önemlidir. REF CURSOR REF CURSOR tipine geçmeden önce daha basit bir tip olan CURSOR tipinden bahsetmek gerekir. CURSOR tipi T-SQL’deki CURSOR’lara karşılık gelir; ancak kullanımında çok sayıda detay vardır. Mantıksal olarak da T-SQL’deki CURSOR’larla aynı mantıkta çalışır. Örnek 20: CURSOR Tanımlama 1DECLARE 2 CURSOR curUrun IS 3 SELECT 4 UrunId, UrunAdi 5 FROM Urun; 6 ID INTEGER; 7 AD VARCHAR2(10); 8BEGIN 9 OPEN curUrun; 10 LOOP 11 FETCH curUrun INTO ID, AD; 12 EXIT WHEN NOT curUrun%FOUND; 13 14 END LOOP; 15 CLOSE curUrun; 16 END; DBMS_OUTPUT.PUT_LINE(ID || ‘ – ‘ || AD); Örnek 20’yi çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 1 - Bisküvi 2 - Çikolata 2, 3, 4 ve 5 numaralı satırlar CURSOR tanımını oluşturur. 3, 4 ve 5 numaralı satırlardaki SELECT cümleciğinin sonucu curUrun adlı CURSOR içine satır satır atılacaktır; ancak bu işlem bu bölümde yapılmaz. Burada sadece CURSOR için tanım mevcuttur. 9 numaralı satırda CURSOR açıldıktan sonra, bir sonraki konuda işleyeceğimiz LOOP döngüsü içinde CURSOR tanımına göre satır satır işleme yapılacaktır. 11 numaralı satırda ilk staır CURSOR değişkenleri olan ID ve AD değişkenle- 213 214 Bölüm 11 rine alınır. 12 numaralı satır LOOP döngüsünün curUrun içinde satır bulunamadığında çıkması için yazılmıştır. curUrun%FOUND ifadesi T-SQL’deki CURSOR için kullandığımız WHILE döngüsündeki @@FETCH_STATUS != -1 ifadesi ile aynı şekilde çalışır. 13 numaralı satırda CURSOR değişkenleri DBMS çıktısı olarak yazılır. 15 numaralı satırda da CURSOR kapatılır. OPEN ile açılan bir CURSOR belleğin etkin kullanımı açısından mutlaka CLOSE ile kapatılmalıdır. Örnek 20: T-SQL’de CURSOR Tanımlama 1DECLARE @ID INT, @AD VARCHAR(10) 2 DECLARE curUrun INSENSITIVE CURSOR FOR 3 SELECT 4 UrunId, UrunAdi 5 FROM Urun 6 OPEN vurUrun 7 FETCH NEXT FROM curUrun INTO @ID, @AD 8 WHILE @@FETCH_STATUS != -1 9 BEGIN 10 PRINT CONVERT(VARCHAR, @ID) + ‘ – ‘ + @AD 11 FETCH NEXT FROM curUrun INTO ID, AD 12 END 13 CLOSE curUrun 14 DEALLOCATE curUrun Örnek 20.1’de Örnek 20’deki PL/SQL ile kullanılan CURSOR yapısnın T-SQL karşılığını görebilirsiniz. Örnek 21: CURSOR Değerlerinin RECORD İçine Atılması 1DECLARE 2 TYPE REC_URUN IS RECORD (ID INTEGER, AD VARCHAR2(10)); 3 TYPE REC_URUN_ARR IS TABLE OF REC_URUN INDEX BY PLS_ INTEGER; 4 rUrun REC_URUN; 5 rUrunArr REC_URUN_ARR; 6 CURSOR curUrun IS 7 SELECT 8 UrunId, UrunAdi 9 FROM Urun; 10 ROWN PLS_INTEGER; 11 BEGIN 12 OPEN curUrun; 13 ROWN := 1; 14 LOOP 15 FETCH curUrun INTO rUrun; 16 EXIT WHEN NOT curUrun%FOUND; 17 rUrunArr(ROWN) := rUrun; 18 DBMS_OUTPUT.PUT_LINE(rUrunArr(ROWN).ID || ‘ – ‘ || rUrunArr(ROWN).AD); Oracle Üzerinde Programlama ve PL/SQL 19 END LOOP; 20 CLOSE curUrun; 21 END; Örnek 21’i çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 1 - Bisküvi 2 - Çikolata 2 numaralı satırda CURSOR için çalışan SELECT sorgusunun kayıt desenine uygun şekilde bir RECORD oluşturduk. 3 numaralı satırda da CURSOR üzerinden REC_URUN tipindeki kayıtlarımızı toplayacağımız değişken uzunluklu, PLS_INTEGER indisli, REC_URUN tipinde bir dizi tipi tanımladık. 4 numaralı satırda CURSOR’dan dönen her satırı tutacağımız kayıt değişkenini tanımladık. 5 numaralı satırda da bu kayıt değişkenlerinin hepsini toplayacağımız diziyi tanımladık. 10 numaralı satırda REC_URUN_ARR tipindeki dizinin indisini tutmak için PLS_INTEGER tipinde bir değişken tanımladık. 13 numaralı satırda dizi indisleri 1’den başladığı için ROWN değerini 1 yaptık. 15 numaralı satırda Örnek 20’ye göre farklı olarak CURSOR’dan dönen kaydı olduğu gibi REC_URUN tipindeki rUrun adlı kayıt değişkenine atadık. 17 numaralı satırda da REC_URUN_ARR tipindeki rUrunArr dizisinin ROWN numaralı indisindeki alana rUrun kaydını yerleştirdik. 18 numaralı satırda rUrunArr(ROWN).ID ve rUrunArr(ROWN).AD ifadelerini rUrunArr dizisinin her elemanının REC_URUN kayıt tipinde olmasından dolayı, kayıt deseninde tanımlı değişkenlerle kullanabildik. Örnek 22: REF CURSOR Değerlerinin RECORD İçine Atılması 1DECLARE 2 TYPE REC_URUN IS RECORD (ID INTEGER, AD VARCHAR2(10)); 3 TYPE URUN_CUR_TYPE IS REF CURSOR RETURN REC_URUN; 4 rUrun REC_URUN; 5 cUrunType URUN_CUR_TYPE; 6BEGIN 7 OPEN cUrunType FOR 8 SELECT 9 UrunId, UrunAdi 10 11 LOOP 12 FETCH cUrunType INTO rUrun; 13 EXIT WHEN NOT cUrunType%FOUND; 14 rUrun.AD); DBMS_OUTPUT.PUT_LINE(rUrun.ID || ‘ – ‘ || 15 END LOOP; 16 CLOSE cUrunType; 17 END; FROM Urun; Örnek 22’yi çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 1 - Bisküvi 2 - Çikolata 2 numaralı satırda Örnek 21’deki gibi RECORD tipinde kayıt desenimizi tanımladık. 3 numaralı satırda da REF CURSOR tipimizi tanımladık. 4 ve 5 numaralı satırlarda bu tiplere ait değişkenlerimizi oluşturduk. 7, 8, 9 ve 10 numaralı satırlarda URUN_CUR_TYPE adında REF CURSOR tipinde 215 216 Bölüm 11 REC_URUN tipinde değer döndüren CURSOR yapımızı hem tanımladık, hem de açtık. Bu yapı TSQL’deki CURSOR yapısına daha çok benzemektedir; ancak T-SQL’de RECORD yapısına benzer yapılar olmadığı için bu örneğin bire bir T-SQL karşılığı yoktur. 12 numaralı satırda oluşturduğumuz REF CURSOR tipinin içindeki değeri REC_URUN tipinin içine yerleştirdik. 14 numaralı satırda da o satır için rUrun adlı kayıttaki ID ve AD alanlarının değerlerini yazdırdık. LOB (Large Object) Tipleri Yapısal olmayan büyük verilerin tutulması için kullanışlı tiplerdir. En fazla tutabilecekleri veri miktarı 4GB’tır. Asıl verilerini işaretçi mantığıyla tutarlar. BFILE BFILE veri tipi, büyük verilerinin veri tabanı üzerinde değil işletim sisteminin üzerindeki dosya sistemi üzerinde tutulmasını sağlayan bir tiptir. BFILE aslında bu fiziksel dosyaya ait adresi tutar. BFILE tipindeki veri değiştirilemez. Kullanımı transaction mantığı çerçevesinde risklidir; çünkü transaction ile çalışan bir tip değildir ve transaction’a dahil edilmezler. Replikasyon ile replike edilemezler. BLOB (Binary Large Object) BLOB veri tipinin BFILE veri tipinden farkları, tuttuğu verinin veri tabanı dosyaları içinde tutulması ve transaction’a dahil olması ve replike edilebilir olmasıdır. BLOB veri tipinde veri binary olarak saklanır. Microsoft SQL Server 2005 ile birlikte gelen BLOB tipi gibidir. CLOB (Character Large Object) CLOB veri tipinin BLOB veri tipinden farkı tuttuğu verinin binary yerine alfanümerik olmasıdır. Diğer özellikleri BLOB tipi gibidir. Microsoft SQL Server 2005 ile birlikte gelen CLOB tipi gibidir. NCLOB NCLOB veri tipinin CLOB veri tipinden farkı NCHAR tipinden oluşmasıdır. Bunun anlamı yerel alfabelerin de rahatlıkla kullanılabileceğidir. PL/SQL’de Döngü Yapıları LOOP Döngüsü LOOP döngüsü normalde koşulsuz bir döngü tipidir. EXIT ifadesi işletilene kadar döngü tekrarlamaya devam eder. LOOP ile CURSOR konusunda da örnek görmüştük. Örnek 23: LOOP Döngüsü 1DECLARE 2 ix PLS_INTEGER; 3BEGIN 4 ix := 1; 5 LOOP 6 IF ix != 5 7 THEN 8 ix := ix + 1; 9 DBMS_OUTPUT.PUT_LINE(ix); 10 ELSE 11 12 END IF; 13 END LOOP; 14 END; EXIT; Oracle Üzerinde Programlama ve PL/SQL Örnek 23’ü çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 2 3 4 5 Bu örnekte dikkat etmemiz gereken en önemli nokta 4 numaralı satırda eğer ix değişkenine değer ataması yapmazsak 6 numaralı satırdaki karşılaştırmadan beklenmedik şekilde FALSE değeri üretilmesidir. Bu sebeple LOOP döngü yapısını kullanırken kontrol değişkenine ilk değer atamasını yapmak önemlidir. 5 ile 13 numaralı satırlar arasında tanımlanan LOOP döngüsünden çıkmak için bu blok içinde kod akışında mutlaka EXIT ifadesinin çalışması gerekir; aksi takdirde sonsuz döngü oluşacaktır. Örneğimizde eğer ix değişkeninin değeri 5’e eşit olursa 10 numaralı satırdaki ELSE bloğu çalışır ve EXIT ifadesi de böylece çalışmış olur ve LOOP döngüsü sonlandırılarak program akışı 13 numaralı satırdan itibaren devam eder. EXIT WHEN İfadesi LOOP döngüsünde koşullu çıkışı sağlamak için kullanılan bir ifadedir. IF bloğu yerine kullanılabilir. Örnek 24: LOOP Döngüsü ve EXIT WHEN İfadesi 1DECLARE 2 ix PLS_INTEGER; 3BEGIN 4 ix := 1; 5 LOOP 6 EXIT WHEN ix = 5; 7 ix := ix + 1; 8 END LOOP; 9END; Örnek 24’ü çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 2 3 4 5 Bu örnekte LOOP döngüsü her döndüğünde 6 numaralı satırda EXIT WHEN ix = 5 ifadesiyle karşılaşarak ix değerinin 5’e eşit olup olmadığını kontrol edecektir. Eğer ix 5’e eşitse EXIT ifadesi çalışacak ve program akışı 8 numaralı satırdan devam edecektir. WHILE – LOOP Döngüsü WHILE – LOOP döngüsü EXIT WHEN ifadesi ile kullanılan LOOP döngüsüne çok benzerdir. Bu döngü tipinde WHILE ile tanımlanan koşul sağlandığı sürece döngü iterasyona devam eder. Örnek 25: WHILE - LOOP Döngüsü 1DECLARE 2 3BEGIN ix PLS_INTEGER; 217 218 Bölüm 11 4 ix := 1; 5 WHILE ix < 5 6 LOOP 7 8 END LOOP; ix := ix + 1; 9END; Örnek 25’i çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 2 3 4 5 Bu örnekte 5 numaralı satırdaki ix < 5 koşulu sağlandığı sürece program akışı 8 numaralı satıra gelmez. Dolayısıyla koşul sağlanana kadar kaç defa çalışacağını bilmediğimiz yapılarda kullanılması uygundur. EXIT ifadesinin olmaması sebebiyle koşullu çıkışın sağlanması semantik olarak sağlandığı için LOOP döngüsüne tercih edilmelidir. WHILE ifadesinin yanındaki koşulun tipi mutlaka BOOLEAN olmalıdır. 0 (sıfır) dışındaki tüm değerler TRUE, 0 (sıfır) FALSE’tur gibi bir durum söz konusu değildir. FOR – LOOP Döngüsü FOR – LOOP döngüsü de WHILE – LOOP döngüsüne benzer yapıdadır; ancak en önemli farkı koşula bağlı olmaktan çok iterasyon sayısının bilindiği yapılarda kullanılır. WHILE ile geçişkendir. Örnek 26: FOR - LOOP Döngüsü 1BEGIN 2 FOR ix IN 2..5 3 LOOP 4 5 END LOOP; DBMS_OUTPUT.PUT_LINE(ix); 6END; Örnek 26’yı çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 2 3 4 5 Bu örnekte WHILE – LOOP döngüsüne göre kontrol ettiğimizde DECLARE bölümünde bir değişken tanımlamaya ihtiyacımız yoktur. 2 numaralı satırdaki FOR ix IN 2..5 ifadesi ile ix değişkeni otomatikman tanımlanmış olur. 2 numaralı satırın anlamı, 2’den 5’e kadar olan sayıları her döngüde 1’er artırarak ix değişkenine atama yapmaktır. Örnek 27: FOR - LOOP Döngüsü 1BEGIN 2 FOR ix IN REVERSE 2..5 3 LOOP Oracle Üzerinde Programlama ve PL/SQL 4 DBMS_OUTPUT.PUT_LINE(ix); 5 END LOOP; 6END; Örnek 27’yi çalıştırdığımızda aldığımız DBMS çıktısı şu şekildedir: 5 4 3 2 Bu örnekte 2 numaralı satırda IN ifadesinden sonra kullandığımız REVERSE ifadesi döngünün 2’den 5’e kadar 1’er artırarak değil, 5’ten 2’ye kadar 1’er eksilterek dönmesini sağlayacaktır ve her seferinde ix değişkenine 5’ten itibaren 1’er eksilterek değer ataması yapacaktır. Procedure Oluşturmak PL/SQL’de procedure olışturmak T-SQL’den çok farklı değildir. PL/SQL’de Procedure’lar TSQL’deki SP (stored procedure)’ler gibi IN, OUT ve IN OUT yönlü parametrelere sahiptirler, PL/SQL’deki procedure’larda RETURN yoktur, bunun yerine FUNCTION kullanılır. Örnek 28: PL/SQL’de Procedure 1CREATE OR REPLACE PROCEDURE UrunYaz 2( 3 ID IN PLS_INTEGER, 4 AD IN VARCHAR2 5) 6AS 7 RowCount PLS_INTEGER; 8BEGIN 9 SELECT 10 COUNT(*) INTO RowCount 11 FROM Urun 12 WHERE UrunId = ID; 13 IF RowCount = 0 14 THEN 15 AD); 16 END IF; 17 END; INSERT INTO Urun(UrunId, UrunAdi) VALUES(ID, Örnek 28’deki kodu çalıştırdığınızda UrunYaz adında bir procedure SYSTEM schema’sı için login olduğumuz veri tabanı üzerinde daha önceden yazılmamışsa yaratılacaktır, eğer daha önceden varsa üstüne yazılacaktır. Bunu sağlayan 1 numaralı saturdaki CREATE OR REPLACE ifadesidir. Bunun yerine sadece CREATE ya da REPLACE de kullanılabilir. 3 ve 4 numaralı satırlarda UrunYaz procedure’ının argümanlarıdır. 3 numaralı satırda ID adında, PLS_INTEGER tipinde, IN yani dışarıdan gelen bir argüman tanımlanmıştır. 4 numaralı satırda AD adında, VARCHAR2 tipinde, IN yönünde diğer argüman tanımlanmıştır. 7 numaralı satırda procedure’ın içinde kullanacağımız RowCount adında, PLS_INTEGER tipinde bir değişken aynı ID’ye sahip tabloda başka kayıt olup olmadığını kontrol etmek amacıyla, satır sayısını tutmak üzere tanımlanmıştır. 9, 10, 11 ve 219 220 Bölüm 11 12 numaralı satırlardali SELECT cümleciğiyle RowCount değerine UrunYaz procedure’ına ID argümanıyla gelen değere sahip UrunId’li alanların sayısı atanmıştır. 13 numaralı satırda eğer bu ID’ye sahip bir kayıt yoksa 15 numaralı satırdaki INSERT cümleciği çalışması için RowCount değişkeninin 0 olup olmadığı kontrol edilmektedir. Eğer RowCount 0 ise INSERT çalışır, aksi takdirde procedure sonlanır. Bu procedure’ı çalıştırmak için yazmamız gereken kod Örnek – 29’daki gibidir: Örnek 29: PL/SQL ile Procedure Çalıştırmak 1DECLARE 2 IDx PLS_INTEGER := 4; 3 ADx VARCHAR2(10) := ‘Gofret’; 4BEGIN 5 UrunYaz(ID => IDx, AD => ADx); 6END; Örnek 29’da 2 ve 3 numaralı satırlarda UrunYaz procedure’ına argüman olarak göndereceğimiz değerleri tanımladık. 5 numaralı satırda da UrunYaz procedure’ını bu değerlerle çalıştırdık. Örnek 30: PL/SQL ile OUT Parametreli Procedure 1CREATE OR REPLACE PROCEDURE UrunYazOut 2( 3 ID IN PLS_INTEGER, 4 AD IN VARCHAR2, 5 EKLENDI OUT BOOLEAN 6) 7AS 8 RowCount PLS_INTEGER; 9BEGIN 10 SELECT 11 COUNT(*) INTO RowCount 12 FROM Urun 13 WHERE UrunId = ID; 14 IF RowCount = 0 15 THEN 16 AD); INSERT INTO Urun(UrunId, UrunAdi) VALUES(ID, 17 EKLENDI := TRUE; 18 ELSE 19 20 END IF; 21 END; EKLENDI := FALSE; Örnek 30’da 5 numaralı satırda EKLENDI adında, BOOLEAN tipinde, OUT yönlü bir argüman tanımladık. Eğer 16 numaralı satırda INSERT işlemine gelinirse EKLENDI değeri TRUE olur; aksi takdirde FALSE olur. Örnek – 31’de de UrunYazOut procedure’ının nasıl çalıştırıldığını görebilirsiniz. Oracle Üzerinde Programlama ve PL/SQL Örnek 31: PL/SQL ile Out Argümanlı Procedure Çalıştırmak 1DECLARE 2 IDx PLS_INTEGER := 4; 3 ADx VARCHAR2(10) := ‘Gofret’; 4 DURUM BOOLEAN; 5BEGIN 6 UrunYazOut(ID => IDx, AD => ADx, EKLENDI => DURUM); 7 IF DURUM = TRUE 8 THEN 9 DBMS_OUTPUT.PUT_LINE(‘Eklendi’); 10 ELSE 11 12 END IF; 13 END; DBMS_OUTPUT.PUT_LINE(‘Eklenmedi’); Örnek 31’de 4 numaralı satırda UrunYazOut procedure’ından OUT parametre (argüman) yoluyla gelecek değeri tutmak için bir BOOLEAN değişken oluşturduk. 6 numaralı satırda da IN yönlü argümanlar gibi OUT yönlü argümanımızı da oluşturduk. 7 numaralı satırdan itibaren IF bloğundan DURUM değişkeninin değerine göre DBMS çıktısına ‘Eklendi’ ya da ‘Eklenmedi’ yazacaktır. Fonksiyon Oluşturmak PL/SQL’de fonksiyonlar T-SQL’deki fonksiyonlara çok benzer özellikler gösterir. IN, OUT ve IN OUT yönlü argümanlarla çalışırlar ve RETURN ifadesi ile bir değer döndürürler. C# ve Java’daki dönmüş tipi void olmayan metotlar gibidir. Örnek 32: PL/SQL’de Fonksiyon 1CREATE OR REPLACE FUNCTION UrunAdiOku 2( 3 ID IN PLS_INTEGER 4) 5RETURN VARCHAR2 6AS 7 AD VARCHAR2(10); 8BEGIN 9 SELECT 10 UrunAdi INTO AD 11 FROM Urun 12 WHERE UrunId = ID; 13 RETURN AD; 14 END; Örnek 32’deki kodu çalıştırdığınızda UrunAdiOku adında fonksiyonumuz eğer daha önce yaratılmamışsa yaratılacaktır; aksi takdirde üstüne yazılacaktır. Procedure’lardaki gibi fonksiyonlarda da CREATE ve REPLACE ifadeleri tek başlarına kullanılabilir. 221 222 Bölüm 11 Örnek 33: PL/SQL ile Fonksiyon Çalıştırmak 1DECLARE 2 IDx PLS_INTEGER := 4; 3 ADx VARCHAR2(10); 4BEGIN 5 ADx := UrunAdiOku(ID => IDx); 6 DBMS_OUTPUT.PUT_LINE(ADx); 7END; Örnek 33’de 3 numaralı satırda VARCHAR2(10) tipinde tanımalanan ADx isimli değişken UrunAdiOku fonksiyonundan dönen değeri tutacak değişkendir. 5 numaralı satırda ADx değişkenine UrunAdiOku fonksiyonundan dönen değerin ataması yapılmıştır. Bu atama sırasında tip uyumu çok önemlidir. Eğer uyumsuz tipler kullanırsanız istenmeyen sonuçlarla karşılaşabilirsiniz. 6 numaralı satırda da ADx değişkenine atanan fonksiyon dönüş değeri DBMS çıktısına yazdırılır.