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); } }