Welcome to My Blog 👋

Java, Spring Framework, Microservices, Docker, Kubernetes, AWS and Others 🚀
Follow Me

Polymorphism çok şekillilik demektir. Bu konu kalıtım konusu ile iç içe geçmiş bir konudur. Kalıtım yolu ile oluşturduğumuz sınıfların birbiri yerine kullanılabilmesini sağlar. Örneğin Canli adında bir sınıfımızın olduğunu düşünelim. Daha sonra Bitki, Hayvan ve Insan adındaki sınıflarımızı Canli sınıfından extends ettiğimizi kabul edelim. Polymorphism sayesinde Canli sınıfından bir nesne referansı ile bitki, hayvan ve insan sınıfından oluşturduğumuz nesneleri gösterebiliriz. Bu ne işimize yarayacak derseniz, örneğin bizim bitki, hayvan ve insan için aynı işi yapmasını istediğimiz bir metot olsun. Normal şartlarda bu iş için 3 ayrı metot yazıp kod tekrarı yapmamız gerekecekti ancak polymorphism sayesinde tek bir metot yazıp bu 3 ayrı nesne için sadece canli sınıfından bir nesne alabiliriz. Daha sonra bu metoda insan, hayvan veya bitki sınıfından bir nesne gönderildiğinde hangi nesne gönderilmiş olursa olsun o nesne gibi davranış sergileyecektir.
Üst sınıf nesnesinin bir alt sınıf nesnesini göstermesine upcasting nedir. Bunun tam tersi de mümkündür. Yani insan sınıfından bir nesne canli sınıfından bir nesneyi gösterebilir. Bu işleme de downcasting denir. Ancak bu işlemde dikkat edilmesi gereken yer bu işlemi gerçekleştirirken alt sınıfın tipinin belirtilmesi gerektiğidir.

class Canli {

    public void sesCikar() {

        System.out.println("ses çıkarma");
    }

}

class Insan extends Canli {
    
    @Override
    public void sesCikar() {

        System.out.println("konuşma");
    }
}

class Hayvan extends Canli {

    @Override
    public void sesCikar() {

        System.out.println("bağırma");
    }
}

public class Example {

    public static void main(String[] args) {

        Insan insan1 = new Insan();
        Hayvan hayvan1 = new Hayvan();
        Canli canli2 = new Canli();

        // Upcasting
        Canli canli1 = insan1;
        canli1.sesCikar();
        // Downcasting
        Insan insan2 = (Insan) canli1;
        insan2.sesCikar();

        // Polymorphism
        polymorphism(canli1);
        polymorphism(insan2);
        polymorphism(hayvan1);
        polymorphism(canli2);

    }

    public static void polymorphism(Canli c) {
        
        c.sesCikar();
    }
}

Java diline generic tipler kod tekrarını azaltmak için eklenmiştir. Generic tipler ile yazdığımız class'ların, metotların veya interface'lerin veri tiplerini çalışma anında belirtebiliriz. Örneğin bir metot yazdınız ve bu metot bir değişken alıyor. Bu değişkenin her veri tipinde olabilmesini isterseniz generic tipleri kullanabilirsiniz. Bu işlem generic tiplerden önce object sınıfı ile yapılabiliyordu ancak tip denetimi ve tip dönüşümü gibi ekstra işlemler gerektirdiği için generic tipler ile yapılması çok daha mantıklıdır.
Generic tiplerin, kod sadeliği sağlaması, kodu hızlı geliştirmemize olanak vermesi ve tip güvenliği gibi bir çok avantajı vardır.

Generic tipler class'lar ile kullanıldığında aşağıdaki şekilde kullanılabilir. Genel tanımlaması class ClassAdi<T,K,L,...> şeklinde olabilir. T,K,L gibi istediğimiz kadar farklı tip belirtebiliriz hepsi nesne oluştururken belirtiğimiz tipi temsil eder.

class GenericTipler<T> {
    
    T obj;
    
    public GenericTipler(T obj) {
        
        this.obj = obj;
    }

    public T getObj() {
        return obj;
    }

}

public class Example {

    public static void main(String[] args) {

        GenericTipler exampleString = new GenericTipler("Berkay");
        GenericTipler exampleInteger = new GenericTipler(5);
        
        System.out.println(exampleString.getObj().getClass().getName());
        System.out.println(exampleInteger.getObj().getClass().getName());

    }
}

Generic tipler metotlar ile kullanılabilir. Genel tanımlama ön_ekler <T,K> dönüş_tipi metot_adi(T obj, K obj2, K obj3, ...) gibi olur.


public class Example {

    public static void main(String[] args) {

        goster("berkay");
    }

    public static <T> void goster(T obj) {
        
        System.out.println(obj.getClass().getName());
    }
}

Generic tipler interface ve kalıtım ile de kullanılabilir. Bu kullanım class'lardaki kullanım ile aynıdır.

Java'da kullanılan bu iki yapı birbirine çok sık karıştırılmaktadır. Ancak bu iki yapı da çok basittir.

Override

Override bir metodun tekrardan yazılması anlamına gelir. Bildiğiniz gibi Java'da kalıtım vardır. Aynı şekilde interface yapısıda Java dilinde kullanılır. Yazdığımız bir sınıfa kalıtım yolu ile veya interface yolu ile birden çok metot dahil edebiliriz ve bu metotları kullanabiliriz. Ancak kalıtım ile aldığımız bir metodu değiştirmek istersek o zaman override etmemiz yani yeniden yazmamız gerekir. Interface yapısı ile aldığımız metotları da zorunlu olarak tekrardan yazmamız zaten gereklidir. İşte bu işleme override denir.

