Transaction Management Deep

01.07.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Spring Boot | 28 daqiqa o'qish

Bu bo‘lim quyidagilarni qamrab oladi: Propagation levels, Isolation levels, Rollback rules, TransactionTemplate, va @Transactional proxy/self-call muammolari.


1. Transaction nima?

Transaction - bir nechta database operatsiyani bitta yaxlit ish sifatida bajarish mexanizmi.

Masalan pul o‘tkazish:

1. Ali hisobidan 100 000 so‘m ayirish
2. Vali hisobiga 100 000 so‘m qo‘shish

Bu ikkala amal ham bajarilishi kerak. Bittasi bajarilib, ikkinchisi xato bo‘lsa, sistema buziladi.

Shuning uchun transaction kerak:

Hammasi bajarildi  -> COMMIT
Xato bo‘ldi        -> ROLLBACK

2. Transactionning asosiy maqsadi: ACID

Transaction odatda ACID prinsipiga tayanadi.

Prinsip

Ma’nosi

Atomicity

Hammasi bajariladi yoki hech biri bajarilmaydi

Consistency

Database noto‘g‘ri holatga tushib qolmaydi

Isolation

Bir transaction boshqasiga noto‘g‘ri ta’sir qilmaydi

Durability

Commit bo‘lgan data saqlanib qoladi

Masalan:

@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    accountRepository.decreaseBalance(fromId, amount);
    accountRepository.increaseBalance(toId, amount);
}

Agar increaseBalance() xato bersa, decreaseBalance() ham rollback bo‘ladi.


3. Spring’da transaction qanday ishlaydi?

Spring’da eng ko‘p ishlatiladigan usul:

@Transactional
public void createOrder() {
    // DB operations
}

Lekin ichkarida Spring taxminan shunday qiladi:

try {
    transactionManager.begin();

    createOrder();

    transactionManager.commit();
} catch (Exception e) {
    transactionManager.rollback();
    throw e;
}

Siz faqat annotation yozasiz. Spring esa transaction ochish, commit qilish, rollback qilishni boshqaradi.


4. @Transactional qayerga qo‘yiladi?

Eng yaxshi joy - service layer.

@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;

    public OrderService(
            OrderRepository orderRepository,
            PaymentRepository paymentRepository
    ) {
        this.orderRepository = orderRepository;
        this.paymentRepository = paymentRepository;
    }

    @Transactional
    public void createOrder(CreateOrderRequest request) {
        Order order = new Order();
        orderRepository.save(order);

        Payment payment = new Payment();
        payment.setOrder(order);
        paymentRepository.save(payment);
    }
}

Nega controllerga emas?

@RestController
public class OrderController {

    @Transactional // yomon amaliyot
    @PostMapping("/orders")
    public void create() {
    }
}

Controller HTTP bilan ishlashi kerak. Transaction esa biznes operatsiya chegarasida bo‘lishi kerak.

To‘g‘ri chegara:

Controller -> Service transaction -> Repository

5. @Transactional class level vs method level

Method level

@Service
public class UserService {

    @Transactional
    public void createUser() {
    }

    public User getUser() {
    }
}

Faqat createUser() transaction bilan ishlaydi.

Class level

@Service
@Transactional
public class UserService {

    public void createUser() {
    }

    public void updateUser() {
    }

    public User getUser() {
    }
}

Bu classdagi public methodlarning hammasiga transaction qo‘llanadi.

Lekin o‘qish methodlari uchun alohida belgilash yaxshi:

@Transactional(readOnly = true)
public User getUser(Long id) {
    return userRepository.findById(id).orElseThrow();
}

6. readOnly = true nima qiladi?

@Transactional(readOnly = true)
public List<User> getUsers() {
    return userRepository.findAll();
}

Bu Spring va ORMga signal beradi:

"Bu transaction ichida data o‘zgarmaydi, faqat o‘qiladi."

Foydasi:

  • Hibernate dirty checkingni kamaytirishi mumkin;

  • ba’zi DB optimizatsiyalar ishlashi mumkin;

  • kod niyatini aniq ko‘rsatadi.

