I'm always excited to take on new projects and collaborate with innovative minds.

Email

contact@niteshsynergy.com

Website

https://www.niteshsynergy.com/

The ACID properties

The ACID properties (Atomicity, Consistency, Isolation, Durability) are fundamental principles in database management systems (DBMS) to ensure that transactions are processed reliably. Here's a real-time example for each property, along with a use case:

1. Atomicity (ROLLBACK)

  • Definition: A transaction is treated as a single unit, which either fully succeeds or fully fails. If a transaction fails at any point, all changes made during the transaction are rolled back, ensuring the database remains in a consistent state.

Use Case: Banking Transfer

  • Imagine you are transferring money from one account to another. The transaction involves two steps:
    1. Deduct the amount from the sender’s account.
    2. Add the amount to the recipient’s account.
  • If the second step fails after the deduction, the first step (deducting from the sender’s account) should be rolled back to ensure no money is lost.
  • Example: The transaction begins, but the bank's system crashes before the recipient’s account is credited. The system automatically rolls back the transaction to ensure the sender's account is restored.

2. Consistency

  • Definition: A transaction brings the database from one valid state to another, ensuring that the database constraints, rules, and integrity are maintained before and after the transaction.
  • Use Case: E-Commerce Inventory Management
    • When a customer places an order on an e-commerce platform, the system must ensure that the quantity of the item purchased is updated correctly in the database.
    • Example: If a customer buys the last 3 units of a product, the system checks that the inventory count is updated to reflect this after the transaction. If the transaction completes, the database will not allow a negative stock quantity or allow more items to be sold than available.

3. Isolation

  • Definition: Transactions are executed in isolation from one another, meaning the intermediate state of one transaction is not visible to other transactions. This ensures that transactions do not interfere with each other.
  • Use Case: Online Reservation System
    • When two users are booking the last available seat on a flight, the system ensures that they do not both "see" the seat as available at the same time.
    • Example: User 1 begins the booking process, and before the transaction is complete, User 2 tries to book the same seat. The system isolates the two transactions to ensure that only one user can successfully complete the booking while the other will receive an error stating that the seat is no longer available.

4. Durability (COMMIT)

  • Definition: Once a transaction has been committed, the changes are permanent, even in the event of a system crash. The database ensures that all changes made by the transaction are saved and will persist.
  • Use Case: Order Processing System
    • Once a customer places an order, the system confirms the order and records it in the database. After the order is confirmed, no matter what happens (e.g., power failure), the order data is saved.
    • Example: A customer successfully places an order for a product, and the system commits the transaction. If the server crashes immediately afterward, the order is still intact and retrievable when the system comes back online.

In summary:

  • Atomicity ensures that transactions are all-or-nothing.
  • Consistency guarantees that the database remains in a valid state.
  • Isolation makes sure transactions do not interfere with each other.
  • Durability ensures that committed transactions are permanent.

These properties are crucial for ensuring reliable and correct database transactions in systems like banking, e-commerce, reservation platforms, and more.

 

Here’s how you can achieve each of the ACID properties in Java with examples for each:

 

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class BankService {

   private final AccountRepository accountRepository;

   public BankService(AccountRepository accountRepository) {
       this.accountRepository = accountRepository;
   }

   @Transactional
   public void transferFunds(Long senderId, Long receiverId, double amount) {
       // Deduct from sender's account
       Account sender = accountRepository.findById(senderId).orElseThrow(() -> new RuntimeException("Sender not found"));
       sender.setBalance(sender.getBalance() - amount);
       accountRepository.save(sender);

       // Simulate an error: Uncomment the line below to trigger rollback
       if (amount > 1000) throw new RuntimeException("Simulated error");

       // Add to receiver's account
       Account receiver = accountRepository.findById(receiverId).orElseThrow(() -> new RuntimeException("Receiver not found"));
       receiver.setBalance(receiver.getBalance() + amount);
       accountRepository.save(receiver);
   }
}
 

@Transactional ensures that if an exception occurs, the whole transaction will be rolled back, ensuring atomicity.

 

2. Consistency

Consistency ensures that the database constraints and business logic are always maintained.

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class InventoryService {

   private final ProductRepository productRepository;

   public InventoryService(ProductRepository productRepository) {
       this.productRepository = productRepository;
   }

   @Transactional
   public void purchaseProduct(Long productId, int quantity) {
       Product product = productRepository.findById(productId)
               .orElseThrow(() -> new RuntimeException("Product not found"));

       if (product.getStock() < quantity) {
           throw new RuntimeException("Insufficient stock available");
       }

       product.setStock(product.getStock() - quantity);
       productRepository.save(product);
   }
}
 

The purchaseProduct method ensures that if there is insufficient stock, a runtime exception is thrown, maintaining consistency. The @Transactional annotation will ensure that any operation is atomic and consistent.

 

3. Isolation

Isolation ensures that one transaction is not affected by another transaction, which is critical in multi-user environments.

Example: Isolation with Spring Boot

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ReservationService {

   private final ReservationRepository reservationRepository;

   public ReservationService(ReservationRepository reservationRepository) {
       this.reservationRepository = reservationRepository;
   }

   @Transactional
   public void bookSeat(Long seatId, Long customerId) {
       // Fetch seat and customer details
       Seat seat = reservationRepository.findSeatById(seatId);
       if (seat.isBooked()) {
           throw new RuntimeException("Seat is already booked");
       }
       
       seat.setBooked(true);
       reservationRepository.save(seat);
       
       // Log customer booking
       reservationRepository.saveBookingLog(customerId, seatId);
   }
}
@Transactional ensures that while the seat is being booked, the operation is isolated from other transactions that might also be booking seats simultaneously.

 

4. Durability (COMMIT)

Once a transaction is committed, it should be saved permanently in the database even if the system crashes afterward.

Example: Durability with Spring Boot

 

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

   private final OrderRepository orderRepository;

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

   @Transactional
   public void placeOrder(Long customerId, List<Long> productIds) {
       // Place the order and save to database
       Order order = new Order();
       order.setCustomerId(customerId);
       order.setProductIds(productIds);
       order.setStatus("PLACED");
       
       orderRepository.save(order);
       
       // After the commit, this data should persist even if the system crashes
   }
}
 

With @Transactional, the changes made to the database will persist even if the system crashes after the commit. Once the transaction is committed, it’s durable.

 

Key Notes:

  • Atomicity is achieved by ensuring the transaction is all or nothing.
  • Consistency ensures the database's rules and integrity are respected.
  • Isolation is managed through transactions to avoid conflicts between concurrent transactions.
  • Durability ensures that once committed, the data persists even after crashes, which can be implemented by using JPA or Spring Data repositories.

In real-time enterprise applications, these concepts are implemented using frameworks like Spring Transaction Management or JDBC (for lower-level control), making the system robust and reliable.

7 min read
Nov 19, 2024
By Nitesh Synergy
Share