public class Ornek extends Ornek2 {

    // Overload

    public void selamVer() {
        System.out.println("Selam");
    }

    public void selamVer(String isim) {
        System.out.println("Selam " + isim);
    }

    public void selamVer(String isim, String soyisim) {
        System.out.println("Selam " + isim + " " + soyisim);
    }

    // Override

    @Override
    public void hareketEt() {

        System.out.println("hareketEt metodu override edildi");
    }

}

class Ornek2 {

    public void hareketEt() {

        System.out.println("hareket edildi");
    }

}

Overload

Overload bir metodun aşırı yüklenmesi anlamına gelir. Java'da overload ile aynı isimde birden fazla metot yazabiliriz. Bu metotları birbirinden ayıran fark aldıkları parametreler olur. Örneğin programımızda selamVer adında bir metot olsun. Bu metodu selamVer() şeklinde yazarak ekrana sadece selam yazdırabiliriz. Aynı şekilde başka bir metot olarak selamVer(String isim) şeklinde tanımlayabiliriz. Programımızda selamVer metodunu bir string değer ile çağırırsak ikinci metot çağırılır eğer parametresiz çağırırsak birinci metot çağırılır. Java burada metodun aldığı parametrelere bakarak hangi metodun çağırılacağına karar verir. Bu işleme overload denir. Burada dikkat edilmesi gereken konular ise parametrelerin sayısı, tipi ve sırası farklı ise kodumuz sorunsuz çalışacaktır. Ancak aynı parametreler ile aynı isimde iki metot yazarsa burada hata alırız. Bir diğer konu ise Java dönüş değerine göre metotları ayırt edemez. Yani void selamVer() metodu ve String selamVer() metodunu programımıza yazarsa Java bu ikisi arasındaki farkı anlayamaz ve hata verir. Çünkü selamVer() metodunu çağırdığımız yerde hangi dönüş değerini beklediğimizi belirtmek zorunda değilizdir.




Abstract sınıflar genellikle Java'da oluşturduğumuz sınıfların ortak özelliklerini bir arada toplamak için kullanılır. Bu yüzden abstract sınıflardan bir nesne yaratılamaz. Abstract sınıflarımızın içerisinde içi dolu veya boş metotlar ve değişkenler tanımlayabiliriz. İçi boş metottan kastettiğimiz şey sadece metodun var olduğunu ve extend edilen sınıfta kullanılması gerektiğini ama ne yapacağını belirtmediğimiz metotlardır. Abstract sınıflar bu yönleri ile interface'lere çok benzemektedir. Interface ile de aynı şekilde bir sınıfın alması gereken metotları belirtebiliyorduk. Interface ile belirttiğimiz metotlarında sadece ismini, dönüş değerini ve aldığı parametreleri belirtiyorduk. Abstract sınıflarda da gövdesiz metotlar ile aynı işi yapabiliyoruz. Abstract sınıflarda oluşturduğumuz gövdesiz metotların başına abstract anahtar kelimesini koymamız gerekiyor. Aynı şekilde bir sınıfın abstract olduğunu belirtmek için de sınıfın başına abstract anahtar kelimesini koyuyoruz. Bir diğer dikkat edilmesi gereken konu da eğer bir sınıfta abstract bir metot varsa o sınıf abstract bir sınıf olmak zorundadır.

package deneme;

public abstract class Canli {

    int yas;

    public abstract void yaslan();

    public void yemekYe() {
        System.out.println("yemek yendi");
    }

}

class Insan extends Canli {

    public void yaslan() {

        yas++;
    }

}

Abstract sınıfların interface'ler ile benzerliklerini anlattık. Peki hangi durumda hangi yapıyı kullanacağımıza nasıl karar vereceğiz diye merak ediyorsanız bunun cevabı çok basittir. Interface yapısında has a ilişkisi vardır. Yani türkçe olarak sahiptir anlamına gelir. Örnek olarak insan sınıfı kıyafet adında bir interface'i implements ederek bu sınıfın metotlarını kullanabilir. Örneğin bu metotlar kıyafetiGiy veya kiyafetiTemizle olabilir. Abstract yapıda ise is a ilişkisi vardır. Örneğin insan bir canlıdır ilişkisi is a ilişkisidir. Eğer bir sınıf diğer sınıfın alt bir türü ise örneğin araç ve araba veya bisiklet sınıfları gibi o zaman abstract sınıflar kullanılır. Burada dikkat edilmesi gereken bir diğer konu da Java'da çoklu kalıtım yoktur ancak birden fazla interface bir sınıfa implements edilebilir.