Lekin bu har doim "write qilishni fizik bloklaydi" degani emas. Ba’zi holatda DB yoki ORM baribir yozishga ruxsat berishi mumkin. Shuning uchun readOnly = trueni xavfsizlik mexanizmi deb emas, optimizatsiya va niyat belgisi deb tushunish kerak.


7. Rollback qoidalari

Spring’da default qoida:

Exception turi

Rollback bo‘ladimi?

RuntimeException

Ha

Error

Ha

Checked exception

Yo‘q

RuntimeException rollback qiladi

@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    throw new RuntimeException("Something went wrong");
}

Bu holatda save() rollback bo‘ladi.


Checked exception default rollback qilmaydi

@Transactional
public void createOrder() throws IOException {
    orderRepository.save(new Order());

    throw new IOException("File error");
}

Bu holatda default bo‘yicha rollback bo‘lmasligi mumkin, chunki IOException - checked exception.

Rollback kerak bo‘lsa:

@Transactional(rollbackFor = IOException.class)
public void createOrder() throws IOException {
    orderRepository.save(new Order());

    throw new IOException("File error");
}

8. rollbackFor va noRollbackFor

rollbackFor

@Transactional(rollbackFor = Exception.class)
public void importUsers() throws Exception {
    userRepository.save(new User());

    throw new Exception("Import failed");
}

Bu checked exception uchun ham rollback qiladi.

noRollbackFor

Ba’zan exception bo‘lsa ham rollback qilmaslik kerak bo‘ladi.

@Transactional(noRollbackFor = NotificationException.class)
public void createOrder() {
    orderRepository.save(new Order());

    try {
        notificationService.sendSms();
    } catch (NotificationException e) {
        throw e;
    }
}

Lekin bunday kodni ehtiyotkorlik bilan yozish kerak. Ko‘pincha notificationni transaction ichida qilmaslik yaxshiroq.


9. Eng katta xato: exceptionni yutib yuborish

Yomon:

@Transactional
public void createOrder() {
    try {
        orderRepository.save(new Order());
        paymentRepository.save(new Payment());
    } catch (Exception e) {
        log.error("Error", e);
    }
}

Bu yerda exception tashqariga chiqmaydi. Spring transactionni muvaffaqiyatli deb o‘ylaydi va commit qiladi.

To‘g‘ri:

@Transactional
public void createOrder() {
    try {
        orderRepository.save(new Order());
        paymentRepository.save(new Payment());
    } catch (Exception e) {
        log.error("Error", e);
        throw e;
    }
}

Yoki custom exception:

@Transactional
public void createOrder() {
    try {
        orderRepository.save(new Order());
        paymentRepository.save(new Payment());
    } catch (Exception e) {
        throw new OrderCreationException("Order creation failed", e);
    }
}

10. Propagation nima?

Propagation - agar bir transaction ichida boshqa transactionli method chaqirilsa, Spring nima qilishi kerakligini bildiradi.

Masalan:

@Transactional
public void createOrder() {
    saveOrder();
    saveAudit();
}

Agar saveAudit() ham @Transactional bo‘lsa, u yangi transaction ochadimi yoki mavjudiga qo‘shiladimi?

Buni propagation belgilaydi.


11. Propagation.REQUIRED

Default propagation - REQUIRED.

@Transactional(propagation = Propagation.REQUIRED)
public void saveOrder() {
}

Ma’nosi:

Agar transaction bor bo‘lsa -> unga qo‘shil
Agar transaction yo‘q bo‘lsa -> yangi och

Bu eng ko‘p ishlatiladigan holat.

Misol:

@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        orderRepository.save(new Order());
        auditService.saveAudit();
    }
}
@Service
public class AuditService {

    @Transactional
    public void saveAudit() {
        auditRepository.save(new AuditLog());
    }
}

Bu yerda saveAudit() default REQUIRED, demak createOrder() transactioniga qo‘shiladi.

Agar createOrder() rollback bo‘lsa, audit ham rollback bo‘ladi.


12. Propagation.REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAudit() {
}

