Yine biraz geç de olsa, Transactions konusundaki notlarım aşağıdaki gibidir. Kitapta bayağı ilerledik aslında, diğer notlarımı da en yakın zamanda eklemeye çalışacağım.
- Transaction kavramı, bir veritabanını File System’den ayıran en önemli özelliklerdendir. Transaction’lar, veritabanını bir durumdan başka bir duruma geçirirler ve bunu atomik olarak yaparlar.
- Bir veritabanı sistemindeki Transaction’lar ACID özelliklerini sağlamalıdır.
- Atomicity : Ya hep ya hiç. Bir Transaction ya tamamlanır, ya da hiç çalışmamış gibi davranılır.
- Consistency : Bir transaction veritabanını bir durumdan diğerine geçirir. Eksik veri ile bırakmaz.
- Isolation : Transaction COMMIT edilene kadar, o transaction’un değişiklikleri diğer Transaction’lara gözükmeyebilir.
- Durability : Transaction tamamlandığında, veri sağlama alınmış demektir.
- Oracle’da bir Transaction, veriyi değiştiren yani ilk TX kilidini alan statement tarafından başlatılır, elle başlatılmaz. Bu noktadan sonra ya COMMIT ya da ROLLBACK ile Transaction sonlandırılır.
- COMMIT : Değişiklikleri kalıcı hale getirir.
- ROLLBACK : Değişiklikleri geri alır, Transaction çalışmamış gibi davranılır.
- SAVEPOINT : Transaction içinde belli noktalara geri dönüş sağlayabilmek için kullanılır.
- Tekrar söylemek lazım, DDL her zaman COMMIT edilir. DDL hata verse dahi o Transaction COMMIT edilir.
- COMMIT ve ROLLBACK, o Transaction’ın elindeki kilitlerin hepsini kaldırır.
- Eğer çalıştırılan statement bir trigger’ı tetikliyorsa ve bu trigger çalıştıktan sonra hata alınıyorsa, bu noktada yapılan ROLLBACK işlemi trigger’ın yaptığı değişiklikleri de geri alır.
- PL/SQL bloklarında çalışan prosedürler de statement olarak kabul edilir ve atomiktir.
Son iki madde ile ilgili olarak aşağıdaki örnekleri inceleyelim ve örnekler için Thomas Kyte’a teşekkür edelim. Örnekte, T2 tablosu bir trigger yardımıyla T tablosundaki satırların sayısını tutmakla görevli. T tablosuna ise bir contraint sayesinde sadece sıfırdan büyük değerler girilebiliyor:
CREATE TABLE T ( x int check( x>0 ) );
CREATE TABLE T2( cnt int );
INSERT INTO T2 VALUES( 0 );
CREATE TRIGGER t_trigger BEFORE INSERT OR DELETE
ON T FOR EACH ROW
BEGIN
if (inserting) then
UPDATE T2 set cnt = cnt + 1;
Else
UPDATE T2 set cnt = cnt - 1;
end if;
dbms_output.put_line( ‘I fired and updated ’ || sql%rowcount || ‘ rows.’ );
END;
Şimdi tabloya satır eklemeye çalışalım.
INSERT INTO T VALUES ( 1 );
I fired and updated 1 rows
1 row created.
INSERT INTO T VALUES ( -1 );
INSERT INTO T VALUES (-1 )
*
ERROR at line 1:
ORA-02290: check constraint (TKYTE.SYS_C001570) violated
I fired and updated 1 rows
Görüldüğü gibi, ilk satırı hata almadan ekleyebildik ve çıkış olarak ekrana trigger içerisinden “I fired and updated 1 rows” yazıldı. İkinci denememizde ise doğal olarak hata aldık, ancak aynı mesajı ekranda yine gördük. Yani trigger çalıştı, ve T2’deki satırı güncelledi. Bu noktada T2 tablosunda ‘2’ değerini görmeyi bekleriz. Ancak T2 tablosunda ‘1’ değeri bulunmaktadır. Yani Oracle, trigger içerisinde çalışan kodu da transaction’ın bir parçası olarak görür ve hata durumunda onu da geri alır. Diğer veritabanlarında tam tersi geçerlidir, trigger’lar kendilerinden sorumludur. Diğer sistemlerde çağırılan statement bir hata alırsa, trigger’lar kendi rollback işlemlerini yapmalıdırlar. Oracle’da statement’lar çağırılırken, aslında aşağıdaki gibi sarmalanır:
Savepoint statement1;
Insert into t values ( 1 );
If error then rollback to statement1;
Savepoint statement2;
Insert into t values ( -1 );
If error then rollback to statement2;
Eğer t_trigger’da içerisinde gerçekleştirdiği işlemler sonucunda başka bir trigger’ı tetikleseydi, hatta o da başka bir trigger’ı tetikleseydi bile, ilk ifadeye yapılan ROLLBACK sonucunda bütün trigger’ların gerçekleştirdiği işlemler geri alınacaktı.
- PL/SQL blokları da statement level atomicity özelliklerini kullanırlar. Aşağıdaki prosedürü ele alalım:
CREATE OR REPLACE PROCEDURE p
AS
BEGIN
INSERT INTO T VALUES ( 1 );
INSERT INTO T VALUES (-1 );
END;
Bu prosedürün hata alacağını zaten biliyoruz. Burada soru şu; prosedür çağırıldıktan ve hata aldıktan sonra, T ve T1 tablolarının son durumu nasıl olacak? Burada p prosedürü bir statement olarak ele alınıyor ve ya hep ya hiç mantığı ile, ikinci INSERT hata aldığında bütün prosedür geri sarılıyor. İlginç noktalardan bir diğeri, eğer prosedürü aşağıdaki gibi çağırırsak:
BEGIN
P;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
İkinci satır hata almasına rağmen, T tablosuna birinci satırın INSERT edildiğini görürüz. Bunun nedeni, yukarıda belirtilen sarmalama kodunda saklıdır. İkinci statement hata aldığında, “If Error then rollback” çalışamadan Exception yakalanır, ve böylece ROLLBACK yapılmamış olur. Buradan alınması gereken ders şudur, bir Exception bloğu transaction’un davranışını önemli ölçüde değiştirebilir.
- Tablolar üzerindeki Integrity Constraint’ler varsayılan olarak SQL çalıştırıldıktan sonra kontrol edilir. Arka arkaya çalışan SQL’lerde de her SQL ifadesinden sonra kontrol yapılır. Bu işlemin sonra yapılmasının sebebi çok basittir, aşağıdaki örneği inceleyelim:
CREATE TABLE T (x int unique);
INSERT INTO T VALUES (1);
INSERT INTO T VALUES (2);
UPDATE T set x = x + 1;
Eğer her satır UPDATE edildikten sonra kontrol edilseydi, Unique Constraint ihlali yapılmış olurdu.
- Integrity Constraint Check işlemini istersek erteleyebiliriz, ancak bu erteleme en fazla bir sonraki COMMIT’e kadar olabilir. Yani COMMIT, mutlaka yapılmamış Constraint Check’i yapar. Constraint Check işlemi her constraint için aşağıdaki şekilde ertelenebilir ve açılabilir:
SET CONSTRAINT <constraint_name> deferred;
SET CONSTRAINT <constraint_name> immediate;
- Geliştirme yaparken, sadece COMMIT edilmesi gerektiğinde COMMIT etmeliyiz. Oracle’da transaction’lar için açılan kilitler pahalı değildir. Veri bütünlüğünden ödün vererek kilitleri kullanmamaya çalışmak saçma olur, çünkü kilitlerin Oracle’da maliyeti yoktur. Okuyucular yazıcıları, yazıcılar da okuyucuları beklemez. Transaction’lar veri bütünlüğünü korumak için vardır, hız kazandırmak için değil.
- JDBC API kullanırken AUTOCOMMIT meselesine dikkat etmek gerekir. AUTOCOMMIT, her SQL ifadesinden sonra bir COMMIT çalıştırır, ve malum COMMIT çalıştırıldıktan sonra o veri geri sarılamaz.