Java nesne yönelimli programlama dilidir. Java'da oluşturulan nesneler belleğin heap bölgesinde oluşturulur. Java'da bir sınıftan üretilen her nesne için heap bölgesinde ayrı bir alana veri yazılır. Yani Java'da aynı class'tan üretilen tüm objeler birbirinden farklı veriler ve metotlar içerir. Ancak tüm nesnelerde aynı üye kullanılmasını istersek o üyeyi static tanımlayabiliriz. Örneğin bir sınıfın kaç adet nesnesinin oluşturulduğunu tutmak istersek bu durumda static değişken kullanmalıyız.
Eğer bir sınıf içerisindeki bir üyeyi static tanımlamak istersek o sınıfıntan üretilen her nesne için o üye sadece bir tane olur. Örneğin Urün sınıfında stok değişkenini static tanımlarsak o stok değişkeni sadece 1 tane olur ve tüm nesneler o değişkeni kullanır. Peki hangi durumlarda sınıf üyelerimizi static tanımlamalıyız diye soracak olursanız eğer bir değişkenin değeri her üretilen nesne için farklı olmayacaksa tüm nesneler için aynı olacaksa o değişken static olmalıdır. Aynı şekilde bir metot tüm nesneler için aynı çıktıyı verecekse o değişken de static tanımlanmalıdır. Ayrıca static değişken ve metotları kullanmak için sınıftan bir nesne yaratmamıza gerek yoktur sadece sınıf adı ile erişim gerçekleştirebiliriz. Ancak dikkat edilmesi gereken bir nokta da eğer bir metot static tanımlanmış ise o metot içerisinde bir nesne yaratmadığımız herhangi bir class'ın static olmayan üyesine erişemeyiz. Ayrıca kendi class'ı içerisindeki static olmayan metot ve değişkenlere de erişemeyiz. Çünkü sınıf ve nesne kavramı ayrı şeylerdir. Static olmayan sınıf üyeleri aslında var olmayan şeylerdir ve var olmayan bir şeye var olan yani static olan bir üyeden erişimin yapılamaz.

public static int stok;
public static void stoktaVarMi(){}




Java'da bulunan final anahtar kelimesi bir çok yerde kullanılmaktadır ve kullanılan yere göre anlamı değişmektedir.

Eğer final anahtar kelimesi bir class ile kullanılırsa o class'tan bir alt class üretilemeyeceği anlamına gelir.
Eğer final anahtar kelimesi bir metot ile kullanılırsa o metodun override edilemeyeceği yani alt class'larda değiştirilemeyeceği anlamına gelir.
Eğer final anahtar kelimesi bir değişken ile kullanılırsa o değişkene oluşturulurken değer atanabileceği ve daha sonra bu değerin değiştirilemeyeceği anlamına gelir.
Eğer final anahtar kelimesi bir metot parametresi ile kullanılırsa o metot parametresinin metot içerisinde değiştirilemeyeceği anlamına gelir.

Örnek Kullanım;
  • public final class Canli{}
  • final void hareketEt(){}
  • final int yas = 5
  • void hareketEt(final int hiz){}



Java dilinde oluşturduğumuz sınıflara, metotlara veya değişkenlere nereden erişilebileceğini sınırlayabiliriz. Bu işlemi yapmak için özel erişim belirleyicileri kullanırız. Bu belirteçler sınıflarımızı kapsüller yani gizler. Bir sınıfın içerisinden o sınıfın tüm üyelerine erişebiliriz ancak bazı durumlarda bu üyelere dışarıdan erişilmesini, bu üyelerin değiştirilmesini veya kullanılmasını kısıtlamamız gerekebilir. Bu durumlarda erişim belirteçlerini kullanmalıyız.

  • private (Sadece o sınıfın elemanları erişebilir)
  • public (Her yerden erişilebilir)
  • protected (Paket içerisinden veya alt sınıflardan erişilebilir)
  • boş (Paket içerisinden erişilebilir)

Örnek Kullanım;

  • public class Canli 
  • private int yas 
  • protected void hareketEt(){} 
  • class Hayvan 

Object sınıfı Java'da bulunan özel bir sınıftır. Bu sınıfı özel yapan ise Java'da bulunan tüm class'ların bu sınıftan türetilmiş olmasıdır. Java'da kendimiz bir class yazıp object oluşturduğumuzda veya var olan bir sınıftan da object oluşturduğumuzda bazı ortak metotların olduğunu belki fark etmişsinizdir. Örneğin equals() veya toString() gibi metotlar Java'da bulunan tüm class'larda mevcuttur. İşte bunun gibi ortak metotlar Object sınıfından miras alınmaktadır. Object sınıfının bir diğer özelliği ise mesela bir metot yazdığımızı düşünelim. Bu metodun da her veri tipinde bir değişken alabileceğini düşünelim. Normal şartlarda her veri türü için ayrı bir metot yazmamız gerekirdi ancak Object tipinden bir değişken alıp daha sonra bu değişkenin hangi class'tan üretildiğini öğrenebiliriz ve o class'a dönüştürebiliriz.


Java'da anonymous class yapısı bir inner classtır. Yani başka bir class içinde tanımlanmış bir class'tır. Anonymous class'ların bir ismi yoktur bu yönden inner class'lardan ayrılır. İsimlerinin olmaması da bu classlardan bir object yaratılma ihtiyacı olmamasından dolayıdır. Peki bu class'lardan object yaratmayacaksak bu class'lar ne işe yaramaktadır derseniz biz bir sınıftan object yaratırken bu object'in bir veya birden fazla metodunu override etmek isteyebiliriz. Sadece yaratacağımız bu object'te bu metotların override edilmesini isteyebiliriz. Bu durumlarda anonymoun class'ları kullanılırız. Bu yapı genellikle gui sınıflarında kullanılır. Örnek;

Bir araba sınıfımızın olduğunu ve bu araba sınıfının hareketEt adında da bir metodunun olduğunu düşünelim. Bu araba sınıfından bir object oluştururken bu hareketEt metodunu değiştirme ihtiyacımız varsa ve bu değişiklik sadece bu object için olacaksa o zaman anonymous class'ları kullanabiliriz.