Ma’nosi:

Har doim yangi transaction och
Agar eski transaction bo‘lsa, vaqtincha to‘xtat

Real misol: order xato bo‘lsa ham audit saqlansin.

@Service
public class OrderService {

    @Transactional
    public void createOrder() {
        try {
            orderRepository.save(new Order());

            throw new RuntimeException("Order failed");
        } catch (Exception e) {
            auditService.saveAudit("ORDER_FAILED");
            throw e;
        }
    }
}
@Service
public class AuditService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveAudit(String message) {
        auditRepository.save(new AuditLog(message));
    }
}

Natija:

Order -> rollback
Audit -> commit

Bu productionda juda foydali.

Lekin haddan tashqari ishlatilsa:

  • connection pool bosimi oshadi;

  • transaction oqimi murakkablashadi;

  • debugging qiyinlashadi.


13. Propagation.NESTED

@Transactional(propagation = Propagation.NESTED)
public void saveChildData() {
}

Ma’nosi:

Mavjud transaction ichida savepoint yaratadi
Ichki qism rollback bo‘lsa, butun transaction emas, shu qism rollback bo‘lishi mumkin

Tasavvur:

Outer transaction start
  save order
  savepoint
    save optional details
    error
  rollback to savepoint
  continue outer transaction
commit outer transaction

Misol:

@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    try {
        optionalService.saveOptionalDetails();
    } catch (Exception e) {
        log.warn("Optional details failed");
    }
}
@Transactional(propagation = Propagation.NESTED)
public void saveOptionalDetails() {
    optionalRepository.save(new OptionalDetails());

    throw new RuntimeException("Optional failed");
}

NESTED hamma transaction managerlarda bir xil ishlamasligi mumkin. Odatda JDBC savepoint qo‘llab-quvvatlashi kerak.


14. Boshqa propagation turlari

Propagation

Ma’nosi

REQUIRED

Bor transactionga qo‘shiladi, bo‘lmasa yangi ochadi

REQUIRES_NEW

Har doim yangi transaction ochadi

NESTED

Existing transaction ichida savepoint ishlatadi

SUPPORTS

Transaction bo‘lsa ishlatadi, bo‘lmasa transactionsiz ishlaydi

NOT_SUPPORTED

Transactionsiz ishlaydi, bor transactionni suspend qiladi

MANDATORY

Transaction bo‘lishi shart, bo‘lmasa xato

NEVER

Transaction bo‘lmasligi kerak, bo‘lsa xato

Eng ko‘p ishlatiladi:

REQUIRED
REQUIRES_NEW
NESTED

15. Isolation nima?

Isolation - parallel transactionlar bir-birini qanday ko‘rishini belgilaydi.

Bir vaqtning o‘zida ikkita request keldi:

Transaction A: user balansini o‘qiyapti
Transaction B: shu balansni o‘zgartiryapti

Savol:

A transaction B hali commit qilmagan datani ko‘rishi mumkinmi?

Buni isolation level hal qiladi.


16. Isolation muammolari

16.1 Dirty Read

Bir transaction boshqa transactionning hali commit qilinmagan datasini o‘qiydi.

Transaction B: balance = 100 dan 50 qildi, lekin commit qilmadi
Transaction A: balance = 50 deb o‘qidi
Transaction B: rollback qildi

A noto‘g‘ri data o‘qidi.


16.2 Non-repeatable Read

Bir transaction bir xil rowni ikki marta o‘qiydi, lekin natija o‘zgarib qoladi.

Transaction A: balance = 100 o‘qidi
Transaction B: balance = 200 qilib commit qildi
Transaction A: yana o‘qidi -> balance = 200

Bitta transaction ichida bir xil row har xil chiqdi.


16.3 Phantom Read

Bir transaction bir xil queryni ikki marta ishlatadi, lekin ikkinchi safar yangi rowlar paydo bo‘ladi.

Transaction A: SELECT * FROM orders WHERE status = 'NEW' -> 10 ta
Transaction B: yangi NEW order qo‘shdi, commit qildi
Transaction A: yana query qildi -> 11 ta

