Spring Transaction Management uygulama için basit ve yönetilebilir bir uygulama çatısı sunar. Java Transaction yönetimi JTA(Java Transaction API),JDBC(Java Database Connectivity),JPA(Java Persistence APİ), JDO(Java Data Objects) gibi standartlar altında toplanmıştır.
Spring Framework’ünde Transaction yönetimine başlamadan önce kısaca Transaction kavramının tanımını yapalım. İlişkisel Veritabanı Yönetim Sistemimlerinde(RDBMS) Transaction kavramı ACID kısaltması ile tanımlanır. İngilizce Atomicity, Consistency, Isolation ve Durability kelimelerinin baş harflerinden oluşan bu kısaltma Transaction’ı oluşturan 4 ana özelliktir. Yani veritabanı üzerinde yapılan işlem bölünmez(atomic), izole(isolation), tutarlı (consistency) ve devamlılık(durability) sağlıyarak sonlanabiliyorsa bu işlem transactional bir işlemdir. Aşagıdaki örnekte basit bir kişi formu oluşturulmuş ve kişilerin eklenme adımındaki işlem için Spring Transaction Management yer alan farklı PROPAGATION değerlerinin davranışları incelenmiştir.
Örnek formu karşılayan Controller sınıfı aşağadaki şekilde oluşturulmuştur.
doPost metodu içinde alınan name,surname,tckNo parametreleri ile Person nesnesi oluşturulup kaydetme işlemini yapması için personService.savePerson metoduna geçilmiştir. Öncesinde gelen isteğin tümüde isteğin loglanabilemsi için requestString değişkenine atanmıştır.
Aşağıda PersonServiceİmpl#savePerson metodunda önce gelen istek kaydedilmiş ardından person nesnesi kaydedilmiş son olarakta kaydedilen person nesnesi dönüş logu olarak kaydedilmiştir.
Son adımda kaydedilen dönüş logunu ApiLogService içerisinde 4 farklı propagation tipinde metod karşılamaktadır. Bu metodların hepsi aynı işi yapmakla beraber Propagation tanımları farklı olduğu için gösterdikleri rollback davranışları incelenecektir.
Not: Propagation kavramı incelenirken dikkat edilmesi gereken 2 önemli nokta vardır. Bir tanesi yapılan tanıma göre sistemde kaç tane transaction oluştuğu diğeri ise atılan exception’ın CheckedException(Exception sınıfından kalıtlayan) mı yoksa UncheckedException(RuntimeException sınıfından kalıtlayan) mı olduğudur. CheckedException default davranışı commit ederek akışa devam etmektir. UncheckedExceptions ise rollback yaparak akışa devam eder.
Yukarıdaki şekilde görüldüğü üzere REQUIRED olarak tanımlanan bir metod sistemde kendisinden önce metod hiyerarşisi içinde oluşturulan bir transaction var ise ona dahil olur. Şekilde method 2 Propagation değeri REQUIRED olarak tanımlanırsa yeni bir transaction oluşturmayacak ve method 1 transaction’ı içine dahil olacaktır.
Senaryomuza göre savePerson metodunun Propagation.REQUIRES_NEW, saveApiRequest metodunun Propagation.REQUIRES_NEW, saveResponseRequired ise Propagation.REQUIRED olarak tanımlandığını görüyoruz. saveResponseRequired metodunda dönüş logu veritabanına kaydedildikten sonra hata durumunu simüle etmek için UnCheckedException exception değeri atılmıştır. Bu durumda önce sistemde kaç adet transaction olduğuna bakıyoruz. savePerson metodu REQUIRES_NEW(aşağıdaki adımda detaylandırılacaktır) olarak tanımlandığı için yeni bir transaction oluşturulmuştur.Bu transactiona Transaction A diyelim. saveApiRequest metodu savePerson içinde yer alır ancak REQUIRES_NEW olarak tanımlandığı için yeni bir transaction daha oluşur. Bu transactiona da Transaction B diyelim. saveResponseRequired metodu savePerson içinde yer alır PROPAGATION değeri REQUIRED’tır dolayısıyla sistemde kendinden önce yer alan metod hiyerarşinde Transaction A içine dahil olur. Yani yeni bir transaction oluşmaz. Mevcut durumda sistemde 2 adet Transaction bulunmaktadır. Form üzerinden ilgili değerleri girip kaydetmek istediğimizde Transaction A içinde saveApiRequest metodu çalışır ve Transaction A commit edilir. Ardından person nesnesi savePerson metodu yardımı ile kaydedilir ancak Transaction B saveResponseRequired ile devam ettiği için commit gerçekleşmez. saveResponseRequired metodu içinde hatanın oluşması ile birlikte Transaction B rollback edilir.
Yukarıdaki şekilde görüldüğü üzere REQUIRES_NEW olarak tanımlanan bir metod sistemde kendisinden önce metod hiyerarşisi içinde oluşturulan bir transaction olmasına rağmen ona dahil olmuyor ve yeni bir transaction oluşturuluyor.
Bu adımda ki senaryomuza göre saveResponseRequiresNew metodu üsteki senaryodan farklı olarak PROGATION.REQUIRES_NEW olarak tanımlanmış. Buna göre savePerson PROGATION.REQUİRES_NEW olarak tanımlandığı için yeni bir transaction yaratılır. Buna Transaction A diyelim. saveApiRequest metodu PROGATION.REQUİRES_NEW olarak tanımlanmıştır bu da yeni bir transaction oluşturur. Bu transactiona da Transaction B diyelim. Son olarak saveResponseRequiresNew metodu PROPAGATION.REQUIRES_NEW olarak tanımlanmıştır bu da yeni bir transaction oluşturur buna da Transaction C diyelim. Form üzerinden ilgili değerleri girip kaydetmek istediğimizde Transaction A içinde saveApiRequest metodu çalışır ve Transaction A commit edilir. Ardından person nesnesi savePerson metodu yardımı ile kaydedilir ve Transaction B commit edilir. saveResponseRequiresNew metodu içinde hatanın oluşması ile birlikte Transaction C rollback edilir. Son durumda apiLog(sadece istek loğu) ve person nesnesi kaydedilirken apiLog(dönüş logu) kaydedilememiştir.
Sıklıkla kullanılmayan bir özellik olup, kendinden önce oluşturulmuş bir Transaction bekler. Aksi durumda hata verir.
Sıklıkla kullanılmayan bir özellik olup, kendinden önce hiçbir Transaction oluşturulmamış olamsini bekler. Aksi durumda hata verir.
Nested Propagation tag’i sadece DataSourceTransactionManager transaction manager ile çalışmaktadır. Çünkü her bir transaction’ın açılan bağlantı özelinde kontrol edilmesi gerekir.
Sıklıkla kullanılmayan bir özellik olup, kendinden önce bir transaction yaratılmış ise ona dahil olur ve onun içnde çalışır. Eğer kendinden önce yaratılmış bir transaction yok ise hata vermeden yine çalışır ancak transactional bir davranış sergilemez.
Sıklıkla kullanılmayan bir özellik olup, diğer tüm transaction alanlarının dışında çalıştırılır.