public class Araba {
     hareketEt(){
         System.out.println("hareket ediyor");
     }
}

public class Main {
     public static void main() {
        Araba opel = new Araba() {
              @Override
              hareketEt(){
                   System.out.println("yeni hareket");
              }
        }
     }
}


Classpath Java kodu derlenirken kaynak kodun bağlantılı olduğu sınıfların arandığı yoldur. Yani adından da anlaşılacağı gibi Java'da yazılan sınıfların yoludur. Eğer classpath yanlış verilirse derleme esnasında bağlantılı sınıflar bulunamayacağı için derleme başarısız olur.


Oracle Java dilini satın aldıktan sonra geliştiricilerin bilgilerini belgelemek için sertifikalar vermeye başlamıştır. Çeşitli belgeler ile Java dilindeki uzmanlığınızın alanını ve seviyesini belirtebilirsiniz. Ülkemizde daha çok yaygın olmamakla birlikte yurt dışı için bu sertifikalar önemlidir. Sertifikalar hakkında daha detaylı bilgi almak için benimde takip ettiğim bu bloglardaki ilgili yazıları okuyabilirsiniz.



Java'da bir nesnenin hangi sınıfa ait olduğunu çalışma anında tespit etmek için instanceof operatörünü kullanabiliriz. Bu operatör iki değer alır. Sol tarafına bir nesne ve sağ tarafına ise bir sınıf değeri alır. Eğer belirtilen nesne belirtilen sınıfa ait ise true değil ise false değerini döndürür. Örnek;

Canli canli = new Canli();
System.out.println(canli instanceof Canli);
System.out.println(canli instanceof Araba);

Ekran Çıktısı:

true
false


Java'da oluşturduğumuz nesneleri kopyalayarak yeni nesneler oluşturabiliriz. Kopyalayarak oluşturduğumuz yeni nesne tamamen yeni bir nesne de olabilir, kopyalanan nesne ile aynı nesne de olabilir. Bu yüzden Java'da iki tür nesne kopyalama yöntemi vardır.

  • Sığ Kopyalama (Shallow Copy)
  • Derin Kopyalama (Deep Copy)

Sığ kopyalamada oluşturulan iki nesnede aynı bellek bölgesini gösterir. Örneğin;

Canli canli1 = new Canli();
Canli canli2 = canli1;

Yukarıdaki ilk satırda canli1 isminde bir nesne oluşturuluyor ve bu nesne için new anahtar sözcüğü ile bellekte bir yer ayrılıyor. canli1 adındaki bu nesne bellekte onun için ayrılar yeri gösteriyor. Daha sonra canli2 adında oluşturduğumuz nesneye de canli1 adındaki nesnenin gösterdiği bellek adresini atıyoruz. Bu şekilde iki nesnede aynı bellek bölgesini gösteriyor. Bu tür nesne kopyalamasına sığ kopyalama(shallow copy) denir.

Derin kopyalamada ise yeni nesne için bellekte yeni bir alan oluşturulur ve kopyalanacak nesnenin tüm özelliklere teker teker yeni nesneye aktarılır.

Canli canli1 = new Canli();
canli1.isim = "kedi";
Canli canli2 = new Canli();
canli2.isim = canli1.isim;

Yukarıda görüldüğü gibi iki nesne içinde new anahtar sözcüğü ile bellekte iki ayrı yer ayrılıyor ve sadece nesnenin özellikleri kopyalanıyor. Bunun gibi bir kopyalama işlemine derin kopyalama(deep copy) denir.

Java dilinde veri tipleri üçe ayrılır;
  • İlkel (Primitive) Veri Tipleri
  • Referans Veri Tipleri
  • Null Veri Tipi
İlkel veri tipleri kendi içinde alt gruplara ayrılır. Bunlar;
  • Sayısal Veri Tipleri
  • Mantıksal Veri Tipleri
Referans veri tipleri kendi içinde alt gruplara ayrılır. Bunlar;
  • Class
  • Interface
  • Array
Nesne yönelimli programlamada tüm veri tipleri bir sınıftır. Ancak veri tipleri çok sık kullanıldığından dolayı her seferinde bir nesne oluşturulmak istenmemesinden dolayı Java ilkel veri tiplerini bir nesne oluşturmadan kullanmamıza olanak sağlar. Referans tipler ise direk bir nesnesini oluşturduğumuz tiplerdir. Bu nesnelerin içerisine değer atarız ve o sınıfın metotlarını kullanabiliriz.

İlkel veri tipleri sabit uzunluklu veri tipleridir. Yani içerisine girilebilecek veriler sınırlıdır. Örneğin byte veri tipi -128 ile +127 arasında sayılar alır. Bu sayılardan farklı bir sayı içerisine girilemez. Bu veri tiplerinin bellekte kapladıkları alan sabittir. Örneğin byte veri tipi içinde 0 da tutsa 127 de tutsa bellekte kapladığı yer hep 1 byte olacaktır.

Referans tipler ise değişken uzunluklu veri tipleridir. Örneğin String veri tipinin içerisine istediğimiz kadar karakter girebiliriz ve bu karakterlere göre bellekte kapladığı alan farklı olacaktır.