17. Isolation levellar

Isolation

Dirty Read

Non-repeatable Read

Phantom Read

READ_UNCOMMITTED

Mumkin

Mumkin

Mumkin

READ_COMMITTED

Yo‘q

Mumkin

Mumkin

REPEATABLE_READ

Yo‘q

Yo‘q

DBga bog‘liq

SERIALIZABLE

Yo‘q

Yo‘q

Yo‘q


18. READ_COMMITTED

@Transactional(isolation = Isolation.READ_COMMITTED)
public void process() {
}

Ma’nosi:

Faqat commit qilingan datani o‘qiydi.

Ko‘p database’larda default darajaga yaqin. Masalan, PostgreSQL default isolation - READ COMMITTED.

Bu odatda ko‘p business applicationlar uchun yetarli.


19. REPEATABLE_READ

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void process() {
}

Ma’nosi:

Bitta transaction ichida o‘qilgan row qayta o‘qilganda o‘zgarmas bo‘lib ko‘rinadi.

Bu inventory, balance, stock kabi joylarda foydali bo‘lishi mumkin.

Lekin har doim ham race conditionni to‘liq hal qilmaydi. Ba’zan explicit lock yoki optimistic locking kerak bo‘ladi.


20. SERIALIZABLE

@Transactional(isolation = Isolation.SERIALIZABLE)
public void process() {
}

Eng kuchli isolation.

Ma’nosi:

Transactionlar xuddi ketma-ket bajarilgandek natija beradi.

Kamchiligi:

  • sekinroq;

  • locklar ko‘payadi;

  • deadlock ehtimoli oshadi;

  • throughput pasayadi.

Shuning uchun uni default qilib qo‘yish noto‘g‘ri.


21. Real muammo: lost update

Ikki user bir vaqtda bitta mahsulot stockini kamaytiryapti.

Initial stock = 10

Transaction A: stock = 10 o‘qidi
Transaction B: stock = 10 o‘qidi

A: stock = 9 saqladi
B: stock = 9 saqladi

Aslida stock 8 bo‘lishi kerak edi, lekin 9 bo‘lib qoldi.

Bu lost update.

Yechimlar:

  • optimistic locking;

  • pessimistic locking;

  • atomic update query;

  • yuqoriroq isolation;

  • business constraintlar.


22. Optimistic locking

Entityga version qo‘shiladi:

@Entity
public class Product {

    @Id
    private Long id;

    private int stock;

    @Version
    private Long version;
}

Agar ikki transaction bitta rowni update qilsa, bittasi muvaffaqiyatli bo‘ladi, ikkinchisi OptimisticLockException oladi.

Bu high-read, low-conflict systemlarda yaxshi.


23. Pessimistic locking

Rowni lock qilib qo‘yish.

public interface ProductRepository extends JpaRepository<Product, Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("select p from Product p where p.id = :id")
    Optional<Product> findByIdForUpdate(Long id);
}

Service:

@Transactional
public void decreaseStock(Long productId) {
    Product product = productRepository.findByIdForUpdate(productId)
            .orElseThrow();

    product.decreaseStock();
}

Bu holatda boshqa transaction shu rowni yozish uchun kutadi.

Kamchiligi:

  • lock kutish;

  • deadlock;

  • throughput pasayishi.


24. Atomic update query

Ko‘p holatda eng sodda va kuchli yechim:

@Modifying
@Query("""
    update Product p
    set p.stock = p.stock - :amount
    where p.id = :id and p.stock >= :amount
""")
int decreaseStock(Long id, int amount);

Service:

@Transactional
public void buy(Long productId, int amount) {
    int updated = productRepository.decreaseStock(productId, amount);

    if (updated == 0) {
        throw new IllegalStateException("Not enough stock");
    }
}

Bu read -> modify -> write muammosini kamaytiradi.


25. Transaction va flush

JPA/Hibernate’da save() chaqirilganda SQL darrov DBga ketmasligi mumkin.

@Transactional
public void createUser() {
    userRepository.save(new User("Ali"));

    System.out.println("Saved");
}

