6. [Cours]: API'e Giriş JDBC
Anahtar kelimeler: ilişkisel veritabanları, API JDBC, SQLException.
6.1. Support
![]() | ![]() | ![]() |
[support / chap-06] klasörü, bu bölümdeki Eclipse projelerini içerir.
6.2. Architecture
![]() |
JDBC katmanı (Java DataBase Connectivity), veritabanlarına evrensel erişim sağlayan bir arayüzdür. Bu katman, [DAO] katmanına her zaman aynı arayüzü sunar. SGBD değiştirilirse, sadece JDBC sürücüsünü değiştirmek yeterlidir. [DAO] katmanı değişmez.
6.3. Bir veritabanını işletmenin adımları
![]() |
Yukarıdaki mimaride, konsol programı tarafından bir veritabanının çalıştırılması aşağıdaki adımları içerir:
- veritabanının JDBC sürücüsünün yüklenmesi;
- veritabanıyla bağlantı kurulması;
- veritabanına SQL komutunun gönderilmesi ve SQL komutunun sonuçlarının işlenmesi;
- bağlantının kapatılması;
- adım yalnızca bir kez gerçekleştirilir. 2-4. adımlar tekrar tekrar gerçekleştirilir. Bağlantının açık bırakılmadığına dikkat edilmelidir. Artık ihtiyaç kalmadığında bağlantı kapatılır.
6.3.1. 1. adım - JDBC sürücüsünün belleğe yüklenmesi
Kod
// sürücünün yüklenmesi JDBC
try {
Class.forName(nom de la classe du pilote JDBC);
} catch (ClassNotFoundException e1) {
// istisna işleme
}
- satırdaki işlemin amacı, veritabanından JDBC sürücüsünü belleğe yüklemektir. Bu işlemin yalnızca bir kez yapılması yeterlidir. Ancak tekrarlanması hata oluşturmaz. JDBC sürücüsünün sınıfı, projenin Classpath'inde aranır. Bu nedenle, Eclipse projesinde, JDBC sürücü sınıfını içeren [jar] dosyasının projenin Classpath'ine eklenmiş olması gerekir.
6.3.2. 2. adım - bağlantı açma
JDBC sürücüsü yerleştirildikten sonra, BD ile bir bağlantı açması istenir:
Kod
package spring.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class IntroJdbc01 {
...
Connection connexion = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// bağlantı açma
connexion = DriverManager.getConnection(url, user, passwd);
...
} catch (SQLException e1) {
// istisna işleniyor
...
} finally {
// bağlantıyı kapat
if (connexion != null) {
try {
connexion.close();
} catch (SQLException e2) {
// istisnayı işleme
...
}
}
}
- 3-7. satırlar: JDBC arayüzünün uygulama sınıflarının tümü [java.sql] paketindedir. Ayrıca, hata durumunda hepsi [SQLException] türünde bir istisna atar (satır 19, 27). Bu istisna, [Exception] sınıfından türetilmiştir ve “kontrollü” bir istisnadır: bunu yönetmek için bir try/catch bloğu eklemek zorunludur; alternatif olarak, istisnayı yönetmemek ve yöntemin [throws SQLException] ile yöntem imzasını tamamlayarak istisnayı dışarı çıkarmasını belirtmek de mümkündür;
- 17. satır, [DriverManager.getConnection], üç parametre bekleyen statik bir yöntemdir:
- [url]: Veritabanındaki URL. Bu, kullanılan BD'e bağlı bir karakter dizisidir. MySQL için bu, [jdbc:mysql://localhost:3306/nom_de_la_bd] biçimindedir;
- [user]: bağlantının sahibi;
- [passwd]: şifresi;
- 24-30. satırlar: istisna olsun ya da olmasın bağlantının kapatılması için [finally] cümlesinde bağlantının kapatılması gerekir.
6.3.3. 3. adım - SQL ve [SELECT] komutlarının verilmesi
Bağlantı kurulduktan sonra, SQL komutları verilebilir. [SELECT] okuma komutlarının yönetilme şekli, [UPDATE, INSERT, DELETE] güncelleme işlemleri için kullanılan yöntemden farklıdır. Öncelikle SQL ve [SELECT] komutlarıyla başlayalım:
Kod
Connection connexion = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// bağlantı açma
connexion = DriverManager.getConnection(url, user, passwd);
// işlem başlangıcı
connexion.setAutoCommit(false);
// salt okunur modda
connexion.setReadOnly(true);
// [PRODUITS] tablosu okunuyor
ps = connexion.prepareStatement("SELECT ID, NOM, CATEGORIE, PRIX, DESCRIPTION FROM PRODUITS");
rs = ps.executeQuery();
System.out.println("Liste des produits : ");
while (rs.next()) {
System.out.println(new Produit(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getDouble(4), rs.getString(5)));
}
// işlemi kaydet
connexion.commit();
} catch (SQLException e1) {
// istisna işleniyor
doCatchException(connexion,e1);
} finally {
// finally bloğu işleniyor
doFinally(rs, ps, connexion);
}
private void doFinally(ResultSet rs, PreparedStatement ps, Connection connexion) {
....
}
- 8. ve 10. satırlar: salt okunur modda (10. satır) bir işlem açılır (8. satır). Bir işlem, ya hepsi başarılı olur ya da hepsi başarısız olur şekilde bir dizi SQL emrinden oluşur. Dolayısıyla, N adet SQL emri içeren bir işlemde, I+1 numaralı emir başarısız olursa, önceki I emir iptal edilir. Okuma işlemi için işlem açılması gerekli değildir. Bununla birlikte, salt okunur bir işlem açmak, bazı SGBD emirlerinin belirli optimizasyonlar yapmasına olanak sağlayabilir;
- 12. satır: bir [PreparedStatement] kullanımı. Bir [PreparedStatement]'in parametreleri normalde ? karakteriyle gösterilir. Burada ise yoktur. Bir [PreparedStatement], SGBD tarafından hazırlanan bir emirdir. Bu hazırlık bir maliyete sahiptir ve yalnızca bir kez yapılır. Ardından bu hazırlanan emir, SGBD tarafından, "?" ile gösterilen formal parametrelerin yerini alacak farklı etkin parametrelerle yürütülür. Tüm sütunları almak için * notasyonunu kullanmak yerine, istenen sütunları isimlendirmek daha tercih edilebilir. Sütunların adlarını belirterek, daha sonra SELECT sorgusundaki konumlarından değerlerini elde edebiliriz;
- 13. satır: [PreparedStatement] sorgusunun yürütülmesi. [ResultSet] türünde bir nesne elde edilir;
[ResultSet] türündeki bir nesne, bir tabloyu, yani satır ve sütunlardan oluşan bir kümeyi temsil eder. Herhangi bir anda, tabloda yalnızca “geçerli satır” olarak adlandırılan bir satıra erişilebilir. [ResultSet]’in ilk oluşturulduğu sırada geçerli satır yoktur. Bunu elde etmek için [ResultSet.next()] işlemini gerçekleştirmek gerekir. next yönteminin imzası şöyledir:
Bu yöntem, [ResultSet]'in bir sonraki satırına geçmeye çalışır ve başarılı olursa true'i, aksi takdirde false'i döndürür. Başarılı olması durumunda, bir sonraki satır yeni geçerli satır olur. Önceki satır kaybolur ve geri dönüp onu kurtarmak mümkün olmaz.
[ResultSet] tablosunda, yürütülen [SELECT] sorgusunda belirtilen labelCol1, labelCol2,... adlı sütunlar bulunur. Aşağıdaki sorgu ile:
SELECT ID as myId, NOM as myNom, CATEGORIE as myCategorie, PRIX as myPrix, DESCRIPTION as myDescription FROM PRODUITS
- [ID] sütunu, [ResultSet] tablosunda [myId] adlı sütuna aktarılacaktır;
- [NOM] sütunu, [ResultSet] tablosundaki [myNom] adlı sütuna aktarılacaktır;
- ...
Yukarıda, [myCol] tanımlayıcıları sütun etiketleri olarak adlandırılır. Bu etiketler yoksa, [ResultSet]'in sütun adları SGBD'e bağlıdır. [SELECT] tek bir tablo üzerinde işlem yaptığında, sütun etiketleri varsayılan olarak SELECT tarafından istenen sütun adları olacaktır. Sorun, [SELECT]'in birden fazla tablo üzerinde çalışması ve bu tablolarda aşağıdaki örnekte olduğu gibi aynı sütun adlarının bulunması durumunda ortaya çıkar:
SELECT PRODUITS.NOM, CATEGORIES.NOM FROM PRODUITS, CATEGORIES WHERE PRODUITS.CATEGORIE_ID=CATEGORIES.ID
[PRODUITS] tablosunun, [CATEGORIES] tablosuna, [Produits] ilişkisi ile temsil edilen bir yabancı anahtara sahip olduğunu varsayalım.CATEGORIE_ID --> [CATEGORIES].ID ve [PRODUITS] ile [CATEGORIES] tablolarının her ikisinde de [NOM] adlı bir alan bulunmalıdır. Bu durumda, [ResultSet]'te [PRODUITS.NOM] ve [CATEGORIES.NOM] sütunlarına verilen adlar, SGBD'e bağlıdır. SGBD ile veri aktarımı için burada sütun etiketleri kullanılmalıdır ve şu şekilde yazılmalıdır:
SELECT PRODUITS.NOM as p_NOM, CATEGORIES.NOM as c_NOM FROM PRODUITS, CATEGORIES WHERE PRODUITS.CATEGORIE_ID=CATEGORIES.ID
[ResultSet] dosyasının geçerli satırındaki çeşitli alanları işlemek için aşağıdaki yöntemler kullanılabilir:
bu, geçerli satırdaki “labelColi” adlı sütunu ve dolayısıyla bu etikete sahip [SELECT] sütununu elde etmek içindir. Type, coli alanının türünü belirtir. Aşağıdaki [getType] yöntemleri kullanılabilir: getInt, getLong, getString, getDouble, getFloat, getDate, ... Sütun adını kullanmak yerine, yürütülen [SELECT] sorgusundaki konumunu kullanabilirsiniz:
burada i, istenen sütunun indeksidir (i>=1).
- 15-17. satırlar: BD sorgusunda okunan değerlerin alınması;
- 19. satır: İşlem onaylanır (diğer bir deyişle, taahhüt edilir). Bu, işlemi sonlandırır ve SGBD'in işlem için ayırdığı kaynakları serbest bırakır;
- satır 25: [finally]'te kaynaklar serbest bırakılır. Bu işlem, aşağıdaki [doFinally] yöntemini çağırır:
private void doFinally(ResultSet rs, PreparedStatement ps, Connection connexion) {
// ResultSet kapatılıyor
if (rs != null) {
try {
rs.close();
} catch (SQLException e1) {
}
}
// kapatma [PreparedStatement]
if (ps != null) {
try {
ps.close();
} catch (SQLException e2) {
}
}
if (connexion != null) {
try {
// bağlantıyı kapat
connexion.close();
} catch (SQLException e3) {
// istisnayı işleme
}
}
}
- 3-9. satırlar: [ResultSet]'in kapatılması;
- satır 11-17: [PreparedStatement]'in kapatılması;
- satır 18-27: bağlantının kapatılması;
3-17. satırlardaki kapatma işlemleri, 18-25. satırlarda bağlantının kapatıldığı için gereksiz görünebilir. Aslında, bazı durumlarda bunlar gereksiz değildir ve [http://stackoverflow.com/questions/4507440/must-jdbc-resultsets-and-statements-be-closed-separately-although-the-connection] olarak bırakılması tavsiye edilir.
- 22. satır: istisna, aşağıdaki [doCatchException] yöntemi ile işlenir:
private static void doCatchException(Connection connexion, Throwable th) {
// işlemi iptal et
try {
if (connexion != null) {
connexion.rollback();
}
} catch (SQLException e2) {
// istisnayı işleme
}
}
- 4-6. satırlar: İşlem iptal edilir. Bu, işlemi sonlandırır ve SGBD, işlem için ayrılan kaynakları serbest bırakabilir;
6.3.4. 3. adım - SQL ve [INSERT, UPDATE, DELETE] komutlarının verilmesi
SQL ve [INSERT, UPDATE, DELETE] komutları güncelleme işlemleridir: veritabanını değiştirirler ancak herhangi bir satır döndürmezler. Döndürülen tek bilgi, güncelleme işleminden etkilenen satır sayısıdır.
Kod
Connection connexion = null;
PreparedStatement ps = null;
try {
// bağlantı açma
connexion = DriverManager.getConnection(url, user, passwd);
// işlemi başlat
connexion.setAutoCommit(false);
// okuma/yazma modunda
connexion.setReadOnly(false);
// tablo güncelleniyor
ps = connexion.prepareStatement("UPDATE PRODUITS SET PRIX=PRIX*1.1 WHERE CATEGORIE=?");
// kategori 1
ps.setInt(1, 10);
// yürütme
int nbLignes=ps.executeUpdate();
// işlemi kaydet
connexion.commit();
} catch (SQLException e1) {
// istisna işleniyor
doCatchException(connexion, e1);
} finally {
// finally bloğu işleniyor
doFinally(null, ps, connexion);
}
}
- 9. satır: bağlantı okuma ve yazma için kullanılır;
- 11. satır: 1 parametreli (? ile gösterilen) bir [PreparedStatement]. Birden fazla parametre olabilir. Parametreler 1'den başlayarak numaralandırılır;
- 13. satır: Tek parametreye değeri atanır. [setType]'in ilk parametresi, [PreparedStatement] içindeki parametrenin konumudur (1, 2, ...) ve ikincisi ise ona atanan değerdir. [setInt, setLong, setFloat, setDouble, setString, setDate, ...] yöntemleri kullanılabilir;
- 15. satır: SELECT emirleri için ayrılmış olan [executeQuery] yöntemi yerine [executeUpdate] yöntemi kullanılır. Yöntem, işlemden etkilenen satır sayısını döndürür. 0 olabilir.
- 17. satır: İşlem onaylanır;
6.3.5. 4. adım - bağlantının kapatılması
Çoklu kullanıcı ortamında bir bağlantı mümkün olduğunca çabuk kapatılmalıdır, çünkü SGBD komutu sınırlı sayıda açık bağlantıyı kabul eder. Önceki örneklerde, istisna olsun ya da olmasın bağlantının kapatılması için SQL işlemlerinin [finally] cümlesinde kapatılıyordu.
6.4. Örnek bir proje
6.4.1. Destek
![]() |
[support / chap5] klasörü, bu bölümdeki [1, 2] Eclipse projelerini içerir. [database] klasörü, bu bölümdeki MySQL örnek veritabanını oluşturmaya yarayan SQL komut dosyasını içerir ([1, 3]).
6.4.2. Kullanılan veritabanı
Aşağıdaki örneklerde şu MySQL veritabanı kullanılmaktadır:
![]() |
- [ID]: AUTO_INCREMENT modunda birincil anahtar (birincil anahtar belirtilmezse, SGBD tarafından oluşturulur);
- [NOM]: ürün adı - benzersiz;
- [CATEGORIE]: ürünün kategori numarası;
- [PRIX]: ürünün fiyatı;
- [DESCRIPTION]: ürün açıklaması;
Bunu, [WampServer] aracıyla şu şekilde oluşturacağız: [1-9]:
![]() |
![]() |
![]() |
![]() |
6.4.3. Eclipse Projesi
![]() |
Proje, aşağıdaki [pom.xml] dosyasıyla tanımlanan bir Maven projesidir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.jdbc</groupId>
<artifactId>intro-jdbc-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
</project>
- 8-12. satırlar: JDBC sürücüsü, SGBD MySQL5;
- 13-17. satırlar: jSON (Javascript Object Notation) formatını işleyebilen bir kütüphane (bkz. paragraf 22.6). Bunu, veritabanındaki ürünleri jSON biçiminde görüntülemek için kullanacağız;
6.4.4. Ürün sınıfı
[Produit] sınıfı şu şekildedir:
package istia.st.jdbc;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Produit {
// alanlar
private int id;
private String nom;
private int categorie;
private double prix;
private String description;
// yapıcılar
public Produit() {
}
public Produit(int id, String nom, int categorie, double prix, String description) {
this.id = id;
this.nom = nom;
this.categorie = categorie;
this.prix = prix;
this.description = description;
}
// alıcı ve ayarlayıcılar
...
// String'e dönüştürme
public String toString() {
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
}
- 34. satır: Ürünün jSON dizesini görüntülemek için jSON kütüphanesi kullanılır. Bu, aşağıdakine benzer bir görüntü sağlar:
Liste des produits :
{"id":1,"nom":"NOM1","categorie":1,"prix":100.0,"description":"DESC1"}
{"id":2,"nom":"NOM2","categorie":1,"prix":101.0,"description":"DESC2"}
Yukarıdaki [toString] yönteminin avantajı, sınıfa alanlar eklenip çıkarılsa bile, [toString] yönteminin her zaman geçerli kalmasıdır. Ayrıca, alanların kendisi nesneler ise (listeler, tablolar, sözlükler, kullanıcı nesneleri), jSON kütüphaneleri bunları da jSON dizesine dönüştürebilir;
6.4.5. [Static] sınıfı
[Static] sınıfı, ana sınıfta sıkça kullanılan kodları yöntemler halinde bir araya getirir:
package istia.st.jdbc;
import java.util.ArrayList;
import java.util.List;
public class Static {
public static List<String> getErreursFromThrowable(Throwable th) {
// istisnanın hata mesajları listesi alınır
List<String> erreurs = new ArrayList<String>();
while (th != null) {
// throwable'ın hata mesajı
erreurs.add(th.getMessage());
// throwable'ın nedenine geçilir
th = th.getCause();
}
// sonuç
return erreurs;
}
public static void show(String title, List<String> messages){
// başlık
System.out.println(String.format("%s : ",title));
// mesajlar
for(String message : messages){
System.out.println(String.format("- %s",message));
}
}
}
- 8-19. satırlar: [Exception] sınıfının üst sınıfı olan [Throwable] türündeki bir nesne içinde kapsüllenmiş hata listesini elde etmeyi sağlar;
- 21-28. satırlar: bir mesaj listesini ekrana görüntüler;
Bu kod, ana sınıfta yer alabilir çünkü burada onu kullanan tek sınıf budur. Burada, diğer sınıfların da bu koda ihtiyaç duyabileceği daha geniş bir durum ele alınmaktadır.
6.4.6. Ana sınıfın iskeleti
package istia.st.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class IntroJdbc01 {
// sabitler
final static String url = "jdbc:mysql://localhost:3306/dbIntroJdbc";
final static String user = "root";
final static String passwd = "";
final static String insert = "INSERT INTO PRODUITS(ID, NOM, CATEGORIE, PRIX, DESCRIPTION) VALUES (?, ?, ?, ?, ?)";
final static String delete = "DELETE FROM PRODUITS";
final static String select = "SELECT ID, NOM, CATEGORIE, PRIX, DESCRIPTION FROM PRODUITS";
final static String update = "UPDATE PRODUITS SET PRIX=PRIX*1.1 WHERE CATEGORIE=?";
final static String insert2 = "INSERT INTO PRODUITS(ID, NOM, CATEGORIE, PRIX, DESCRIPTION) VALUES (100,'X',1,1,'x')";
public static void main(String[] args) {
// MySQL'ten JDBC sürücüsünün yüklenmesi
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e1) {
doCatchException("Pilote JDBC introuvable", null, e1);
return;
}
// [PRODUITS] tablosu boşaltılıyor
delete();
// tablo dolduruluyor
insert();
// tablo okunuyor
select();
// güncelleniyor
update();
// görüntüleme
select();
// iki özdeş öğenin eklenmesi
// işlem nedeniyle ekleme başarısız olmalı ve iki öğeden hiçbiri eklenmemelidir
insert2();
// kontrol ediliyor
select();
// bitti
System.out.println("Travail terminé");
}
// ürün listesi
private static void select() {
...
}
// ürün silme
public static void delete() {
...
}
// ürün ekleme
public static void insert() {
...
}
// 2 ürün ekleniyor
public static void insert2() {
...
}
// bazı ürünlerin güncellenmesi
public static void update() {
..
}
private static void doFinally(ResultSet rs, PreparedStatement ps, Connection connexion) {
// kapatma ResultSet
if (rs != null) {
try {
rs.close();
} catch (SQLException e1) {
}
}
// kapatma [PreparedStatement]
if (ps != null) {
try {
ps.close();
} catch (SQLException e2) {
}
}
// bağlantıyı kapat
if (connexion != null) {
try {
connexion.close();
} catch (SQLException e3) {
// hata mesajları görüntüleniyor
Static.show("Les erreurs suivantes se sont produites lors de la fermeture de la connexion",
Static.getErreursFromThrowable(e3));
}
}
}
private static void doCatchException(String title, Connection connexion, Throwable th) {
// hata mesajları görüntüleniyor
Static.show(title, Static.getErreursFromThrowable(th));
// işlem iptal edildi
try {
if (connexion != null) {
connexion.rollback();
}
} catch (SQLException e2) {
// hata mesajları görüntüleniyor
Static.show(title, Static.getErreursFromThrowable(e2));
}
}
}
6.4.7. Ürün tablosunun içeriğinin silinmesi
[delete] yöntemi, tablonun içeriğini siler:
// ürünlerin silinmesi
public static void delete() {
Connection connexion = null;
PreparedStatement ps = null;
try {
// bağlantı açılıyor
connexion = DriverManager.getConnection(url, user, passwd);
// işlem başlangıcı
connexion.setAutoCommit(false);
// [PRODUITS] tablosu boşaltılıyor
ps = connexion.prepareStatement(delete);
ps.executeUpdate();
// işlemi kaydet
connexion.commit();
// varsayılan moda geri dön
connexion.setAutoCommit(true);
} catch (SQLException e1) {
// istisna işleniyor
doCatchException("Les erreurs suivantes se sont produites à la suppression du contenu de la table", connexion, e1);
} finally {
// finally bloğu işleniyor
doFinally(null, null, connexion);
}
}
Bu örnekte işlemler kullanılmaktadır. Bir işlem, SQL emirlerini gruplandırmaya olanak tanır; bu emirlerin tümü başarılı olmalı veya tümü iptal edilmelidir. Bilinmesi gereken dört işlem vardır:
- işlemin başlatılması: [connexion.setAutoCommit(false)];
- işlemin başarıyla tamamlanması: [connexion.commit()]. Bu durumda, işlem sırasında BD üzerinde gerçekleştirilen tüm işlemler onaylanır;
- işlemin başarısızlıkla sonuçlanması: [connexion.rollback()]. Bu durumda, işlem sırasında BD üzerinde gerçekleştirilen tüm işlemler iptal edilir;
- API'in varsayılan modu olan [auto-commit] moduna geri dönüş: JDBC : [connexion.setAutoCommit(true)]. Bu modda, her bir SQL emri bir işlem konusu olur. Dolayısıyla, iki ekleme yapıldığında ve ikincisi başarısız olursa:
- [AutoCommit=true] modunda, ilk ekleme kalır (1. AutoCommit tarafından onaylanmıştır);
- [AutoCommit=false] modunda ise ilk ekleme işlemi iptal edilir;
Örneklerimizde, bir istisna oluştuğunda her seferinde [doCatchException] yönteminde işlemi iptal ediyoruz:
private static void doCatchException(String title, Connection connexion, Throwable th) {
// hata mesajlarını görüntüleme
Static.show(title, Static.getErreursFromThrowable(th));
// işlemi iptal et
try {
if (connexion != null) {
connexion.rollback();
}
} catch (SQLException e2) {
// hata mesajları görüntüleniyor
Static.show("Erreur lors de l'annulation de la transaction", Static.getErreursFromThrowable(e2));
}
}
6.4.8. Ürün tablosunun içeriğinin oluşturulması
[insert] yöntemi, tablonun içeriğini oluşturur:
// ürünler ekleniyor
public static void insert() {
Connection connexion = null;
PreparedStatement ps = null;
try {
// bağlantı açılıyor
connexion = DriverManager.getConnection(url, user, passwd);
// işlem başlangıcı
connexion.setAutoCommit(false);
// tablo dolduruluyor
ps = connexion.prepareStatement(insert);
for (int i = 0; i < 10; i++) {
// hazırlık
int n = i + 1;
ps.setInt(1, n);
ps.setString(2, String.format("NOM%s", n));
ps.setInt(3, n / 5 + 1);
ps.setDouble(4, 100 * (1 + (double) i / 100));
ps.setString(5, String.format("DESC%s", n));
// yürütme
ps.executeUpdate();
}
// işlemi kaydet
connexion.commit();
// varsayılan moda dönüş
connexion.setAutoCommit(true);
} catch (SQLException e1) {
// istisna işleniyor
doCatchException("Les erreurs suivantes se sont produites à la création du contenu de la table", connexion, e1);
} finally {
// finally bloğunu işleme
doFinally(null, null, connexion);
}
}
6.4.9. Ürün tablosunun içeriğinin görüntülenmesi
[select] yöntemi, tablonun içeriğini görüntüler:
// ürün listesi
private static void select() {
Connection connexion = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// bağlantı açılıyor
connexion = DriverManager.getConnection(url, user, passwd);
// işlem başlangıcı
connexion.setAutoCommit(false);
// [PRODUITS] tablosu okunuyor
ps = connexion.prepareStatement(select);
rs = ps.executeQuery();
System.out.println("Liste des produits : ");
while (rs.next()) {
System.out.println(new Produit(rs.getInt(1), rs.getString(2), rs.getInt(3), rs.getDouble(4), rs.getString(5)));
}
// işlemi kaydet
connexion.commit();
// varsayılan moda geri dönülüyor
connexion.setAutoCommit(true);
} catch (SQLException e1) {
// istisna işleniyor
doCatchException("Les erreurs suivantes se sont produites à la lecture de la table", connexion, e1);
} finally {
// finally bloğu işleniyor
doFinally(null, null, connexion);
}
}
6.4.10. Tablo içeriğinin güncellenmesi
[update] yöntemi, belirli ürünleri günceller:
// bazı ürünlerin güncellenmesi
public static void update() {
Connection connexion = null;
PreparedStatement ps = null;
try {
// bağlantı açılıyor
connexion = DriverManager.getConnection(url, user, passwd);
// işlem başlangıcı
connexion.setAutoCommit(false);
// tablo güncelleniyor
ps = connexion.prepareStatement(update);
// kategori 1
ps.setInt(1, 1);
// işlem yürütülüyor
ps.executeUpdate();
// işlemi kaydet
connexion.commit();
// varsayılan moda dönüş
connexion.setAutoCommit(true);
} catch (SQLException e1) {
// istisna işleniyor
doCatchException("Les erreurs suivantes se sont produites à la mise à jour du contenu de la table", connexion, e1);
} finally {
// finally bloğunu işleme
doFinally(null, null, connexion);
}
}
6.4.11. İşlemin rolü
[insert2] yöntemi, tabloya aynı birincil anahtara sahip iki ürünü ekler; bu ise mümkün değildir. Bir işlem içinde olduğumuzdan, ilk ekleme işlemi iptal edilecektir.
// aynı biri ncil anahtarlara sahip 2 ürünün eklenmesi
public static void insert2() {
Connection connexion = null;
PreparedStatement ps = null;
try {
// bağlantı açılıyor
connexion = DriverManager.getConnection(url, user, passwd);
// işlem başlatılıyor
connexion.setAutoCommit(false);
// 1 satır ekleniyor
ps = connexion.prepareStatement(insert2);
// işlem yürütülüyor
ps.executeUpdate();
// aynı satır ikinci kez ekleniyor, dolayısıyla aynı birincil anahtarla
// işlem nedeniyle ekleme başarısız olmalı ve her iki öğe de eklenmemelidir
ps.executeUpdate();
// işlemi tamamla
connexion.commit();
// varsayılan moda geri dönülür
connexion.setAutoCommit(true);
} catch (SQLException e1) {
// istisna işlenir
doCatchException("Les erreurs suivantes se sont produites lors de l'ajout", connexion, e1);
} finally {
// finally bloğu işleniyor
doFinally(null, null, connexion);
}
}
6.4.12. Sonuçlar
[main] yönteminin çalıştırılması sonucunda aşağıdaki sonuçlar elde edilir:
6.5. [DataSource] türünde bir veri kaynağının kullanılması
[javax.sql.DataSource] türünde bir veri kaynağı kullanarak önceki uygulamayı tekrar ele alacağız:

