JavaRush /Kurslar /All lectures for UZ purposes /Deklarativ tranzaktsiyalarni boshqarish

Deklarativ tranzaktsiyalarni boshqarish

All lectures for UZ purposes
Daraja, Dars
Mavjud
Ko'pchilik Spring Framework foydalanuvchilari deklarativ tranzaktsiyalarni boshqarishni tanlashadi. Bu variant dastur kodiga eng kam ta'sir qiladi va shu sababli, eng mos keladigan yengil konteyner g'oyasiga yaqinroqdir.

Deklarativ tranzaktsiyalarni boshqarish Spring Frameworkda aspektga yo'naltirilgan dasturlash (AOP) orqali amalga oshiriladi. Lekin tranzaktsiya aspekt kodlari Spring Framework distributivida keltirilgan va shablon sifatida foydalanish mumkin bo'lgani uchun, bu kodlardan samarali foydalanish uchun AOP tushunchalarini bilish shart emas.

Deklarativ tranzaktsiyalarni boshqarish Spring Frameworkda EJBdan kelgan CMTga o'xshaydi, chunki tranzaktsiyalarni qanday amalga oshirish (yoki umuman amalga oshirmaslik)ni aniqlash mumkin. Zarur bo'lganda tranzaktsiya kontekstida setRollbackOnly() chaqiruvini amalga oshirish mumkin. Ikkala turdagi tranzaktsiyalarni boshqarish orasidagi farqlar quyidagicha:

  • EJBdagi CMTdan farqli o'laroq, JTAga bog'langan, Spring Frameworkdagi deklarativ tranzaktsiyalarni boshqarish har qanday muhitte ishlaydi. U JTA tranzaktsiyalari yoki JDBC, JPA yoki Hibernate bilan lokal tranzaktsiyalar bilan ishlashi mumkin, konfiguratsiya fayllariga tuzatishlar kiritish orqali.

  • Spring Frameworkda deklarativ tranzaktsiyalarni boshqarishni har qanday klassga qo'llash mumkin, faqat EJB klasslari kabi maxsus klasslarga emas.

  • Spring Framework deklarativ orqaga qaytarish qoidalarini taklif qiladi, EJBda analogi yo'q. Orqaga qaytarish qo'llab-quvvatlovi dasturli va deklarativ mavjud.

  • Spring Framework tranzaktsiyalar logikasini AOP orqali sozlash imkonini beradi. Masalan, tranzaktsiya orqaga qaytarilganida maxsus logikani qo'shish mumkin. Shuningdek, tranzaktsion maslahatlar bilan birga istalgan maslahatlarni qo'shish mumkin. EJBdagi CMTdan foydalanish jarayonida, konteynerning tranzaktsiyalarni boshqarishiga setRollbackOnly() orqali ta'sir ko'rsatish mumkin emas.

  • Spring Framework yuqori darajali dastur serverlarida bo'lgani kabi, tranzaktsiya kontekstlarini masofaviy chaqiriqlar orqali tarqatishni qo'llab-quvvatlamaydi. Agar ushbu funktsiya kerak bo'lsa, EJBdan foydalanishni tavsiya qilamiz. Ammo, tranzaktsiyalar odatda masofaviy chaqiriqlarni qamrab olmasligi kerakligini hisobga olib, bunday funktsiyadan foydalanishdan oldin yaxshilab o'ylab ko'ring.