Hibernate SQLni odatda:

  • flush paytida;

  • querydan oldin;

  • transaction commit oldidan;

yuboradi.

Majburiy flush:

userRepository.saveAndFlush(user);

Yoki:

entityManager.flush();

Lekin flush() commit degani emas. Flush SQLni DBga yuboradi, transaction hali rollback bo‘lishi mumkin.


26. Transaction va Lazy loading

@Transactional(readOnly = true)
public Order getOrder(Long id) {
    Order order = orderRepository.findById(id).orElseThrow();
    order.getItems().size();
    return order;
}

items LAZY bo‘lsa, transaction ochiq paytda yuklanadi.

Lekin transactiondan tashqarida:

Order order = orderService.getOrder(id);
order.getItems().size(); // LazyInitializationException

Bu muammo chiqishi mumkin.

Yechimlar:

  • DTOga map qilish transaction ichida;

  • fetch join;

  • entity graph;

  • queryni to‘g‘ri yozish;

  • Open Session in View’ga suyanmaslik.


27. @Transactional va self-invocation

Bu juda muhim.

@Service
public class UserService {

    public void register() {
        saveUser();
    }

    @Transactional
    public void saveUser() {
        userRepository.save(new User());
    }
}

Bu yerda register() ichidan saveUser() chaqirilsa, transaction ishlamasligi mumkin.

Sabab:

this.saveUser()

proxy orqali o‘tmaydi.

Tashqaridan chaqirilsa:

Controller -> UserService Proxy -> saveUser()

ishlaydi.

Ichkaridan chaqirilsa:

UserService object -> this.saveUser()

proxy chetlab o‘tiladi.


28. Self-invocation yechimi

Eng toza yechim - methodlarni boshqa servicega ajratish.

@Service
public class UserRegistrationService {

    private final UserPersistenceService userPersistenceService;

    public UserRegistrationService(UserPersistenceService userPersistenceService) {
        this.userPersistenceService = userPersistenceService;
    }

    public void register() {
        userPersistenceService.saveUser();
    }
}
@Service
public class UserPersistenceService {

    @Transactional
    public void saveUser() {
        userRepository.save(new User());
    }
}

Bu yerda chaqiruv proxy orqali o‘tadi.


29. Private methodda @Transactional

Ishlamaydi.

@Transactional
private void saveUser() {
}

Spring AOP proxy private methodni intercept qila olmaydi.

Odatda transaction methodlari public bo‘lishi kerak.


30. Final method/class muammosi

CGLIB proxy final methodni override qila olmaydi.

Yomon:

@Service
public final class PaymentService {

    @Transactional
    public void pay() {
    }
}

Yoki:

@Service
public class PaymentService {

    @Transactional
    public final void pay() {
    }
}

Bunday holatlarda transaction ishlamasligi yoki kutilmagan muammo berishi mumkin.


31. Multiple transaction manager

Ba’zan projectda bir nechta database bo‘ladi.

@Transactional(transactionManager = "orderTransactionManager")
public void createOrder() {
}

Yoki:

@Transactional(transactionManager = "paymentTransactionManager")
public void createPayment() {
}

Agar noto‘g‘ri transaction manager tanlansa, transaction kutganingizdek ishlamaydi.

Distributed transaction esa alohida murakkab mavzu. Ko‘p microservice architecture’da 2PC o‘rniga:

  • Saga;

  • transactional outbox;

  • idempotency;

  • event-driven consistency;

ishlatiladi.


32. Programmatic transaction: TransactionTemplate

Ba’zan annotation yetarli bo‘lmaydi. Transaction chegarasini kod ichida aniq boshqarish kerak bo‘ladi.

@Service
public class OrderService {

    private final TransactionTemplate transactionTemplate;