İlkel veri tiplerinin içerisinde değerler tutulur. Yani örnek olarak int veri tipi içerisine 8 sayısını girdiğimizde bu veri tipinin içerisinde 8 değeri olur. Daha sonra bu değişkeni bir metoda gönderdiğimizde değişkenin kendisi değil içerisindeki değer gönderilir. Referans tiplerde ise değerler tutulmaz. Değişkenlerin içerisinde değerin bellekte bulunduğu adres tutulur. Böylelikle bu veri tiplerinin metotlara parametre olarak geçtiğimizde değer değil değerin adresi metoda gönderilir. Bu şekilde değişken üzerinde işlem yapılır.

Bir istisna olarak String veri tipi bir referans tip olmasına rağmen bir nesne yaratılmadan kullanılabilir. String sınıfı çok sık kullanılan bir sınıf olduğu için Java'da böyle bir kolaylık uygulanmıştır.

İlkel veri tipleri üzerinde operatörler ile işlem yapabiliriz. Referans tipler üzerinde işlem yapabilmek için ilgili sınıfın metotlarını kullanmak zorundayız.

İstersek bir sınıf yazarak kendi referans veri tipimizi oluşturabiliriz.

İlkel Veri Tipleri
  • byte
  • short
  • int
  • long
  • float
  • double
  • boolean
  • char
Referans Veri Tipleri
  • Byte
  • Short
  • Integer
  • Long
  • Float
  • Double
  • Boolean
  • Character
  • String
  • ...


JDK, Java uygulamaları geliştirmek için gerekli tüm araçları bize sağlar. İçerisinde JVM, JRE, Java kütüphaneleri, Java Compiler ve Interpreter içerir. Bir java uygulaması geliştirmek için JDK yüklememiz yeterlidir çünkü diğer araçları JDK içirisinde barındırır.




Java kodlarının derlenerek byte kodlara dönüştürülmesi gerekir. Daha sonra bu byte kodlar JVM tarafından sistemin anlayacağı dile çevrilebilir. Java kodlarını byte koda çeviren mekanizmaya JRE denir. JRE içinde java kütüphanelerini ve JVM'yi barındırır.



JVM(Java Virtual Machine), Java dilinin en önemli özelliği olan bir kere yaz her yerde çalıştır mantığını sağlayan mekanizmadır. Java kodları hemen hemen tüm platformlar üzerinde çalışabilir. Java kodları .java uzantılı dosyalara yazılır. Daha sonra bu dosya compile edilerek byte koda dönüştürülür. Bu byte kodlar .class uzantılı dosyalara yazılır. Daha sonra bu byte kodlar JVM üzerinde çalıştırılır ve sistemin anlayacağı şekle dönüştürülür.



Yani JVM sistemin üzerinde çalışarak Java kodlarının her sistemde çalışabilmesini sağlar. Bunu yapabilmesi için her sisteme özel bir JVM olması gerekir. Sistemin Java kodu ile uygun bir şekilde çalışabilmesi için gerekli düzenlemeleri JVM yapar. Bizim yapmamız gereken tek şey Java kodunun çalışacağı makineye uygun JVM'yi yüklemek ve kodumuzu derlemektir.





Java JDBC kütüphanesi ile veritabanından kayıt silme işlemi. Connection nesnesi ile veritabanı bağlantısı oluşturulur. Statement nesnesi ile SQL cümlesi çalıştırılır.


package delete;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class DeleteExample {

 public static void main(String[] args) {

  try {

   Class.forName("com.mysql.jdbc.Driver");
   Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/first_example","root","123456");
   
   String deleteSQL = "DELETE FROM person WHERE id = ?";
   PreparedStatement state = connection.prepareStatement(deleteSQL);
   state.setInt(1, 1);
   int result = state.executeUpdate();

   if (result > 0) {

    System.out.println("Successful");

   } else {

    System.out.println("Failed");
   }

   state.setInt(1, 2);
   result = state.executeUpdate();

   if (result > 0) {

    System.out.println("Successful");

   } else {

    System.out.println("Failed");
   }

   state.close();
   connection.close();

  } catch (Exception e) {

   e.printStackTrace();
  }

 }
 
}


Java JDBC kütüphanesi ile veritabanından kayıt çekme işlemi. Connection nesnesi ile veritabanı bağlantısı oluşturulur. Statement nesnesi ile SQL cümlesi çalıştırılır. ResultSet nesnesi ile dönen kayıtlara erişilir.


package select;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;


public class SelectExample {
 
 public static void main(String[] args) {

  try {
   
   Class.forName("com.mysql.jdbc.Driver");
   Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/first_example","root","123456");
   
   ResultSet result;
   
   // SELECT With Dynamic Parameters
   String selectSQL = "SELECT * FROM person WHERE id = ?";
   PreparedStatement preparedState = connection.prepareStatement(selectSQL);
   preparedState.setInt(1, 1);
   result = preparedState.executeQuery();
   
   if (result.next()) {

    System.out.println(result.getInt("id") + " - "
      + result.getString("name") + " - "
      + result.getString("surname"));
   }
   
   System.out.println("-----------------------------------------------");

   // SELECT With Static Parameters
   Statement state = connection.createStatement();
   result = state.executeQuery("SELECT * FROM person WHERE id = 1");
   
   if (result.next()) {

    System.out.println(result.getInt("id") + " - "
      + result.getString("name") + " - "
      + result.getString("surname"));
   }
   
   System.out.println("-----------------------------------------------");

   // SELECT All Records
   Statement stateAllRecords = connection.createStatement();
   result = stateAllRecords.executeQuery("SELECT * FROM person");

   while (result.next()) {

    System.out.println(result.getInt("id") + " - "
      + result.getString("name") + " - "
      + result.getString("surname"));

   }

   preparedState.close();
   connection.close();

  } catch (Exception e) {

   e.printStackTrace();

  }

 }

}