Orqaga qaytarish qoidasi tushunchasi juda muhimdir. Ushbu qoidalar qaysi istisnolar (va ko'tariladigan voqealar) avtomatik orqaga qaytarishiga olib kelishi kerakligini belgilashga imkon beradi. Bu Java kodida emas, konfiguratsiyada deklarativ ravishda belgilanishi mumkin. Shu sababli, TransactionStatus obyekti uchun setRollbackOnly()ni chaqirib, amaldagi tranzaktsiyani orqaga qaytarishingiz mumkin, lekin ko'pincha MyApplicationExceptionException istisnosi o'zgartirilganda har doim orqaga qaytarilishi shart bo'lgan qoidani belgilash yaxshiroqdir. Ushbu variantning muhim afzalligi shundaki, biznes obektlar tranzaktsion infratuzilmadan mustaqil bo'ladi. Masalan, odatda Spring tranzaktsiya API interfeyslarini yoki boshqa Spring API interfeyslarini import qilishni talab qilmaydi.

EJB konteynerining standart ishlash logikasi odatda tranzaktsiyani tizim istisnosi (odatda ish vaqti istisnosi)da avtomatik ravishda qaytaradi. CMT esa EJBdan tranzaktsiyani dastur istisnosi (ya'ni java.rmi.RemoteExceptionni hisobga olmaganda tekshiriladigan istisno)da avtomatik qaytarmaydi. Springda deklarativ tranzaktsiyalarni boshqarishning standart logikasi EJB kelishuvi ortidan keladi (orqaga qayta ishlash faqat tekshirilmagan istisnolarni avtomatik ravishda sodir bo'ladi), lekin ko'pincha bu logikani sozlash foydali bo'ladi.

Deklarativ tranzaktsiyani Spring Frameworkda amalga oshirish haqida asosiy ma'lumotlar

Oddiygina, klasslarni @Transactional annotatsiyasi bilan belgilang, konfiguratsiyaga @EnableTransactionManagement annotatsiyasini qo'shing va hamma narsa qanday ishlashini tushunishni kutmang. Yanada chuqur tushunishni ta'minlash uchun ushbu bo'limda Spring Framework deklarativ tranzaktsion infratuzilmasining ichki mexanizmlari tranzaktsiya bilan bog'liq masalalar kontekstida tasvirlangan

Spring Frameworkda deklarativ tranzaktsiyalarni qo'llab-quvvatlash bilan bog'liq bo'lgan eng muhim tushunchalar shundaki, bu yordam AOP proksi orqali ta'minlanadi va tranzaktsion maslahatlar metama'lumotlar (hozirda XML yoki annotatsiya asosida) asosida boshqariladi. AOPni tranzaktsion metama'lumotlar bilan birlashtirish AOP proksini yaratadi, u TransactionInterceptorni tegishli TransactionManagerni (tranzaktsiyalarni boshqarish uchun qo'llaniladigan) amalga oshirish bilan birgalikda, metod chaqiruvlarini o'z atrofida boshqarish uchun qo'llaniladi.

Springda AOP AOP bo'limida ko'rib chiqildi.

TransactionInterceptor Spring Frameworkda imperativ va reaktiv dasturlash modellari uchun tranzaktsiyalarni boshqarishni ta'minlaydi. Tusuvchi tranzaktsiyalarni boshqarishning istalgan turini aniqlaydi, metodning qaytariladigan turini tekshirish orqali. Metodlar, qaytariladigan turlar, masalan, Publisher yoki Flow (yoki Kotlin tili bo'yicha ularning subtipi) reaktiv tranzaktsiyalarni boshqarishga mos keladi. Boshqa barcha qaytariladigan turlar, jumladan void, imperativ tranzaktsiyalarni boshqarish uchun kodning bajarilish yo'lini ishlatadilar.

Tranzaktsiyalarni boshqarishning xususiyatlari qaysi tranzaktsiya boshqaruvchisi talab qilinishi kerakligini ta'sir qiladi. Imperativ tranzaktsiyalar PlatformTransactionManagerni talab qiladi, reaktiv tranzaktsiyalar esa ReactiveTransactionManager ni amalga oshirishni talab qiladi.

Annotatsiya @Transactional odatda PlatformTransactionManager tomonidan boshqarilgan, oqimga bog'langan tranzaktsiyalar bilan ishlaydi, hozirgi bajarilayotgan oqimda barcha ma'lumotlarga kirish operatsiyalari uchun tranzaktsiya ochib beradi. Eslatma: bu emas metod ichida qayta ishga tushirilgan oqimlarga nisbatan qo'llanilmaydi.

Reaktiv tranzaktsiya, ReactiveTransactionManager tomonidan boshqariladigan, Reactorning konteksini ishlatadi, oqimning lokal xususiyatlarining o'rniga. Natijada, barcha ishtirok etgan ma'lumotlarga kirish operatsiyalari ayni Reactorning bir xil reaktiv konveyeridagi kontekstdan bajarilishi kerak.

Quyidagi rasmda tranzaktsion proksi uchun metod chaqiruvi kontseptual tasviri ko'rsatilgan:

Deklarativ tranzaktsiyani amalga oshirish misoli

Quyidagi interfeys va uning mos keladigan amalga oshirilishini ko'rib chiqing. Ushbu misolda Foo va Bar klasslari nusxa sifatida ishlatiladi, shuning uchun siz ma'lum modelga chalg'imasdan tranzaktsiyalarni ishlatishga e'tibor bera olasiz. Ushbu misol maqsadlari uchun, DefaultFooService klassi UnsupportedOperationExceptionning har bir amalga oshirilgan metodining tanasida generatsiya qilish ijobiy. Bu o'zining qanday yaratilganligini va keyinchalik UnsupportedOperationException namunasiga javoban qanday orqaga qaytarilganligini tushunishga imkon beradi. Quyidagi listinlda FooService interfeysi ko'rsatilgan:

Java
// xizmat interfeysi, biz uni tranzaktsiyalashni xohlashamiz
package x.y.service;
public interface FooService {
    Foo getFoo(String fooName);
    Foo getFoo(String fooName, String barName);
    void insertFoo(Foo foo);
    void updateFoo(Foo foo);
}
Kotlin
// xizmat interfeysi, biz uni tranzaktsiyalashni xohlashamiz
package x.y.service
interface FooService {
    fun getFoo(fooName: String): Foo
    fun getFoo(fooName: String, barName: String): Foo
    fun insertFoo(foo: Foo)
    fun updateFoo(foo: Foo)
}

Quyidagi misolda oldingi interfeysi amalga oshirilishi ko'rsatilgan:

Java
package x.y.service;
public class DefaultFooService implements FooService {
    @Override
    public Foo getFoo(String fooName) {
        // ...
    }
    @Override
    public Foo getFoo(String fooName, String barName) {
        // ...
    }
    @Override
    public void insertFoo(Foo foo) {
        // ...
    }
    @Override
    public void updateFoo(Foo foo) {
        // ...
    }
}
Kotlin
package x.y.service
class DefaultFooService : FooService {
    override fun getFoo(fooName: String): Foo {
        // ...
    }
    override fun getFoo(fooName: String, barName: String): Foo {
        // ...
    }
    override fun insertFoo(foo: Foo) {
        // ...
    }
    override fun updateFoo(foo: Foo) {
        // ...
    }
}

Aytaylik, FooService interfeysining birinchi ikki metodi, getFoo(String) va getFoo(String, String), "faqat o'qish uchun" semantikasi bilan tranzaktsiya kontekstida bajarilishi kerak, va boshqa metodlar, insertFoo(Foo) va updateFoo(Foo), "o'qish-yazish" semantikasi bilan tranzaktsiya kontekstida bajarilishi kerak. Keyingi konfiguratsiya keyingi bir necha paragraflarda batafsil tasvirlanadi:

<!-- "context.xml" fayldan-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- bu xizmat obyektini tranzaktsiyalashni xohlaymiz -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>
    <!-- tranzaktsion maslahat (nima "sodir bo'ladi"; quyidagi <aop:advisor/> bean qarang ) -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- tranzaktsiya semantikasi...... -->
        <tx:attributes>
            <!-- hamma "get" bilan boshlanadigan metodlar faqat o'qish uchun -->
            <tx:method name="get*" read-only="true"/>
            <!-- boshqa metodlar standart tranzaktsion parametrlaridan foydalanadi (quyida qarang) -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!-- yuqorida aytib o'tilgan tranzaktsion maslahat, FooService interfeysida belgilangan har qanday amalga oshirish operatsiyasida bajarilishiga ishonch hosil qiling -->
    <aop:config>
        <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
    </aop:config>
    <!-- DataSourceni unutmang -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
        <property name="username" value="scott"/>
        <property name="password" value="tiger"/>
    </bean>
    <!-- Shuningdek, TransactionManagerni unutmang -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- boshqa <bean/--> belgilari -->
</beans>

Yuqoridagi konfiguratsiyani o'rganing. Siz xizmat obyektini, fooService bini tranzaktsiyalashni xohlayotganingizni taxmin qilinadi. Amalga oshirilishi kerak bo'lgan tranzaktsion semantika <tx:advice/> ta'rifida joylashgan. <tx:advice/> ta'rifi shunday deydi: "barcha get bilan boshlanadigan metodlar faqat o'qish uchun tranzaktsiyada bajarilishi kerak, va boshqa barcha metodlar tranzaktsion parametrlar bilan standart tarzda amalga oshirilishi kerak". <tx:advice/> tegining transaction-manager atributida boshqaradigan TransactionManager bini nomi ko'rsatiladi (bu holda txManager bini).

Agar <tx:advice/>da TransactionManager bini nomi transactionManager bo'lsa, tranzaktsion maslahatda ( <tx:advice/>) transaction-manager atributini tushirib qo'yish mumkin. Agar siz bilan bog'lanmoqchi bo'lgan TransactionManager bini boshqa nomga ega bo'lsa, u holda oldingi misolda bo'lgani kabi transaction-manager atributini ochiq-oydin ishlatishingiz kerak.

<aop:config/> ta'rifi bilan txAdvice bini tomonidan aniqlangan tranzaktsion maslahatlar ma'lum dastur nuqtalarida ishga tushiriladi. Birinchidan, bir nuqta aniqlanadi, undan hamma FooService(fooServiceOperation) interfeysida aniqlangan har qanday operatsiya bajarilishi mos keladi. Keyin nuqta txAdvice bilan Advisor yordamida bog'lanadi. Natijada fooServiceOperationning bajarilishida txAdviceda aniqlangan maslahat ishga tushadi.

<aop:pointcut/> elementida aniqlangan ifoda AspectJdan chiqadigan nuqta ifodasi hisoblanadi. Springdagi nuqta ifodalari haqida ko'proq ma'lumot olish uchun AOP bo'limida qarang.

Ko'pincha butun xizmat darajasini tranzaktsion qilish kerak bo'ladi. Buni amalga oshirishning eng yaxshi yo'li, barcha daraja xizmatida har qanday operatsiya ifodasiga mos kelishiga nuqta ifodasini o'zgartirishdir. Keyingi misolda buni qanday qilishni ko'rsatadi:

<aop:config>
    <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>
Oldingi misolda faraz qiladiki, barcha xizmat interfeyslari x.y.service paketida aniqlangan. Ko'proq ma'lumot olish uchun AOP bo'limida qarang.

Endi biz konfiguratsiyani tahlil qilganimizdan keyin, sizda savol paydo bo'lishi mumkin: "Nega bu konfiguratsiya nima qiladi?".

Yuqorida ko'rsatilgan konfiguratsiya fooService binining ta'rifidan yaratilgan ob'ekt atrofida tranzaktsion proksini yaratish uchun foydalaniladi. Proksi tranzaktsion maslahatlar yordamida sozlangan shunday qilib, agar tegishli metod proksi orqali chaqirilsa, tranzaktsiyalar faqat o'qish uchun ochiladi, to'xtatiladi, belgilanishi mumkin va hokazo, bu metodga bog'liq tranzaktsion konfiguratsiyani asosida amalga oshiriladi. Quyidagi dasturga qarang, u yuqorida ko'rsatilgan konfiguratsiyani sinovdan o'tkazish uchun ishlatiladi:

Java
public final class Boot {
    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
        FooService fooService = ctx.getBean(FooService.class);
        fooService.insertFoo(new Foo());
    }
}
Kotlin
import org.springframework.beans.factory.getBean
fun main() {
    val ctx = ClassPathXmlApplicationContext("context.xml")
    val fooService = ctx.getBean<FooService>("fooService")
    fooService.insertFoo(Foo())
}