    public OrderService(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    public void createOrder() {
        transactionTemplate.execute(status -> {
            orderRepository.save(new Order());
            paymentRepository.save(new Payment());
            return null;
        });
    }
}

Bu programmatic transaction deyiladi.


33. TransactionTemplate qachon kerak?

Masalan, bitta method ichida:

1. DB transactionda order yaratish
2. Transactiondan tashqarida external API chaqirish
3. Yana alohida transactionda audit yozish

Annotation bilan bu ko‘pincha noqulay.

public void processOrder() {
    Long orderId = transactionTemplate.execute(status -> {
        Order order = orderRepository.save(new Order());
        return order.getId();
    });

    externalPaymentClient.charge(orderId);

    transactionTemplate.execute(status -> {
        auditRepository.save(new AuditLog("PAYMENT_CHARGED"));
        return null;
    });
}

Bu yerda transaction chegaralari aniq ko‘rinib turibdi.


34. Transaction ichida external API chaqirish xatosi

Yomon:

@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    paymentClient.charge(); // external API

    orderRepository.updateStatus();
}

Nega yomon?

  • DB connection uzoq vaqt band bo‘ladi;

  • external API sekin bo‘lsa transaction cho‘ziladi;

  • timeout bo‘lishi mumkin;

  • locklar uzoq ushlanadi;

  • rollback external API’ni ortga qaytara olmaydi.

Yaxshiroq yondashuv:

1. Order CREATED holatda saqlanadi
2. Payment event yuboriladi
3. Payment natijasiga qarab status update bo‘ladi

Yoki transactional outbox pattern.


35. Transactional outbox qisqacha

Muammo:

DBga order saqlandi
Kafka/RabbitMQga event yuborish kerak

Agar DB commit bo‘ldi, lekin event yuborilmadi - data mismatch.

Outbox yechim:

Bitta DB transaction ichida:
1. Order save
2. Outbox event save

Keyin alohida worker:
1. Outbox eventlarni o‘qiydi
2. Kafka/RabbitMQga yuboradi
3. sent deb belgilaydi

Service:

@Transactional
public void createOrder() {
    Order order = orderRepository.save(new Order());

    outboxRepository.save(new OutboxEvent(
            "ORDER_CREATED",
            order.getId()
    ));
}

Bu microservice architecture’da juda kerakli pattern.


36. @Transactional va async

Ehtiyot bo‘lish kerak:

@Transactional
public void createOrder() {
    orderRepository.save(new Order());

    asyncService.sendNotification();
}
@Async
public void sendNotification() {
}

@Async boshqa threadda ishlaydi. Transaction context avtomatik ravishda boshqa threadga o‘tmaydi.

Shuning uchun async method ichida lazy entity, transaction context yoki sessionga suyanish xato.

Yaxshi yondashuv:

@Transactional
public void createOrder() {
    Order order = orderRepository.save(new Order());

    eventPublisher.publishEvent(new OrderCreatedEvent(order.getId()));
}

Keyin transaction commit bo‘lgandan keyin event ishlatish:

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderCreatedEvent event) {
    notificationService.send(event.orderId());
}

37. @TransactionalEventListener

Bu juda foydali.

@Transactional
public void createOrder() {
    Order order = orderRepository.save(new Order());

    applicationEventPublisher.publishEvent(
            new OrderCreatedEvent(order.getId())
    );
}

Listener:

@Component
public class OrderEventListener {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void afterCommit(OrderCreatedEvent event) {
        notificationService.sendOrderCreated(event.orderId());
    }
}

Bu listener faqat transaction commit bo‘lgandan keyin ishlaydi.

Fazalar:

Phase

Qachon ishlaydi?

BEFORE_COMMIT

Commitdan oldin

AFTER_COMMIT

Commitdan keyin

AFTER_ROLLBACK

Rollbackdan keyin

AFTER_COMPLETION

Commit yoki rollbackdan keyin


38. Transaction timeout

@Transactional(timeout = 5)
public void process() {
    // 5 sekunddan oshmasligi kerak
}

Agar transaction juda cho‘zilib ketsa, timeout bo‘lishi mumkin.

Bu ayniqsa:

  • batch processing;

  • external dependency;

  • lock kutish;

  • katta querylar;

uchun muhim.


39. Transaction ichida nima qilmaslik kerak?

Transaction ichida quyidagilarni minimal qiling:

Qilmaslik kerak