Java JDBC kütüphanesi ile veritabanındaki kayıtları güncelleme işlemi. Connection nesnesi ile veritabanı bağlantısı oluşturulur. Statement nesnesi ile SQL cümlesi çalıştırılır.

Github Linki

package update;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class UpdateExample {

 public static void main(String[] args) {

  try {

   Class.forName("com.mysql.jdbc.Driver");
   Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/first_example","root","123456");
   
   String updateSQL = "UPDATE person SET name = ? ,surname = ? WHERE id = ?";
   PreparedStatement state = connection.prepareStatement(updateSQL);
   state.setString(1, "Update");
   state.setString(2, "Example");
   state.setInt(3, 5);
   int result = state.executeUpdate();

   if (result > 0) {

    System.out.println("Successful");

   } else {

    System.out.println("Failed");
   }

   state.close();
   connection.close();

  } catch (Exception e) {

   e.printStackTrace();
  }

 }

 
}

Java JDBC kütüphanesi ile veritabanına kayıt işlemi. Connection nesnesi ile veritabanı bağlantısı oluşturulur. Statement nesnesi ile SQL cümlesi çalıştırılır.

Github Linki

package insert;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class InsertExample {

 public static void main(String[] args) {

  try {

   Class.forName("com.mysql.jdbc.Driver");
   Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/first_example","root","123456");
   
   String insertSQL = "INSERT INTO person (name,surname) VALUES (?,?)";
   PreparedStatement state = connection.prepareStatement(insertSQL);
   state.setString(1, "Berkay");
   state.setString(2, "DEMİREL");
   int result = state.executeUpdate();

   if (result > 0) {

    System.out.println("Successful");

   } else {

    System.out.println("Failed");
   }

   state.setString(1, "Insert");
   state.setString(2, "Example");
   result = state.executeUpdate();

   if (result > 0) {

    System.out.println("Successful");

   } else {

    System.out.println("Failed");
   }

   state.close();
   connection.close();

  } catch (Exception e) {

   e.printStackTrace();
  }

 }

}


Statement nesneleri ile SQL ifadelerini veritabanına gönderebilir ve çalıştırabiliriz. Bu nesneler ile parametre geçişi de yapabiliriz. Ayrıca bu nesneler bir veritabanında kullanılan Java ve SQL veri türleri arasındaki veri türü farklılıklarını köprülemeye yardımcı olan yöntemleri de tanımlarlar.

Statement Nesneleri
  • Statement: Statik SQL sorgularını çalıştırmak için kullanılır. Dinamik parametre geçişi yoktur. Bir sorgu çok defa çalıştırılmayacaksa kullanılması mantıklıdır.
  • PreparedStatement: Dinamik SQL sorgularını çalıştırmak için kullanılır. Yani sql sorgularımıza parametre geçişi yapabiliriz. Bir sorgu çok defa çalıştırılcaksa kullanılması mantıklıdır. Çünkü Statement nesnesi ile sorgu her çalıştırıldığında derlenirken PreparedStatement nesnesi sorguyu tek bir kez derler.
  • CallableStatement: Veritabanına kaydedilmiş prosedürleri çalıştırmak için kullanılır. Ayrıca dinamik parametreleri kabul eder.

Statement Kullanımı

Bir SQL deyimini yürütmek için bir Statement nesnesini kullanmadan önce, aşağıdaki örnekte olduğu gibi, Connection nesnesinin createStatement() yöntemini kullanarak bir tane oluşturmanız gerekir.

Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}

Bir Statement nesnesini oluşturduktan sonra, bunu bir SQL ifadesini yürütmek için üç yürütme yönteminden biriyle kullanabilirsiniz.
  • boolean execute (String SQL): Bir ResultSet nesnesi alınabiliyorsa true, aksi takdirde false değerini döndürür. Dinamik SQL kullanmanız gerektiğinde bu yöntemi kullanılabilir.
  • int executeUpdate (String SQL): SQL ifadesinin yürütülmesinden etkilenen satır sayısını döndürür. Bir INSERT, UPDATE veya DELETE ifadesi gibi etkilenen çok sayıda satır almayı beklediğiniz SQL ifadelerini çalıştırmak için bu yöntemi kullanabiliriz.
  • ResultSet executeQuery (String SQL): Bir ResultSet nesnesi döndürür. Bir SELECT deyiminde olduğu gibi bir sonuç kümesi elde etmeyi beklediğimiz durumlarda kullanabiliriz.
PreparedStatement