Oldingi dasturning natijasi quyidagicha ko'rinishi kerak (Log4J chiqishi va UnsupportedOperationException, DefaultFooServicening insertFoo(..) metodida generatsiya qilingan stack izi qoladi):

<!-- Spring konteyneri ishga tushmoqda... -->
[AspectJInvocationContextExposingAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'fooService' with 0 common interceptors and 1 specific interceptors
<!-- DefaultFooService aslida proksi qilingan -->
[JdkDynamicAopProxy] - Creating JDK dynamic proxy for [x.y.service.DefaultFooService]
<!-- ... endi proksi ustida insertFoo(..) metodi chaqirilmoqda -->
[TransactionInterceptor] - Getting transaction for x.y.service.FooService.insertFoo
<!-- bu erda tranzaktsion maslahat amal qiladi...  -->
[DataSourceTransactionManager] - Creating new transaction with name [x.y.service.FooService.insertFoo]
[DataSourceTransactionManager] - Acquired Connection [org.apache.commons.dbcp.PoolableConnection@a53de4] for JDBC transaction
<!-- DefaultFooService ning insertFoo(..) metodi istisnoni yaratmoqda... -->
[RuleBasedTransactionAttribute] - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
[TransactionInterceptor] - Invoking rollback for transaction on x.y.service.FooService.insertFoo due to throwable [java.lang.UnsupportedOperationException]
<!-- va tranzaktsiya orqaga qaytarilmoqda (standart bo'yicha RuntimeException holatlari orqaga qaytarilishga sabab bo'ladi) -->
[DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection@a53de4]
[DataSourceTransactionManager] - Releasing JDBC Connection after transaction
[DataSourceUtils] - Returning JDBC Connection to DataSource
Exception in thread "main" java.lang.UnsupportedOperationException at x.y.service.DefaultFooService.insertFoo(DefaultFooService.java:14)
<!-- Ochiqlik izlari chiqarilgan izlari ko'rinishining tushirilib ketgan -->
at $Proxy0.insertFoo(Unknown Source)
at Boot.main(Boot.java:11)

Reaktiv tranzaktsiyalarni boshqarishni ishlatish uchun kod reaktiv turlarni ishlatishi kerak.

Spring Framework ReactiveAdapterRegistryni ishlatadi, metodining qaytariladigan turi reaktiv ekanligini belgilash uchun.

Quyidagi listinlda oldin foydalanilgan FooServicening o'zgartirilgan versiyasi ko'rsatilgan, lekin bu safar reaktiv turlar kodda qo'llanilgan:

Java
// reaktiv xizmat interfeysi, uni tranzaktsiyalashni xohlaymiz
package x.y.service;
public interface FooService {
    Flux<Foo> getFoo(String fooName);
    Publisher<Foo> getFoo(String fooName, String barName);
    Mono<Void> insertFoo(Foo foo);
    Mono<Void> updateFoo(Foo foo);
}
Kotlin
// reaktiv xizmat interfeysi, uni tranzaktsiyalashni xohlaymiz
package x.y.service
interface FooService {
    fun getFoo(fooName: String): Flow<Foo>
    fun getFoo(fooName: String, barName: String): Publisher<Foo>
    fun insertFoo(foo: Foo) : Mono<Void>
    fun updateFoo(foo: Foo) : Mono<Void>
}

Quyidagi misolda oldingi interfeysni amalga oshirish ko'rsatilgan:

Java
package x.y.service;
public class DefaultFooService implements FooService {
    @Override
    public Flux<Foo> getFoo(String fooName) {
        // ...
    }
    @Override
    public Publisher<Foo> getFoo(String fooName, String barName) {
        // ...
    }
    @Override
    public Mono<Void> insertFoo(Foo foo) {
        // ...
    }
    @Override
    public Mono<Void> updateFoo(Foo foo) {
        // ...
    }
}
Kotlin
package x.y.service
class DefaultFooService : FooService {
    override fun getFoo(fooName: String): Flow<Foo> {
        // ...
    }
    override fun getFoo(fooName: String, barName: String): Publisher<Foo> {
        // ...
    }
    override fun insertFoo(foo: Foo): Mono<Void> {
        // ...
    }
    override fun updateFoo(foo: Foo): Mono<Void> {
        // ...
    }
}

Imperativ va reaktiv tranzaktsiyalarni boshqarish tranzaktsiyalarni chegaralarni va atributlarni aniqlash uchun bir xil semantikaga ega. Imperativ va reaktiv tranzaktsiyalar orasidagi asosiy farq oxirgilarning kechiktirilgan tabiati hisoblanadi. TransactionInterceptor qaytariladigan reaktiv turga tranzaktsion operator qo'shish va tranzaktsiyani tozalash orqali boshlanadi. Shuning uchun, tranzaktsiyali reaktiv metodni chaqirish, reaktiv turdagi ishlovni boshlash uchun tranzaktsiyalarning haqiqiy boshqaruvini turga beradi.

Reaktiv tranzaktsiyalarni boshqarishning boshqa jihati, oldina hisobga olish modeli dasturlash modeli natijasidir.

Imperativ tranzaktsiyalarni qaytarilgan qiymatlar, metod muvaffaqiyatli yakunlanganda, tranzaktsion metodlardan qaytariladi, shuning uchun qisman hisoblangan natijalar metodni yopishdan chiqmasligi kerak.

Reaktiv tranzaktsiyalarni metodlari reaktiv funktsiya-qatlamining tipini qaytaradi, bu hisoblashlar ketma-ketligini va va'da sifatida hisoblashlar boshlangani va yakunlanganini ifodalab turadi.

Publisher tranzaktsiya hali davom etayotgan bo'lsa ham, ma'lumotlarni ishlab chiqarishi mumkin, lekin yakunlangan sharti shart emas. Shuning uchun, butun tranzaktsiyaning muvaffaqiyatli yakunlanishiga bog'liq bo'lgan metodlar, kodni qo'ng'iroq qiladigan kodda hisoblash va natijalarni buferlashga bo'lishi kerak.

Deklarativ tranzaktsiyani orqaga qaytarish

Oldingi bo'limda sinflaringizda (odatda xizmatlar darajasidagi sinflar) tranzaktsion attributlarni deklarativ ravishda qanday belgilash haqida asosiy ma'lumotlar berilgan. Ushbu bo'limda XML konfiguratsiyada oddiy, deklarativ usulda tranzaktsiyalarni qanday orqaga qaytarish nazorat qilinishi mumkinligi tasvirlangan. @Transactional annotatsiyasining orqaga qaytarish semantikasini boshqarish haqida batafsilroq ma'lumotni Anotatsiya @Transactional Atributlari bo'limida ko'ring.

Spring Framework tranzaktsion infratuzilmasiga tranzaktsiyalarni orqaga qaytarishni buyurish uchun tavsiya etilgan usul tranzaktsiya kontekstida hozirda ishga tushirilayotgan koddan Exceptionni chiqarib tashlashdir. Agar qo'ng'iroqlar stackida ko'tarilsa, Spring Framework tranzaktsiya infratuzilmasi kanti kod har qanday qaytarilgan Exceptionni tuzish va tranzaktsiyani orqaga qaytarish uchun belgilash kerakmi yoki yo'qmi degan qaror qabul qiladi.

Standart konfiguratsiyada, Spring Framework tranzaktsiya infratuzilmasi kodi orqaga qaytarishni faqat runtime ortda qoladigan RuntimeException holatlari uchun belgilaydi. Bu, RuntimeExceptionning instansi yoki uning subklass bo'lsa. (Standart bo'yicha Error instancei ham orqaga qaytarishga olib keladi). Tranzaktsion metoddan chiqarib tashlangan tekshirilgan holatlar standart konfiguratsiyada orqaga qaytarishga olib kelmaydi.