[org.apache.tomcat.jdbc.pool.DataSource] sınıfı tarafından uygulanan bir veri kaynağını kullanacağız. Bu sınıf, bir bağlantı havuzu, yani açık bağlantı kümesi kullanır:
- havuz oluşturulduğunda, veritabanına belirli sayıda bağlantı açılır. Bu sayı yapılandırılabilir;
- Java kodu bir bağlantı açtığında, bu bağlantı havuzdan sağlanır;
- Java kodu bir bağlantıyı kapattığında, bu bağlantı havuza geri verilir;
Sonuç olarak, bağlantılar yalnızca bir kez açılır ve bu da veritabanına erişim performansını artırır. Veri kaynağı, bir Spring yapılandırma sınıfında tanımlanacaktır
6.5.1. Eclipse projesi
![]() |
Proje, aşağıdaki [pom.xml] dosyasıyla tanımlanan bir Maven projesidir:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.jdbc</groupId>
<artifactId>intro-jdbc-02</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
<!-- jSON kütüphanesi -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<!-- Tomcat Jdbc -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>8.0.20</version>
</dependency>
</dependencies>
</project>
- 21-25. satırlar: Spring bağımlılığı;
- 27-31. satırlar: veri kaynağını sağlayan kütüphaneye bağımlılık;
Spring yapılandırma sınıfı [AppConfig] şu şekildedir:
package istia.st.jdbc;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
// sabitler
final static String URL = "jdbc:mysql://localhost:3306/dbIntroJdbc";
final static String USER = "root";
final static String PASSWD = "";
final static String INSERT = "INSERT INTO PRODUITS(ID, NOM, CATEGORIE, PRIX, DESCRIPTION) VALUES (?, ?, ?, ?, ?)";
final static String DELETE = "DELETE FROM PRODUITS";
final static String SELECT = "SELECT ID, NOM, CATEGORIE, PRIX, DESCRIPTION FROM PRODUITS";
final static String UPDATE = "UPDATE PRODUITS SET PRIX=PRIX*1.1 WHERE CATEGORIE=?";
final static String INSERT2 = "INSERT INTO PRODUITS(ID, NOM, CATEGORIE, PRIX, DESCRIPTION) VALUES (100,'X',1,1,'x')";
final static String DRIVER_CLASSNAME = "com.mysql.jdbc.Driver";
@Bean
public DataSource dataSource() {
// veri kaynağı TomcatJdbc
DataSource dataSource = new DataSource();
// erişim yapılandırması JDBC
dataSource.setDriverClassName(DRIVER_CLASSNAME);
dataSource.setUsername(USER);
dataSource.setPassword(PASSWD);
dataSource.setUrl(URL);
// başlangıçta açık bir bağlantı
dataSource.setInitialSize(1);
// sonuç
return dataSource;
}
}
- 11-19. satırlar: Daha önce [IntroJdbc01]'te tanımlanan sabitler, [AppConfig]'e taşınmıştır;
- 31-34. satırlar: Veri kaynağını tanımlayan Spring bean'i;
- 24. satır: henüz yapılandırılmamış veri kaynağının oluşturulması;
- satır 26-29: Veri kaynağının veritabanına bağlanmasını sağlayan bilgiler;
- 31. satır: 1 bağlantılık bir havuz oluşturulur. Burada daha fazlasına gerek yoktur. Asla birden fazla eşzamanlı bağlantı olmaz;
6.5.2. Ana sınıf
[IntroJdbc02] ana sınıfı şu şekildedir:
package istia.st.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class IntroJdbc02 {
// veri kaynağı
private static DataSource dataSource;
public static void main(String[] args) {
// Spring bağlamının alınması
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// veri kaynağının alınması
dataSource = ctx.getBean(DataSource.class);
// [PRODUITS] tablosu boşaltılıyor
delete();
...
// bitti
ctx.close();
System.out.println("Travail terminé");
}
// ürün listesi
private static void select() {
Connection connexion = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// bağlantı açılıyor
connexion = dataSource.getConnection();
// işlem başlangıcı
connexion.setAutoCommit(false);
...
} catch (SQLException e1) {
// istisna işleniyor
doCatchException("Les erreurs suivantes se sont produites à la lecture de la table", connexion, e1);
} finally {
// finally bloğu işleniyor
doFinally(null, null, connexion);
}
}
...
}
- 14. satır: veri kaynağı. Bunun, bir arayüz olan [javax.sql.DataSource] türünde olduğuna dikkat edin;
- 18. satır: Spring nesnelerinin örneklenmesi;
- 20. satır: veri kaynağına ilişkin bir referans alınması. Hiçbir aşamada gerçekte kullanılan sınıfın adı geçmediğine dikkat edilmelidir. Dolayısıyla burada, [TomcatJdbc] uygulamasının kullanıldığına dair hiçbir ipucu yoktur;
- satır 36: açık bir bağlantının alınması;
- kodun geri kalanı [IntroJdbc01] sınıfındakiyle aynıdır;
6.6. Conclusion
Veritabanı yönetimi hakkında daha fazla bilgi, [Exploiter une base relationnelle avec l'écosystème Spring] belgesinde bulunabilir.












