Öncelikle bu yazıya geçmeden önce aşağıdaki yazıları okumanızı tavsiye ederim;
Veritabanında Transaction Nedir ? Nasıl Çalışır?
Veritabanında İzolasyon Seviyeleri Nelerdir? Nasıl Çalışır?
Transaction Nasıl Çalışır?
Her şeyden önce, veritabanı işlemlerinin düz Java (JDBC) ile nasıl çalıştığını anlamanız çok önemlidir.
Start(İşlemleri Başlatma), Commit(Taahhüt Etme) ve Rollback(Geri Alma)
İlk fark şudur: Spring’in @Transactional
annotation, düz Hibernate, jOOQ veya başka bir veritabanı kitaplığını kullanmanız önemli değil.
Sonunda, hepsi bir veritabanı işlemini açmak ve kapatmak için aynı şeyi yapar, bu da şudur:
- Adımda öncelikle bir veritabanına bağlanmaya ihtiyacımız var.
DriverManager.getConnection(…)
ile connection kurulur.DataSource
işlem yapacağınız veritabanına erişim bilgilerini sağlayan objemizdir. Ancak çoğu kurumsal uygulamada,DataSource
yapılandırılmış bir yapıya sahip olur(context.xml, application.yml, application.properties gibi belgeler üzerinden okunarak global olarak set edilirler). - Adım ise, bu Java da Veritabanı işlemi başlatmanın tek yoldur.
AutoCommit(true)
her bir SQL ifadesi kendi Transactionında sarar ve çalışır,AutoCommit(false) ile
tam tersidir. Transaction’ın yönetimi sizdedir. Yani istediğiniz yerde transactiona yeni sorgu ekleyebilir, istediğiniz yerde commit eder, istediğiniz yerlerde de rollback edebilirsiniz. Ayrıca transaction başlamadan önce de TimeOut ve ReadOnly çalışıp çalışmayacağınızı, Isolation Seviyesini ve Propagation tipini ayarlayabilirsiniz. - Adım ise Transactionda yaptığımız sql değişikliklerimizi veritabanında yürürlüğe alınmasını gerçekleştirmek için kullanırız. Ya da bir istisna, hata vs. varsa o transactionda yaptığımız sql değişikliklerimizi(çalıştırdığımız insert, update, delete işlemlerini) geri almak için 4.adımı kullanırız.
Evet, bu dört satır, aşağıda birazdan bahsedeceğim @Transactional
annotation’ı kullandığınızda Spring'in yaptığı her şeydir .
NOT: HikariCP gibi bağlantı havuzu kitaplıkları , yapılandırmaya bağlı olarak sizin için auto-commit modunu otomatik olarak değiştirebilir. Ancak bu, şimdilik girmeyeceğim başka bir konudur.)
Spring Framework Transaction Yönetimine Giriş
Kapsamlı transaction desteği, Spring Framework’ü kullanmanın en çekici nedenleri arasındadır. Spring Framework, aşağıdaki faydaları sağlayan, transaction yönetimi için tutarlı bir soyutlama(abstraction) sağlar:
1. Java Transaction API (JTA), JDBC, Hibernate, Java Persistence API (JPA) ve Java Data Objects (JDO) gibi farklı transaction API’leri arasında tutarlı bir programlama modeli sağlar. Yani birden farklı modeli kullanmanıza imkan sunar.
2. Bildirime dayalı transaction yönetimi(declarative transaction management) desteği sunar. Xml ya da Annotation bazlı.
3. Programlı transaction yönetimi(programmatic transaction management) desteği sağlar.
4. Spring’in veri erişim soyutlamalarıyla(data access abstractions) mükemmel entegrasyon sunar.
Spring Framework Transaction Abstraction’ı (işlem soyutlamasını) anlama
Spring de Transaction anahtarı Abstraction’dır, bu abstraction nedir nasıl çalışır. Bir transaction stratejisi org.springframework.transaction.PlatformTransactionManager.java interface(arayüz)’i tarafından tanımlanır.
Yapısı aşağıdaki gibidir;
Ve aşağıdaki sistemler bu yapı üzerinden değişik yöntemler kullanılsa bile aynı transaction içinde çalışabilir.
Bunu nasıl yapar diye soracak olursanız akış şu şekilde;
- TransactionManager arayüzü PlatformTransactionManager ve ReactiveTransactionManager a extends edelir
2. Adımda tipine göre (Reactive ya da Not Reactive) olarak ilgili TransactionManager’ı soyutlanır.
3. Adımda işlem yapılacak olan yöntem (JDBC, Hibernate, JPA vs.), işlem tipine göre (Reactive ya da Not Reactive) olarak ilgili AbstractTransactionManager’ı extends eder.
diğerleride aynı şekilde…
Ve Polymorfizm ile yani; “Nesnenin davranışı runtime (çalışma anında) belirlendiği için, çok biçimlilik özelliği kullanarak hepsini aynı tip obje” olarak sarmallarız. Spring bize bu işi yapmak için oluşturduğu paketi yani;
spring-tx ile bu işlemleri gerçekleştiririz.
PlatformTransactionManager nasıl çalışır?
PlatformTransactionManager bir arayüzdür , kolayca mock veya gerektiğinde stubbed(köküne, derine inilebilr) edilebilir. JNDI gibi bir arama stratejisine bağlı değildir. PlatformTransactionManager uygulamaları(implementasyonları), Spring Framework IoC contanier(kapsayıcısın)daki diğer herhangi bir nesne (veya bean) gibi tanımlanır. Bu avantaj tek başına Spring Framework transactionlarını JTA ile çalışırken bile değerli bir abstraction(soyutlama) haline getirir. Transactional kod, doğrudan JTA kullanımına kıyasla çok daha kolay test edilebilir.
Yine Spring’in felsefesine uygun olarak, PlatformTransactionManager arabiriminin methodlarıdan herhangi biri tarafından oluşturulabilen TransactionException unchecked’dır (yani java.lang.RuntimeException sınıfıdan extend edilmiştir ). Transaction altyapısı arızaları neredeyse her zaman ölümcüldür. Uygulama kodunun bir transaction hatasından gerçekten kurtulabildiği nadir durumlarda, uygulama geliştiricisi yine de catch yapmayı ve TransactionException’ı handle etmeyi seçebilir. Göze çarpan nokta, geliştiricilerin bunu yapmaya zorlanmaması , isteğe bağlı olması.
getTransaction(..) methodu, TransactionDefinition parametresi bağlı, bir TransactionStatus nesnesi döner. Döndürülen TransactionStatus, yeni bir transactionı temsil edebilir veya mevcut çağrı yığınında(call stackde) eşleşen bir transaction varsa, mevcut bir transactionı temsil edebilir. Bu son durumdaki olay, Java EE transaction contextlerinde olduğu gibi, bir TransactionStatus yürütme dizisiyle (thread of execution) ilişkilendirilmesidir.
TransactionDefinition arabirimi nedir?
1. İzolasyon(İzolation)
Bu bir transactionın diğer transactionların çalışmasından yalıtılma derecesi/seviyesini ayarlar. Örneğin, bu transaction diğer transactionden commit edilmemiş yazmaları görebilir mi?
Bu konuyu database kısmında anlatmıştık. Tekrar anlatmayacağım.
Sadece önemli olan konu şu DEFAULT olarak işlem yapılan veri tabanında hangi izolasyon seviyesi kullanılıyor ise o alır.
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT)
Yani işlem yapılan datasource’ın (MySql, Oracle, Postgresql vs.) varsayılan yalıtım düzeyini kullanır. Diğer tüm seviyeler, JDBC izolasyon seviyelerine karşılık gelir.
1.1 READ_UNCOMMITTED
1.2 READ_COMMITTED
1.3 REPEATABLE_READ
1.4 SERIALIZABLE
2. Yayılma(Propagation)
Tipik olarak, bir transaction kapsamında yürütülen tüm kodlar o transactionda çalışır. Ancak, bir transaction bağlamı zaten mevcutken bir transaction methodunun yürütülmesi durumunda davranışı belirtme seçeneğiniz vardır. Örneğin, kod mevcut transactionda çalışmaya devam edebilir (genel durum); veya mevcut transactionda askıya alınabilir ve yeni bir transaction oluşturulabilir. Spring, EJB CMT’den aşina olduğunuz tüm transaction yayılım seçeneklerini sunar . 7 çeşittir;
2.1 REQUIRED
Mevcut bir transaction var ise ona katılır, yoksa yeni bir tane oluşturur. Bu bir transactionda propagation için varsayılan ayarıdır.
2.2 SUPPORTS
Mevcut bir transaction var ise ona katılır, yoksa yeni bir tane oluşturmaz. Bu bir transactionda propagation için varsayılan ayarıdır
Not: Transaction sekronizasyonuyla transaction manager için SUPPORTS eder, senkronizasyonun uygulanacağı transaction kapsamını tanımladığı için, hiç transaction olmamasından biraz farklıdır. Sonuç olarak, belirtilen kapsamın tamamı için aynı kaynaklar (JDBC Bağlantısı, Hazırda Bekletme Oturumu, vb.) paylaşılacaktır. Bunun, transaction managerin gerçek senkronizasyon yapılandırmasına bağlı olduğunu unutmayın.
2.3 MANDATORY
Mevcut bir transaction varsa ona katılır, yoksa bir exception fırlatır.
2.4 REQUIRES_NEW
Yeni bir transaction oluşturur ve varsa mevcut transactionı askıya alır.
NOT: Gerçek transactionın askıya alınma işlemi, tüm transaction managerlarda alışılmışın dışında çalışmayacaktır. Bu özellikle, javax.transaction.TransactionManager’ın (standart Java EE’de sunucuya özeldir) kullanıma sunulmasını gerektiren org.springframework.transaction.jta.JtaTransactionManager için geçerlidir.
2.5 NOT_SUPPORTED
Transaction olmadan çalışır, varsa mevcut tranactionı askıya alır.
NOT: Gerçek işlemin askıya alınması, tüm transaction managerlarda alışılmışın dışında çalışmayacaktır. Bu özellikle, javax.transaction.TransactionManager’ın (standart Java EE’de sunucuya özeldir) kullanıma sunulmasını gerektiren org.springframework.transaction.jta.JtaTransactionManager için geçerlidir.
2.6 NEVER
Transaction olmadan çalışır, varsa o tranaction için exception fırlatır.
2.7 NESTED
Geçerli bir transaction varsa iç içe bir transaction içinde yürütür, aksi takdirde REQUIRED gibi davranır.
Not: İç içe bir tranasctionın fiili olarak oluşturulması yalnızca belirli transaction managerlar üzerinde çalışır. Alışılmışın dışında, bu yalnızca JDBC DataSourceTransactionManager için geçerlidir. Bazı JTA (Java Transaction API) sağlayıcıları iç içe işlemleri de destekleyebilir.
3. Zaman Aşımı(Time-Out)
Bu bir transactionın zaman aşımına uğramadan ve temel alınan transaction altyapısı tarafından otomatik olarak geri alınmadan(rollbak) önce ne kadar süre çalışacağını söyler.
4. Salt okunur durumu(Read-Only)
Kodunuz verileri okuduğunda(read ettiğinde) ancak değiştirmediğinde(modify) salt okunur bir işlem kullanılabilir. Salt okunur işlemler, Hibernate modunu kullandığınız zamanlar gibi bazı durumlarda yararlı bir optimizasyon olabilir
Bu ayarlar standart transaction kavramlarını yansıtır. Bu kavramları anlamak, Spring Framework’ü veya herhangi bir transaction yönetimi çözümünü kullanmak için çok önemlidir.
TransactionStatus arabirimi nedir?
Transactional kodun transaction yürütmesini kontrol etmesi ve transaction durumunu sorgulaması için basit bir yol sağlar. Tüm transaction API’lerinde ortak olduğu için kavramlar tanıdık olmalıdır:
Springde bildirime dayalı veya programlı transaction yönetimini tercih etmenizden bağımsız olarak, doğru PlatformTransactionManager uygulamasını tanımlamak kesinlikle çok önemlidir. Bu uygulamayı genellikle dependecy injection(bağımlılık ekleme) yoluyla tanımlarsınız.
PlatformTransactionManager Uygulamaları
PlatformTransactionManager uygulamaları normalde çalıştıkları ortam hakkında bilgi gerektirir: JDBC, JTA, Hibernate vb. Aşağıdaki örnekler, yerel bir PlatformTransactionManager uygulamasını nasıl tanımlayabileceğinizi gösterir.
1. JDBC Veri Kaynağı(DataSourceTransactionManager)
İlgili PlatformTransactionManager bean tanımı, daha sonra DataSource tanımını içeren bir referansa sahip olacaktır. Bunun gibi görünecek:
2. JtaTransactionManager
JTA’yı bir Java EE containerında(kapsayıcısında) kullanırsanız, Spring’in JtaTransactionManager’ı ile birlikte JNDI aracılığıyla elde edilen bir container DataSource kullanırsınız. JTA ve JNDI arama (lookup)sürümü şöyle görünür:
JtaTransactionManager, containerın global transaction yönetimi altyapısını kullandığından DataSource veya diğer belirli kaynaklar hakkında bilgi sahibi olması gerekmez.
3.HibernateTransactionManager
Aşağıdaki örneklerde gösterildiği gibi, Hibernate local transactionlarını da kolayca kullanabilirsiniz. Bu durumda, uygulama kodunuzun Hibernate Session örneklerini almak için kullanacağı bir Hibernate LocalSessionFactoryBean tanımlamanız gerekir.
DataSource bean tanımı, daha önce gösterilen local JDBC örneğine benzer olacaktır ve bu nedenle aşağıdaki örnekte gösterilmemiştir.
Bu durumda txManager bean’i HibernateTransactionManager türündedir. DataSourceTransactionManager’ın DataSource’a bir referansa ihtiyaç duyması gibi, HibernateTransactionManager’ın da SessionFactory için referansa ihtiyacı vardır
Hibernate ve Java EE container tarafından yönetilen JTA transactionlarını kullanıyorsanız, JDBC için önceki JTA örneğinde olduğu gibi aynı JtaTransactionManager’ı kullanmanız yeterlidir.
NOT : JTA kullanıyorsanız, JDBC, Hibernate JPA veya desteklenen diğer herhangi bir teknoloji olsun, kullandığınız veri erişim teknolojisinden bağımsız olarak işlem yöneticisi tanımınız aynı görünecektir. Bunun nedeni, JTA işlemlerinin, herhangi bir işlem kaynağını listeleyebilen küresel işlemler olmasıdır.
Tüm bu durumlarda, uygulama kodunun değişmesi gerekmez. Bu değişiklik, LOCAL transactionlardan GLOBAL transactionlara geçmek veya bunun tersi anlamına gelse bile, yalnızca yapılandırmayı(config dosyasını) değiştirerek işlemlerin nasıl yönetildiğini değiştirebilirsiniz.
Spring Framework’ün bildirime dayalı Transaction uygulamasını anlama
Sadece @Transactional xml ya da annotasyon ile sınıflarınıza not eklemenizi, yapılandırmanıza @EnableTransactionManagement eklemenizi ve ardından her şeyin nasıl çalıştığını anlamanızı beklemeniz yeterli değildir. Bu bölümde, transactionle ilgili sorunlar olması durumunda Spring Framework’ün bildirime dayalı transaction altyapısının iç işleyişini açıklayacağız.
Spring Framework’ün bildirime dayalı transaction desteğiyle ilgili olarak kavranması gereken en önemli kavramlar, bu desteğin AOP proxy’leri aracılığıyla etkinleştirilmesi ve transactionsel tavsiyenin meta veriler (şu anda XML veya açıklama tabanlı) tarafından yönlendirilmesidir. AOP’nin transaction meta verileriyle birleşimi, transactionleri yöntem çağrıları etrafında yönlendirmek için uygun bir PlatformTransactionManager uygulamasıyla birlikte bir TransactionInterceptor kullanan bir AOP proxy’si verir.
1.XML ile Bildirime Dayalı Transaction Uygulaması
Aşağıda bizim bir config dosyamız var burada, dataSource
adında DataSource
oluştutulmuş ve txAdvice
adındaki Transaction-Manager
’a bu dataSource
referans olarak atandığını farzedelim ve bu managera 2 attribute tanımlanmış. Biri get
ile başlayan tüm methodlar transaction içinde read-only
çalışacak denmiş, diğeri ise tüm methodlar transaction içinde çalışsın denilmiş. Yani get
ile başlayanlar DB tarafında bir ekleme
, silme
, güncelleme
yapamayıp sadece sorgulayacak, diğerleri tüm methodlarCRUD
işlemlerini yapabilicek.
Ardından bu txAdvice
sadece belli bir paketin içindeki, bir sınıfa/arayüze (FooService
) uygulanması için şu x.y.service.FooService.*()
AOP pointcut
tanımlanmış. Bu ne demek? Ben bu paket içindeki bu classı tetiklediğimde, transactiona dahil olup txAdvice
uygulayacak
FooService interface’nin ilk iki methodu olan getFoo(String) ve getFoo(String, String), salt okunur semantik içeren bir transaction contextinde yürütülmesi gerektiğini ve diğer methodların, insertFoo(Foo) ve updateFoo( Foo), okuma-yazma anlambilimi ile bir transaction contextinde yürütülmelidir. Aşağıdaki yapılandırma buradaki olayı özetlemektedir.
Bildirime dayalı (declarative ) bir Transactioni Rollback Etme
Önceki bölüm, uygulamanızda bildirimsel olarak, genellikle servis katmanı sınıfları olmak üzere sınıflar için transactional ayarlarının nasıl belirleneceğinin temellerini özetledik. Bu bölüm, transactionlarin rollback edilmesini basit bir bildirime dayalı biçimde nasıl kontrol edebileceğinizi açıklayacağız.
Spring Framework’ün transaction altyapısına bir transactionin çalışmasının geri alınacağını belirtmenin önerilen yolu, şu anda bir transaction contextinde yürütülmekte olan koddan bir Exception oluşturmaktır. Spring Framework’ün transaction altyapısı kodu, çağrı yığınını doldururken işlenmeyen tüm Exceptionları yakalar ve transactionin rollback için işaretlenip işaretlenmeyeceğine karar verir.
Varsayılan yapılandırmasında, Spring Framework’ün transaction altyapısı kodu, yalnızca çalışma zamanı, unchecked exceptions(denetlenmeyen istisnalar) durumunda rollback için bir transactioni işaretler; diğer bir deyişle, atılan istisna RuntimeException’ın bir instance’ı veya alt sınıfı olduğunda. (Hatalar ayrıca — varsayılan olarak — rollback ile sonuçlanacaktır). Bir transaction methodundan fırlarılan Checked Exceptionlar, varsayılan yapılandırmada rollback ile sonuçlanmaz.
Kontrol edilen istisnalar da dahil olmak üzere, tam olarak hangi İstisna türlerinin bir transactioni rollback için işaretlediğini yapılandırabilirsiniz. Aşağıdaki XML parçacığı, kontrol edilen, uygulamaya özel bir Exception türü için rollback’i nasıl yapılandıracağınızı gösterir.
Mümkünse, geri almak için bildirimsel yaklaşımı kullanmanız şiddetle tavsiye edilir. Kesinlikle ihtiyacınız olduğunda programlı geri alma da kullanım için mevcuttur, ancak kullanımı temiz bir POJO tabanlı mimari elde etme karşısında kaybolup gidiyor, yönetimi güç oluyor.
Farklı Beanler için Farklı Transaction Semantikler (Anlambilimi) Yapılandırma
Bir dizi hizmet katmanı nesnesine sahip olduğunuz ve bunların her birine tamamen farklı bir transaction yapılandırması uygulamak istediğiniz senaryoyu düşünün. Bunu, farklı pointcut ve advice-ref öznitelik değerlerine sahip farklı <aop:advisor/> öğeleri tanımlayarak yaparsınız.
Karşılaştırma noktası olarak, önce tüm hizmet katmanı sınıflarınızın bir root x.y.service paketinde tanımlandığını varsayalım. Bu pakette (veya alt paketlerde) tanımlanan sınıfların instanceları olan ve adları Service ile biten tüm beanlerin varsayılan transaction yapılandırmasına sahip olmasını sağlamak için aşağıdakileri yazarsınız:
Aşağıdaki örnek, tamamen farklı transaction ayarlarıyla iki farklı beanin nasıl yapılandırılacağını gösterir.
<tx:advice/> Ayarları
Bu bölüm, <tx:advice/> etiketi kullanılarak belirtilebilecek çeşitli transaction ayarlarını özetlemektedir. Varsayılan <tx:advice/> ayarları şunlardır:
Yayılma(Propogation) ayarı REQUIRED.
İzolasyon seviyesi DEFAULT.
Transactiion read/write(okuma/yazma).
Transaction timeout (zaman aşımı) varsayılan olarak, temel alınan transaction sisteminin varsayılan zaman aşımına ayarlanır veya zaman aşımları desteklenmiyorsa hiçbiri yoktur.
Herhangi bir RuntimeException, rollback işlemini tetikler ama Checked Exceptionlar tetiklemez.
2. @Transactional Annotasyonunu Kullanma
Transaction yapılandırmasına yönelik XML tabanlı bildirim yaklaşımına ek olarak, annotation-based (açıklama tabanlı) bir yaklaşım kullanabilirsiniz. Transaction semantiğini doğrudan Java kaynak kodunda bildirmek, bildirimleri etkilenen koda çok daha yakın hale getirir. Transactional olarak kullanılması amaçlanan kod neredeyse her zaman zaten bu şekilde konuşlandırıldığından, gereksiz birleştirme tehlikesi yoktur.
Not : Standart javax.transaction.Transactional annotasyonu, Spring’in kendi annotasyonunun yerini alacak bir değişiklik olarak da desteklenir. Daha fazla ayrıntı için lütfen JTA 1.2 belgelerine bakın.
@Transactional annotasyonun kullanılmasının sağladığı kullanım kolaylığı, aşağıdaki metinde açıklanan bir örnekle en iyi şekilde gösterilmiştir. Aşağıdaki sınıf tanımını göz önünde bulundurun:
Yukarıdaki POJO, Spring IoC contanierda bir bean olarak tanımlandığında, bean örneği, yalnızca bir satır XML yapılandırması eklenerek transactional hale getirilebilir
Method görünürlüğü ve @Transactional
Proxy’leri kullanırken @Transactional ek açıklamasını yalnızca genel görünürlüğe sahip yani public methodlara uygulamalısınız. @Transactional annotasyonuyla protected, private veya package-visible methodlara açıklama eklerseniz hiçbir hata oluşmaz, ancak annotatad(açıklamalı) method yapılandırılmış transaction ayarlarını göstermez. Herkese açık(public) olmayan methodlara annotasyon eklemeniz gerekiyorsa AspectJ’yi kullanmayı düşünün (aşağıya bakın).
@Transactional ek açıklamasını bir interface tanımından, bir interfacedeki bir methoddan, bir sınıf tanımından veya bir sınıftaki genel bir methoddan önce yerleştirebilirsiniz. Ancak, @Transactional ek açıklamasının yalnızca varlığı, işlem davranışını etkinleştirmek için yeterli değildir. @Transactional ek açıklaması, @Transactional farkında olan ve uygun beanleri transactional davranışıyla yapılandırmak için meta verileri kullanabilen bazı runtime altyapısı tarafından tüketilebilen meta verilerdir. Önceki örnekte, <tx:annotation-driven/> öğesi işlem davranışını açar.
Not: Spring, açıklama ekleme arayüzlerinin(interfaceklerin) aksine, @Transactional notuyla yalnızca somut sınıflara (ve somut sınıfların methodlerine) açıklama eklemenizi önerir. @Transactional ek açıklamasını bir interfacee (veya bir interface methoduna) kesinlikle yerleştirebilirsiniz, ancak bu yalnızca interface tabanlı proxy’ler kullanıyorsanız beklediğiniz gibi çalışır. Java ek açıklamalarının interfacelerden miras alınmaması, sınıf tabanlı proxy’ler ( proxy-target-class=”true”) veya weaving-based aspect ( mode=”aspectj”) kullanıyorsanız, işlem ayarlarının proxy oluşturma ve weaving-based aspect tarafından tanınmaz ve nesne, kesinlikle kötü olan bir transaction proxy’sine sarılmaz.
Not: Proxy modunda (yani varsayılan modda), yalnızca proxy üzerinden gelen harici method çağrıları durdurulur. Bu, aslında, hedef nesne içindeki bir methodun, hedef nesnenin başka bir methodunu çağıran bir methodun kendi kendine çağrılmasının, çağrılan method @Transactional ile işaretlenmiş olsa bile, çalışma zamanında gerçek bir işleme yol açmayacağı anlamına gelir. Ayrıca, beklenen davranışı sağlamak için proxy’nin tamamen başlatılması gerekir, bu nedenle başlatma kodunuzda, yani @PostConstruct’ta bu özelliğe güvenmemelisiniz.
Kendi kendine çağrıların da transactionlarla sarılmasını bekliyorsanız, AspectJ modunu kullanmayı düşünün (aşağıdaki tabloda mod niteliğine bakın). Bu durumda ilk etapta bir vekil olmayacak; bunun yerine, @Transactional’ı herhangi bir method da çalışma zamanı davranışına dönüştürmek için hedef sınıf dokunacaktır (yani, bayt kodu değiştirilecektir).
Not :@EnableTransactionManagement ve <tx:annotation-driven/>, yalnızca tanımlandıkları uygulama bağlamında beanlerde @Transactional’ı arar.Bu, bir DispatcherServlet için bir WebApplicationContext’e açıklama odaklı yapılandırma koyarsanız, servislernizde değil, yalnızca denetleyicilerinizde @Transactional beanleri kontrol eder.
Bir method için transactional ayarları değerlendirilirken en çok türetilen konum önceliklidir. Aşağıdaki örnekte, DefaultFooService sınıfı, salt okunur bir transaction için ayarlarla sınıf düzeyinde açıklamalıdır, ancak aynı sınıftaki updateFoo(Foo) yöntemindeki @Transactional notu, sınıf düzeyinde tanımlanan transaction ayarlarından önceliklidir.
@Transactional Ayarları
@Transactional ile Çoklu Transaction Yöneticileri
Çoğu Spring uygulaması yalnızca tek bir transaction yöneticisine ihtiyaç duyar, ancak tek bir uygulamada birden çok bağımsız transaction yöneticisi istediğiniz durumlar olabilir. @Transactional annotasyonunun value özniteliği, kullanılacak PlatformTransactionManager’ın kimliğini isteğe bağlı olarak belirtmek için kullanılabilir. Bu, transaction yöneticisi beaninin, bean adı veya qualifier(niteleyici) değeri olabilir.
Örneğin, qualifier gösterimi kullanarak;
uygulama contextinde aşağıdaki transaction yöneticisi tanımıyla birleştirilebilir.
Bu durumda, TransactionalService üzerindeki iki method, “account” ve “order” niteleyicileri tarafından farklılaştırılan ayrı transaction yöneticileri altında çalışacaktır.
Varsayılan <tx:annotation-driven> hedef bean adı transactionManager, özel olarak nitelikli bir PlatformTransactionManager beani bulunmazsa kullanılmaya devam eder.
Özel Shortcut(kısayol) annotasyonları
@Transactional ile aynı öznitelikleri birçok farklı methodda tekrar tekrar kullandığınızı fark ederseniz, Spring’in meta-annotation açıklama desteği, belirli kullanım durumlarınız için özel kısayol annotasyonları tanımlamanıza olanak tanır.
Örneğin, aşağıdaki annotasyonları tanımlamak:
önceki bölümdeki örneği şu şekilde yazmamıza izin verir:
Burada, transaction yöneticisi qualifier(niteleyicisini) tanımlamak için sözdizimini kullandık, ancak propogation(yayılma) davranışı, rollback (geri alma) kuralları, zaman aşımları vb. dahil edebilirdik.
Proxy Nedir? Nasıl Çalışır?
“Proxy modunda (varsayılandır), yalnızca proxy üzerinden gelen harici yöntem çağrıları durdurulur. Bu, aslında, hedef nesne içindeki bir methodun, hedef nesnenin başka bir methodunu çağıran bir methodun kendi kendine çağrılmasının, çağrılan method @Transactional ile işaretlenmiş olsa bile, çalışma zamanında gerçek bir transactiona yol açmayacağı anlamına gelir.”
Aşağıdaki örnek ile olayı özetleyelim. Bizim controller katmanında saveAB() çağrıldığında, onun içinde bulunan saveA() ve saveB() için transaction açmaz. Ama controller katmanından saveA() ya da saveB() direk çağrılır ise o zaman bu iki işlem içinde transaction açılır. Yani en dışta bulunan methodu eğer transactional ise onu Proxye dahil eder içerden çağrılanları Proxy görmez.
Programlı Transaction yönetimi
Spring Framework, programlı işlem yönetimi için iki yol sağlar:
· TransactionTemplate'i kullanma.
· Doğrudan bir PlatformTransactionManager uygulamasını kullanma.
Spring ekibi, programlı işlem yönetimi için genellikle TransactionTemplate'i önerir. İkinci yaklaşım, istisna işleme daha az hantal olsa da, JTA UserTransaction API'sini kullanmaya benzer.
TransactionTemplate Kullanma
TransactionTemplate, JdbcTemplate gibi diğer Spring şablonlarıyla aynı yaklaşımı benimser. Uygulama kodunu, işlemsel kaynakların standart edinimini ve serbest bırakılmasını yapmaktan kurtarmak için bir geri arama yaklaşımı kullanır ve yazılan kodun yalnızca geliştiricinin ne yapmak istediğine odaklanmasıyla, niyet odaklı kodla sonuçlanır.
Not : Aşağıdaki örneklerde göreceğiniz gibi, TransactionTemplate kullanmak sizi kesinlikle Spring'in işlem altyapısı ve API'leri ile eşleştirir. Programlı işlem yönetiminin geliştirme ihtiyaçlarınız için uygun olup olmadığı, kendiniz vermeniz gereken bir karardır.
Bir işlem bağlamında yürütülmesi gereken ve TransactionTemplate'i açıkça kullanacak olan uygulama kodu aşağıdaki gibi görünür. Bir uygulama geliştiricisi olarak, bir işlem bağlamında yürütmeniz gereken kodu içeren bir TransactionCallback uygulaması (genellikle anonim bir iç sınıf olarak ifade edilir) yazarsınız. Daha sonra özel TransactionCallback'inizin bir örneğini TransactionTemplate'de gösterilen execute(..) yöntemine iletirsiniz.
Callback içindeki kod, sağlanan TransactionStatus nesnesinde setRollbackOnly() yöntemini çağırarak işlemi geri alabilir
Transaction Ayarlarının Belirtilmesi
Yayılma modu, yalıtım düzeyi, zaman aşımı vb. gibi işlem ayarlarını TransactionTemplate'de programlı olarak veya yapılandırmada belirtebilirsiniz. TransactionTemplate örnekleri varsayılan olarak, varsayılan işlem ayarlarına sahiptir. Aşağıdaki örnek, belirli bir TransactionTemplate için işlem ayarlarının programlı özelleştirilmesini gösterir:
Aşağıdaki örnek, Spring XML yapılandırmasını kullanarak bazı özel işlem ayarlarına sahip bir TransactionTemplate tanımlar. sharedTransactionTemplate daha sonra gerektiği kadar hizmete enjekte edilebilir.
Son olarak, TransactionTemplate sınıfının örnekleri iş parçacığı güvenlidir, bu örneklerde herhangi bir konuşma durumu korunmaz. Bununla birlikte, TransactionTemplate örnekleri yapılandırma durumunu korur, bu nedenle birkaç sınıf tek bir TransactionTemplate örneğini paylaşabilirken, bir sınıfın farklı ayarlarla (örneğin, farklı bir yalıtım düzeyi) TransactionTemplate kullanması gerekiyorsa, o zaman iki tane oluşturmanız gerekir. farklı TransactionTemplate örnekleri.
PlatformTransactionManager'ı Kullanma
İşleminizi yönetmek için doğrudan org.springframework.transaction.PlatformTransactionManager’ı da kullanabilirsiniz. Bean referansı aracılığıyla, kullandığınız PlatformTransactionManager uygulamasını fasulyenize aktarmanız yeterlidir. Ardından, TransactionDefinition ve TransactionStatus nesnelerini kullanarak işlemleri başlatabilir, geri alabilir ve taahhüt edebilirsiniz
Programlı(Programatic) ve Bildirime Dayalı(Declarativ) Transaction Yönetimi Arasında Seçim Yapma
Programlı transaction yönetimi, yalnızca az sayıda transactional işleminiz varsa genellikle iyi bir fikirdir. Örneğin, yalnızca belirli güncelleme işlemleri için transaction gerektiren bir web uygulamanız varsa, Spring veya başka bir teknolojiyi kullanarak transactional proxy'leri kurmak istemeyebilirsiniz. Bu durumda, TransactionTemplate kullanmak iyi bir yaklaşım olabilir. Transaction adını açıkça belirleyebilmek, yalnızca transaction yönetimine programlı yaklaşım kullanılarak yapılabilecek bir şeydir.
Öte yandan, uygulamanızın çok sayıda transaction işlemi varsa, bildirime dayalı işlem yönetimi genellikle faydalı olur. Transaction yönetimini iş mantığının dışında tutar ve yapılandırılması zor değildir. EJB CMT yerine Spring Framework kullanıldığında, bildirime dayalı transaction yönetiminin yapılandırma maliyeti büyük ölçüde azalır.
Referanslar;