Tranzaktsiya orqaga qaytarish kerak bo'lgan Exception turini, jumladan tekshiriladigan istisnolarni, aniq belgilash uchun orqaga qaytarish qoidalarini belgilashingiz mumkin.

Orqaga qaytarish qoidalari

Orqaga qaytarish qoidalari ma'lum bir istisno yuz berganda tranzaktsiyani orqaga qaytarish kerakligi aniqlab beradi va bu qoidalar shablonlarga asoslanadi. Shablon to'liq klass nomi yoki boshqa klass nomining bir qismi bo'lishi mumkin, istisno turi uchun (u Throwablening subklass bo'lishi kerak), hozirda joker belgilarni qo'llab-quvvatlash mavjud emas. Masalan, qiymat "javax.servlet.ServletException" yoki "ServletException" javax.servlet.ServletException va uning subklasslari bilan mos keladi.

Orqaga qaytarish qoidalari XMLda rollback-for va no-rollback-for atributlari orqali konfiguratsiya qilinishi mumkin, bu esa shablonlarni satrlar sifatida belgilash imkonini beradi. Anotatsiya ishlatilatur @Transactional, orqaga qaytarish qoidalari rollbackFor/noRollbackFor va rollbackForClassName/noRollbackForClassName atributlari orqali konfiguratsiya qilinishi mumkin, bu esa shablonlarni Class havolalari yoki satrlar sifatida belgilash imkonini beradi. Agar istisno turi klass havolasi sifatida belgilangan bo'lsa, shablon sifatida uning to'liq nomi ishlatiladi. Shunday qilib, anotatsiya @Transactional(rollbackFor = example.CustomException.class) @Transactional(rollbackForClassName = "example.CustomException") anotatsiyasiga teng.

