innovationM
← Back to Blogs

Backend

Mastering Transaction Rollbacks in Java Backend Applications

InnovationM Admin 02 Jan 2025 3 min read
Mastering Transaction Rollbacks in Java Backend Applications

What Are Transactions?
A transaction is a sequence of operations performed as a single logical unit of work. To ensure the consistency of data, transactions adhere to the ACID properties:

  • Atomicity: All operations in a transaction succeed or none do.
  • Consistency: Transactions bring the database from one valid state to another.
  • Isolation: Transactions do not interfere with each other.
  • Durability: Once committed, changes are permanent.

    Handling Rollbacks in Java Backend Applications

    Rollback ensures that the system returns to its previous consistent state when a transaction fails. This section includes code samples and real-world examples demonstrating rollback management in Spring Boot.

    1. Basic Rollback Example
    Here’s how Spring’s @Transactional annotation automatically rolls back transactions when an exception occurs:

    Scenario: Bank Funds Transfer

    A user transfers money from one account to another. If the debit or credit operation fails, the entire transaction should rollback.

    @Service 
    
    public class BankService { 
    
    @Autowired 
    
    private AccountRepository accountRepository;
    
    @Transactional 
    
    public void transferFunds(Long fromAccountId, Long toAccountId, BigDecimal amount) { Account fromAccount = accountRepository.findById(fromAccountId) 
    
    .orElseThrow(() -> new IllegalArgumentException("Invalid sender account ID")); Account toAccount = accountRepository.findById(toAccountId) 
    
    .orElseThrow(() -> new IllegalArgumentException("Invalid receiver account ID")); 
    
    // Debit operation 
    
    fromAccount.debit(amount); 
    
    // Simulate an error during the credit operation 
    
    if (toAccountId == null) { 
    
    throw new IllegalStateException("Receiver account ID cannot be null"); } 
    
    // Credit operation 
    
    toAccount.credit(amount); 
    
    accountRepository.save(fromAccount); 
    
    accountRepository.save(toAccount); 
    
    } 
    
    }

    Explanation:
    ● If an exception (e.g., IllegalStateException) occurs, Spring automatically rolls back the transaction.
    ● Without rollback, one account might be debited without the other being credited, causing inconsistency.

    2. Rollback for Specific Exceptions

    Sometimes, you may want to rollback only for certain exceptions while committing for others. Use the rollbackFor attribute in @Transactional.

    Scenario: Order Placement with Inventory Check

    If the inventory is insufficient, rollback the transaction. For non-critical exceptions like logging failures, don’t rollback.

    @Service 
    
    public class OrderService { 
    
    @Autowired 
    
    private InventoryService inventoryService; 
    
    @Autowired 
    
    private OrderRepository orderRepository; 
    
    @Transactional(rollbackFor = {InventoryException.class}) 
    
    public void placeOrder(Order order) { 
    
    // Deduct inventory 
    
    inventoryService.reduceStock(order.getProductId(), order.getQuantity()); 
    
    // Save the order 
    
    orderRepository.save(order);
    
    // Simulate a logging failure 
    
    try { 
    
    logOrder(order); 
    
    } catch (LoggingException e) { 
    
    // Log the failure but don't rollback the transaction 
    
    System.err.println("Logging failed: " + e.getMessage()); 
    
    } 
    
    } 
    
    private void logOrder(Order order) throws LoggingException { 
    
    throw new LoggingException("Failed to log order details"); 
    
    } 
    
    }

    Explanation:
    ● rollbackFor ensures rollback for InventoryException but commits the transaction if a LoggingException occurs.

    3. Custom Rollback Scenarios

    For advanced cases, you might manually mark a transaction for rollback using TransactionAspectSupport.

    Scenario: Conditional Rollback During Payment Processing
    If the payment gateway fails, rollback the transaction manually.

    @Service 
    
    public class PaymentService { 
    
    @Autowired 
    
    private PaymentRepository paymentRepository; 
    
    @Transactional 
    
    public void processPayment(Payment payment) { 
    
    try { 
    
    // Save payment details 
    
    paymentRepository.save(payment); 
    
    // Simulate a payment gateway call 
    
    boolean paymentSuccess = callPaymentGateway(payment); 
    
    if (!paymentSuccess) { 
    
    // Mark for rollback 
    
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); System.err.println("Payment failed, rolling back transaction..."); } 
    
    } catch (Exception e) { 
    
    throw new RuntimeException("Error during payment processing", e); } 
    
    }
    
    private boolean callPaymentGateway(Payment payment) { 
    
    // Simulate a payment failure 
    
    return false; 
    
    } 
    
    }

    Explanation:
    ● TransactionAspectSupport.currentTransactionStatus().setRollbackOn ly() is used to mark the transaction for rollback explicitly.

    Custom Exceptions
    Here are the custom exceptions used in the examples:

    public class InventoryException extends Exception { 
    public InventoryException(String message) { 
    super(message); 
    } 
    } 
    public class LoggingException extends RuntimeException { 
    public LoggingException(String message) { 
    super(message); 
    } 
    }

About the Author

InnovationM Admin

Contributor at InnovationM.

LinkedIn

Transform Your Ideas with Expert Guidance

icon
15+ Years of Expertise

Delivering high-impact solutions with years of industry experience.

icon
100+ Satisfied Clients

Helping contact industry software experts to achieve their brand goals.

icon
250+ In-House Team Members

A skilled team ready to tackle projects of any scale.

Book a consultation call with our experts today