PreparedStatement pstmt = null;
try {
String SQL = "Update Employees SET age = ? WHERE id = ?";
pstmt = conn.prepareStatement(SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
. . .
}




JDBC (Java Database Connectivity), Java programlama dili ile veritabanları (Mysql, Oracle, MSSQL, vs.) arasında bağlantı kurulmasını ve çeşitli işlemlerin yapılmasını sağlayan bir Java kütüphanesidir.

JDBC kütüphanesi ile aşağıdaki veritabanı işlemleri yapılabilir;
  • Veritabanı bağlansının yapılması.
  • SQL deyimleri oluşturulması
  • Veritabanında SQL sorgularının çalıştırılması.
  • Elde edilen kayıtları görüntülemesi ve değiştirilmesi.

JDBC Mimarisi 2 katmandan oluşur.
  • JDBC API: Uygulama ile JDBC Yöneticisi bağlantısını sağlar.
  • JDBC Driver API: JDBC Yöneticisi ile Sürücü Bağlantısını destekler. Aşağıdaki mimari diyagram, JDBC sürücüleri ve Java uygulaması ile ilgili sürücü yöneticisinin yerini göstermektedir.

Ortak JDBC Bileşenleri

JDBC API, aşağıdaki arayüzleri ve sınıfları sağlar;
  • DriverManager: Bu sınıf, veritabanı sürücülerinin bir listesini yönetir. Java uygulamasından gelen bağlantı istekleri ile uygun veritabanı sürücüsünü eşleştirir ve bağlantı oluşturur.
  • Driver: Bu arayüz veritabanı sunucusuyla olan iletişimi idare eder. Nadiren bu nesne kullanılır. Genellikle bu nesneyi yöneten DriverManager nesnesi üzerinden erişim yapılır.
  • Connection: Bu arayüz, bir veritabanıyla iletişim kurmak için tüm yöntemleri içerir. Connection nesnesi iletişim bağlamını temsil eder, yani veritabanıyla yapılan tüm iletişim yalnızca bağlantı nesnesi aracılığıyla yapılır.
  • Statement: Bu arayüz ile SQL ifadelerini veritabanına gönderilir ve çalıştırılır. Ayrıca bu sınıftan türemiş alt sınıflar ile parametre geçişide yapılabilir.
  • ResultSet: Bu nesneler, SQL sorgusu çalıştırıldıktan sonra veritabanından alınan verileri tutar. Verilerin arasında dolaşmanıza izin vermek için bir işaretçi görevi görür.
  • SQLException: Bu sınıf, bir veritabanı uygulamasında meydana gelen hataları yönetir.

Örnek JDBC Kodları

İlişkisel veritabanlarında yaptığımız bazı işlemlerde(select, update, delete vs.) ilgilendiğimiz verinin disk üzerinde aranması ciddi bir sorundur. Örneğin 1 milyon satırın olduğu bir hesaplar tablosunda bir kişinin hesabındaki parayı güncelleyeceksiniz. Bu işlem bir banka uygulamasında çok sık yapılan bir işlemdir. Bu işlemi yapmak için önce veritabanı sisteminin hesabı güncellenecek kişiyi bulması daha sonra o kişinin hesabındaki parayı güncellemesi gerekir. Bu tablodaki 1 milyon kaydın içerisinde ilgilendiğimiz verinin bulunması ciddi bir problemdir. Çünkü normal yöntem ile tüm kayıtların tek tek okunması ve bizim sorgumuz ile karşılaştırılması gerekir. Bu işlem bir milyon satır olan bir veritabanında çok uzun sürecektir ve bu bizim istemediğimiz bir şeydir. Bu problemi ortadan kaldırmak için index denilen yapı kullanılır. İndex'ler arama yapılacak kolonlar üzerine kurulur. Daha sonra arama işlemi sadece bu kolonlar üzerinde olur. Bu şekilde arama işleminde ilgilendiğimiz veri boyutunda ciddi bir şekilde azalma olur. Ayrıca geliştirilen çeşitli index türleri ile arama işlemlerinde çeşitli yöntemler kullanılarak bu işlem çok daha kısa sürelerde yapılabilir hale gelmiştir. Genellikle index yapıları her satırı tek başına ifade edebilen primary key'ler üzerine kurulur. Bu şekilde yapılması ve daha sonra bu primary key üzerinde arama yapılması en iyi yöntemdir. Bu konuda bilinmesi gereken bir diğer önemli bilgi ise index'lere sadece select işlemlerinde ihtiyaç duyulmadığıdır. Örneğin bir kaydı güncellemek veya silmek istediğinizde de öncelikle o kaydın bulunması gerekmektedir. Bu yüzden index veritabanlarında yapılan bir çok işlemi ciddi şekilde hızlandırmaktadır. Index'ler ikiye ayrılırlar. Eğer veriler fiziksel olarak sıralanıyor ise Clustered, fiziksel değil ise Non-Clustered index’tir.

Clustered Index (Kümelenmiş) 
Verileri fiziksel ve alfabetik olarak sıralar. (Örn : Telefon rehberi) Bu tip indexler B-Tree yapısına sahiptir. Verilerin oluştuğu sayfalara pointer(işaretleyici) kullanarak gitmesine gerek kalmadan verinin direk yerine ulaşabilir. Kümelenmiş bir index kullanıldığında ulaşılan yer verinin kendisidir. Eğer tabloda Primary Key (Birincil anahtar) var ise bu kümelenmiş index yapısına sahiptir. Her tabloda en fazla bir tane bulunabilir. Clustered Index yapılan kolon artan veya azalan bir değere sahip olmalıdır.

Non-Clustered Index 
Index oluşturuğumuz verilerin ayrıca sıralandığı ve bu veriler üzerinde arama yapılan index türüdür. Index üzerinde arama yapıldıktan sonra bulunan verinin bellekteki fiziksel yerine gitmek için pointer'lar kullanılır. Bu index türünden tablomuzda çok sayıda olabilir. Örnek verecek olursak bir kitabın içindekiler kısmını kullanarak çok daha hızlı bir şekilde aradığımız sayfayı bulabiliriz. Burada yapılan kitabın içindeki başlıklar üzerinde bir index oluşturmaktır. Daha sonra pointer(sayfa numarası) ile o başlığın gerçekte bulunduğu sayfaya çok kolay bir şekilde gidebiliriz.

Index Nasıl Tanımlanır?

CREATE INDEX index_adi ON tablo_adi(kolon_adlari)

CREATE UNIQUE INDEX index_adi ON tablo_adi(kolon_adlari)  

Not! Unique index tanımladığımızda index tanımlanan her verinin bir tane olması gerekir.

Index Nasıl Silinir?

DROP INDEX index_adi


NOSQL, yıllardır kullanılan ilişkisel veritabanlarına alternatif olarak ortaya çıkmış bir veritabanı sistemidir. Günümüzde verilerin boyutlarının çok fazla artışından ve big data denilen kavramın ortaya çıkışından sonra artık ilişkisel veritabanları ihtiyaçları tam anlamıyla karşılamamaya başlamıştır. Bu yüzden NOSQL veritabanları ilişkisel veritabanlarına bu anlamda bir alternatif üretmek için ortaya çıkmıştır. NOSQL sistemlerinde SQL dili kullanılmadığı için NOSQL olarak adlandırılmıştır.

NOSQL'in önemini şöyle vurgulayabiliriz. Google yıllardır indekslediği sitelerin bilgilerini ilişkisel veritabanında(RDBMS) değil Big Table üzerinde tutuyor. Bu sayede RDBMS gibi büyük verileri performanslı bir şekilde işleyemeyen pahalı sistemler yerine açık kaynaklı ucuz ve performanslı sistemleri tercih ediyor.

NOSQL, RDBMS gibi işlem tabanlı çalışmaz. Bunun yerine yatay büyüme yaparak, performans kazancı sağlar. Verileri bölerek kopyalarını dağınık sistemin farklı parçalarına ekler böylelikle tutarlılık sağlar ve her bir parça için harcanan yük azaltılmış olur.

RDBMS önceden tasarlanıp sütunları belirlenir ve satır satır eklenir. Ancak NOSQL de bu tip bir tanıma gerek yoktur. Bunun yerine daha esnek bir yapı bulunur. Bu kafanızda soru işareti bırakıyorsa şöyle düşünün. 2 adet kolonu olan bir tablonuzda integer verileri tutuyorsunuz yeni satırınızda bunlar yerine varchar eklemeniz gerekiyor. Hiçbir değişikliğe gerek kalmadan verinizi ekleyebilirsiniz. A kolonu B kolonu varken C ve D'ye gerek yok.

NOSQL birincil indeks değerine ihtiyaç duyar ve bunun üzerinden erişim sağlar. RDBMS de bu zorunlu değildir. Oluşan indeks üzerinden belirli aralığa hızlıca ulaşılabilir.

RDBMS bir sistem kullanırken verinin nasıl depolanacağınız ile ilgilenirken, NOSQL için depoladığınız veriyi nasıl kullanacağınızı düşünüyorsunuz.

NOSQL Türleri
  • Döküman (Document) tabanlı:  Bu tip veri tabanları JSON yapısında kayıt yapar. Bu yapılarda sınırsız alan oluşturabilirsiniz. Hatta sınırsız alanların içine sınırsız alanlar ve onların da içine şeklinde devam edebilirsiniz. MongoDB, CouchDB, Amazon Simple DB, Cassandra, HBase...
  • Anahtar / Değer (Key / Value) tabanlı:  Bu sistemlerde anahtarlara karşılık gelen tek bir bilgi bulunur. Kolon yapısı yoktur. MemcacheDB, Berkeley DB, Azure Table Storage...
  • Grafik (Graph) tabanlı:  Bu sistemler diğerlerinden farklı olarak verilerin ilişkisini saklayan Graph Theory modelindeki sistemlerdir. Neo4J, FlockDB...



OLTP ve OLAP, veri depolama sistemleridir. Bu iki sistemde günümüzde kullanılır ve iki sisteminde birbirine göre avantajları ve dezavantajları vardır. Bu iki sistemin kullanıcı kitlesi ve amacı birbirinden farklıdır. OLTP'nin genel amacı veriyi daha verimli tutmaktır. OLAP ise veri üzerinden karar vermeye yöneliktir. OLAP iş dünyasında oluşan ihtiyaçlar üzerine ortaya çıkmış bir veri depolama sistemidir. Bu iki sistemin özellikleri;

OLTP
Temel Amacı : Veriyi daha verimli tutmak
Kullanıcılar : IT Çalışanları, SQL dilini bilen kişiler
Fonksiyonlar : Günlük işler, Belli kategorilere göre veriler
DB Tasarımı : İlişkisel tasarım
Veri : Anlık veriler
Kullanım : Tekrarlı işlemler kullanılır.
İşlemler : Okuma/Yazma/Güncelleme vs.
İşlemlerin Boyutu : Basit sorgular
İşlem Çıktıları : Onlarca, yüzlerce satır
Veritabanı Boyutu : 100MB - 100GB

OLAP
Temel Amacı : Veri üzerinden çıkarım yapmak
Kullanıcılar : Bilgi ile uğraşan tüm kişiler.
Fonksiyonlar : Karar vermeye yönelik veriler
DB Tasarımı : Konuya yönelik tasarım
Veri : Tüm veriler üzerinden veri çıkarımı
Kullanım : Genellikle özel işlemler kullanılır. (Konuya özel veri çıkarımı)
İşlemler : Okuma ağırlıklı
İşlem Boyutu : Karmaşık sorgular
İşlem Çıktıları : Milyonlarca satır
Veritabanı Boyutu : 100GB - 100TB