Sabab

External API call

Sekin, rollback qilib bo‘lmaydi

File upload/download

DB connection band bo‘ladi

Email/SMS yuborish

Commit bo‘lmasdan xabar ketib qolishi mumkin

Katta loop ichida ko‘p save

Batch kerak

User input kutish

Transaction juda cho‘ziladi

Keraksiz querylar

Lock va connection bosimi oshadi


40. Best practice

1. Transactionni service layerda saqlang

Controller -> Service @Transactional -> Repository

2. Transactionni qisqa tuting

Faqat DB consistency uchun kerakli kod transaction ichida bo‘lsin.

3. External API’ni transaction tashqarisiga chiqaring

Ayniqsa payment, SMS, email, file storage.

4. Exceptionni yutmang

Rollback kerak bo‘lsa exception tashqariga chiqsin.

5. readOnly = true ishlating

Read-only query methodlarda niyatni aniq ko‘rsating.

6. Self-invocationdan ehtiyot bo‘ling

this.method() transactionni chetlab o‘tishi mumkin.

7. Propagationni tushunmasdan o‘zgartirmang

REQUIRES_NEW kuchli, lekin noto‘g‘ri ishlatilsa muammo ko‘paytiradi.

8. Isolationni defaultdan oshirishdan oldin o‘ylang

Ko‘pincha muammo isolation bilan emas, locking/modeling bilan hal qilinadi.


41. Interview savollar

Savol 1: @Transactional qanday ishlaydi?

Javob:

Spring @Transactional method atrofida proxy yaratadi. Method chaqirilganda proxy transaction ochadi, method muvaffaqiyatli tugasa commit qiladi, runtime exception bo‘lsa rollback qiladi.


Savol 2: Checked exception rollback qiladimi?

Javob:

Default holatda yo‘q. Spring runtime exception va errorlarda rollback qiladi. Checked exception uchun rollbackFor berish kerak.

@Transactional(rollbackFor = IOException.class)

Savol 3: REQUIRED va REQUIRES_NEW farqi nima?

Javob:

REQUIRED mavjud transactionga qo‘shiladi, yo‘q bo‘lsa yangi ochadi. REQUIRES_NEW esa har doim yangi transaction ochadi va mavjud transactionni vaqtincha suspend qiladi.


Savol 4: Isolation level nima?

Javob:

Isolation parallel transactionlar bir-birining o‘zgarishlarini qanday ko‘rishini belgilaydi. Masalan dirty read, non-repeatable read, phantom read kabi muammolarni boshqaradi.


Savol 5: Nega transaction ichida external API chaqirish yomon?

Javob:

Chunki transaction uzoq ochiq qoladi, DB connection va locklar band bo‘ladi. External API sekin yoki xato bo‘lishi mumkin. Bundan tashqari DB rollback bo‘lsa ham external API chaqiruvini rollback qilib bo‘lmaydi.


Savol 6: Self-invocation transactionga qanday ta’sir qiladi?

Javob:

Agar @Transactional method shu class ichidan this.method() orqali chaqirilsa, chaqiruv Spring proxy orqali o‘tmaydi. Shuning uchun transaction ishlamasligi mumkin.


42. Qisqa xulosa

Transaction management - faqat @Transactional qo‘yish emas.

Developer quyidagilarni tushunishi kerak:

Mavzu

Muhim xulosa

@Transactional

Proxy orqali ishlaydi

Rollback

Runtime exception rollback qiladi, checked exception default rollback qilmaydi

Propagation

Ichma-ich transactionlar qanday ishlashini belgilaydi

Isolation

Parallel transaction muammolarini boshqaradi

Locking

Lost update kabi muammolar uchun kerak

TransactionTemplate

Transaction chegarasini kod bilan boshqaradi

External API

Transaction ichida chaqirish xavfli

Self-invocation

Proxy chetlab o‘tilsa transaction ishlamaydi

Eng muhim gap:

Transaction qisqa, aniq va service layer darajasida bo‘lishi kerak. Transaction ichida faqat database consistency uchun zarur ishlar bajarilishi kerak.