Shablon qanchalik aniq ekanligini va paket ma'lumotlarini (bu majburiy emas) kiritish kerakmi yoki yo'qligini diqqat bilan o'ylab chiqishingiz kerak. Masalan, "Exception" deyarli hamma bilan mos kelib, boshqa qoidalarni yashiradi. Agar "Exception" bo'lib, barcha tekshiriladigan istisnolar uchun qoidalarni belgilasa, <code>"java.lang.Exception"</code> mos keladi. "BaseBusinessException" kabi yanada maxsus ismli istisnolarni ishlatishda, istisnolar shablonlari uchun to'liq shablonlarni ishlatish ehtiyoji ko'p emas.

Shuningdek, orqaga qaytarish qoidalari bir xil nomli istisnolar va ichki sinflar uchun kutilmagan mos kelishlarga sabab bo'lishi mumkin. Bu, ko'tarilgan istisno, orqaga qaytarish qoidalari uchun mos keluvchi istisno shablonini o'z ichiga olgan deb hisoblanadi. Masalan, com.example.CustomException ga mos keladigan qoidalar com.example.CustomExceptionV2 nomli istisnogacha mos keladi (bu CustomException bilan bir xil paketda istisno, lekin qo'shimcha suffiks bilan) yoki com.example.CustomException$AnotherException nomli istisno (bu CustomException ichida e'lon qilingan ichki klass sifatida istisno).

Keyingi XML bo'lagi, ilova uchun specific turdagi tekshirilgan Exceptionni orqaga qaytarish uchun qanday konfiguratsiya qilish mumkinligini ko'rsatadi, rollback-for atributi orqali istisno shablonini ta'minlaydi:

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

Agar sizga tranzaktsiya istisno yaratilganda orqaga qaytarilishi kerak bo'lmasa, shuningdek "orqaga qaytarilmaslik" qoidalarini belgilashingiz mumkin. Keyingi misolda Spring Framework tranzaktsiya infratuzilmasiga InstrumentNotFoundException qaytarilgan holda tranzaktsiyani qayta tiklash buyrug'i berilgan:

<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
    <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

Agar Spring Framework infratuzilmasi istisnoni tutsa va tranzaktsiyani orqaga qaytarish uchun belgilanish keragi aniqlash uchun konfiguratsiya qilingan qoidalarga murojaat qilsa, ustuvorlik eng qat'iy qoidaga ega. Shu sababli, quyidagi konfiguratsiyada har qanday InstrumentNotFoundException bilan bog'liq bo'lgan tranzaktsiya orqaga qaytariladi:

<tx:advice id="txAdvice">
    <tx:attributes>
    <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
    </tx:attributes>
</tx:advice>

Siz, shuningdek, dasturiy ravishda majburiy orqaga qaytarishni belgilashingiz mumkin. Bu jarayon juda sodda bo'lishiga qaramay, juda tajovuzchidir va sizning kodingizni Spring Framework tranzaktsiya infratuzilmasiga qattiq bog'laydi. Keyingi misolda dasturiy ravishda majburiy orqaga qaytarishni qanday belgilash mumkinligi ko'rsatilgan:

Java
public void resolvePosition() {
    try {
        // biron bir biznes logikasi...
    } catch (NoProductInStockException ex) {
        // dasturiy ravishda orqaga qaytarishni boshlash
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
Kotlin
fun resolvePosition() {
    try {
        // biron bir biznes logikasi...
    } catch (ex: NoProductInStockException) {
        // dasturiy ravishda orqaga qaytarishni boshlash
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

Mumkin bo'lgan joylarda, orqaga qaytarish uchun deklarativ usuldan foydalanish qat'iyan tavsiya etiladi. Dasturiy orqaga qaytarish zarurat bo'lgan hollar uchun mavjud, lekin undan foydalanish POJO asosidagi toza arxitektura tamoyillariga zid keladi.

Har xil binglar uchun turli tranzaktsion semantikasini sozlash

Bir xizmat darajasidagi bir nechta ob'ektlar mavjudligini va ulardan har biriga turli tranzaktsion konfiguratsiyalarni qo'llashga muhtoj bo'lgan holatni o'ylab ko'ring. Buning uchun turli pointcut va advice-ref qiymatlari bilan alohida <aop:advisor/> elementlarini aniqlash mumkin.

Boshida, barcha xizmat darajadagi sinflar x.y.service asosiy paketida aniqlangan deb faraz qilaylik. Bu paketda (yoki pastki paketlarda) aniqlangan sinflarning namunalari bo'lgan (yoki Service bilan tugaydigan nomlari) namoyishlar tranzaktsion standart konfiguratsiyaga ega bo'lishi uchun quyidagi tarzda yozishingiz mumkin:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:config>
        <aop:pointcut id="serviceOperation"
                expression="execution(* x.y.service..*Service.*(..))"/>
        <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
    </aop:config>
    <!-- bu ikkita bing tranzaktsion bo'ladi... -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>
    <bean id="barService" class="x.y.service.extras.SimpleBarService"/>
    <!-- ... va bu ikkita bing tranzaktsion bo'lmaydi... -->
    <bean id="anotherService" class="org.xyz.SomeService"/> <!-- (to'g'ri paketda emas) -->
    <bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (Service bilan tugamas) -->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!-- boshqa tranzaktsion infratuzilma binglari, masalan TransactionManager, chyqib ketishgan... -->
</beans>

Keyingi misolda ikki turli binglarni, umuman turli tranzaktsion parametrlar bilan konfiguratsiya qilish ko'rsatadi:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:config>
        <aop:pointcut id="defaultServiceOperation"
                expression="execution(* x.y.service.*Service.*(..))"/>
        <aop:pointcut id="noTxServiceOperation"
                expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
        <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
        <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
    </aop:config>
    <!-- bu bing tranzaktsion bo'ladi ("defaultServiceOperation" nuqtasi qarating) -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>
    <!-- bu bing ham tranzaktsion bo'ladi, lekin butunlay boshqa tranzaktsion parametrlar bilan -->
    <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>
    <tx:advice id="defaultTxAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <tx:advice id="noTxAdvice">
        <tx:attributes>
            <tx:method name="*" propagation="NEVER"/>
        </tx:attributes>
    </tx:advice>
    <!-- boshqa tranzaktsion infratuzilma binglari, masalan TransactionManager, chyqib ketishgan... -->
</beans>

<tx:advice/> Parametrlari

Ushbu bo'limda <tx:advice/> tegidan foydalanish orqali belgilanishi mumkin bo'lgan turli tranzaktsion parametrlari tasvirlangan. <tx:advice/> uchun standart parametrlari quyidagicha:

  • Yayilish parametri REQUIRED.

  • Izolyatsiya darajasi - DEFAULT.

  • Tranzaktsiya "o'qish-yazish" modda bajariladi.

  • Tranzaktsiyaning kutish vaqti asosiy tizimning standart kutish vaqti yoki yo'q (agar kutish vaqti qiymatlari qo'llab-quvvatlanmasa) hisoblanadi.

  • Har qanday RuntimeException orqaga qaytarishga olib keladi, va har qanday tekshiriladigan Exception - yo'q.

Ushbu standart parametrlari o'zgartira olasiz. Keyingi jadvalda <tx:advice/> va <tx:attributes/> teglari ichiga kiritilgan <tx:method/> teglari turli atributlari ko'rsatilgan:

Jadval 1. <tx:method/> Parametrlari
Atribut Majburiy? Standart Tavsif

name

Ha

Tranzaktsion atributlar bilan bog'lanishi kerak bo'lgan metodlarning ismlari. Bir nechta metodlarga bir xil tranzaktsion parametrlari bilan bog'lash uchun qo'llab-quvvatlash belgisi (*) ishlatilishi mumkin (masalan, get*, handle*, on*Event va hokazo).

propagation

Yo'q

REQUIRED

Tranzaktsiyalarni yayilishida ishlash logikasi.

isolation

Yo'q

DEFAULT

Tranzaktsiyalarni izolyatsiya darajasi. Faqat REQUIRED yoki REQUIRES_NEW yayilish sozlamalari uchun qo'llaniladi.

timeout

Yo'q

-1

Tranzaktsiya kutish vaqti (soniyalarda). Faqat REQUIRED yoki REQUIRES_NEW yayilish uchun qo'llaniladi.

read-only

Yo'q

false

"O'qish-yazish" tranzaktsiyaga nisbatan faqat o'qish uchun tranzaktsiya. Faqat REQUIRED yoki REQUIRES_NEW ga qo'llaniladi.

rollback-for

Yo'q

Orqaga qaytarishga olib keladigan Exception instanceining ro'yxati, vergul bilan ajratilgan. Masalan, com.foo.MyBusinessException,ServletException.

no-rollback-for

Yo'q

Orqaga qaytarishga olib kelmaydigan Exception instanceining ro'yxati, vergul bilan ajratilgan. Masalan, com.foo.MyBusinessException,ServletException.

@Transactional anotatsiyasini ishlatish

XML asosidagi tranzaktsion konfiguratsiyasiga qo'shimcha, anotatsiya asosidagi yondashuvdan foydalanishingiz mumkin. Tranzaktsion semantikani bevosita Java manba kodida e'lon qilish, deklaratsiyalarni zararlangan kodga yaqinlashtiradi. Qo'shimcha darajada bog'lanish xavfi kamayadi, chunki tranzaktsiya uchun mo'ljallangan kod, aynan u shunday tarzda joylashtiriladi.

Standart javax.transaction.Transactional anotatsiyasi Spring-ning o'z anotatsiyasiga muqobil sifatida qo'llab-quvvatlanadi. Ko'proq ma'lumotni JTA 1.2 hujjatlarida topishingiz mumkin.

@Transactional anotatsiyasining sodda ishlatilishini eng yaxshi misol orqali tushuntirsa bo'ladi, bu matnda keyinroq izohlanadi. Quyidagi klassning ta'rifini ko'rib chiqing:

Java
// xizmat klassi, biz uni tranzaktsiyalashni xohlaymiz
@Transactional
public class DefaultFooService implements FooService {
    @Override
    public Foo getFoo(String fooName) {
        // ...
    }
    @Override
    public Foo getFoo(String fooName, String barName) {
        // ...
    }
    @Override
    public void insertFoo(Foo foo) {
        // ...
    }
    @Override
    public void updateFoo(Foo foo) {
        // ...
    }
}
Kotlin
// xizmat klassi, biz uni tranzaktsiyalashni xohlaymiz
@Transactional
class DefaultFooService : FooService {
    override fun getFoo(fooName: String): Foo {
        // ...
    }
    override fun getFoo(fooName: String, barName: String): Foo {
        // ...
    }
    override fun insertFoo(foo: Foo) {
        // ...
    }
    override fun updateFoo(foo: Foo) {
        // ...
    }
}

Yuqorida klass darajasidagi ishlatilgan anotatsiya e'lon qiluvchi klass (shuningdek, uning subklasslari)dagi barcha metodlar uchun asosiy qiymatni ko'rsatadi. Bundan tashqari, har bir metodni alohida anotatsiya qilish mumkin. Annotatsiya @Transactional bo'limi uchun, metod ko'rinishligi haqida ko'proq ma'lumot olish uchun qarang. Shuni ta'kidlash kerakki, klass darajasidagi anotatsiya yuqoridagi klasslar ierarxiyasida joylashgan o'z klasslar uchun qo'llanilmaydi; bunday holda, meros qilingan metodlar subklasslar darajasidagi annotatsiyada ishtirok etishi uchun lokal ravishda qayta e'lon qilinishi kerak.

Yuqoridagi POJO klass @Configuration anotatsiyaga ega bo'lgan klassda @EnableTransactionManagement

Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION