Every day we deal with operations that must be done completely or not at all. Think about a bank transfer: money must disappear from one account and appear in another. It can't get stuck halfway. In programming, such operations are called transactions.
Transaction as a basic unit of work
A transaction is a group of operations that must run as a single unit. Either all operations succeed (commit), or the system rolls everything back (rollback), as if nothing happened. It's like game save checkpoints: if something goes wrong, you can always go back to the last save.
For a transaction to be considered correct, it must have the following properties, grouped under the acronym ACID:
| Property | Description |
|---|---|
| Atomicity (Atomicity) | All operations inside the transaction run as a single unit. All or nothing |
| Consistency (Consistency) | After the transaction finishes, the system ends up in a valid state |
| Isolation (Isolation) | Transaction operations are isolated from concurrent transactions (no "interference") |
| Durability (Durability) | After a transaction completes, its results are preserved even if the system crashes |
Imagine a bank transfer. You want to send money from your account to a friend's account. The process can be split into these steps: 1. Decrease the amount on your account. 2. Increase the amount on your friend's account.
If a failure happens between these steps (for example, the connection to the bank drops), we risk either "losing" money or "creating" it out of thin air. To prevent that, both operations are wrapped in a transaction. If one of the steps fails, all changes are rolled back and the system returns to its original state.
Why transactions matter in apps
In dev work, transactions play a key role in keeping data correct, especially when apps talk to databases. Let's break down why you can't really live without them.
Ensuring data consistency
Back to the bank transfer example: imagine the bank server suddenly crashes after debiting your account. Without transactions your friend won't get the money, and you'll be out cash. Not a great look, right?
Transactions handle this cleanly: either the money is transferred, or it gets returned to your account — no stuck payments. This is critical for serious apps: e-commerce sites, booking systems, medical platforms.
Working in multi-user environments
Modern apps often face situations where multiple users try to change the same data:
- Two buyers both target the last pair of sneakers
- Multiple managers edit the same customer card in the CRM at the same time
Without transactions this can lead to real data chaos. Transactions act like a smart traffic controller — they keep operations from stepping on each other.
Problems transactions solve
Now let's look at the main problems transactions help solve.
Preventing partial changes
Imagine you're working on an order model in an online store. Creating an order might include these steps:
- Create a new order record in the database.
- Decrease the available product quantity in inventory.
- Send a notification to the user.
If a failure happens, say, on the second step, you'll end up with a "partially completed" state: the order exists in the system, but the product is still marked as available. That can cause big issues, especially in scalable systems. Transactions help avoid those scenarios.
Data integrity during failures
Failures are inevitable. For example:
- Something went wrong while saving data to the database.
- The network went down.
- The application threw an exception.
With a solid transactional system you can be sure that no "corrupted" changes end up in your database.
Eliminating conflicts during concurrent work
Another issue is when multiple processes or users try to modify the same data at the same time. For example:
- Employee A added a comment in the CRM.
- Employee B simultaneously deleted that same comment.
Who wins? Without isolation, the outcome can be unpredictable. But with proper transaction configuration, concurrent transactions are protected from interfering with each other.
Example: transactions in an online store
Let's build a simple example where transactions are used to process an order in an online store. Our scenario:
- Create a new order record.
- Decrease the product stock in inventory.
- If a failure happens at any step, roll back all changes.
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryService inventoryService;
@Transactional
public void processOrder(OrderRequest orderRequest) {
// Step 1: Creating a new order
Order order = new Order();
order.setProductId(orderRequest.getProductId());
order.setQuantity(orderRequest.getQuantity());
order.setStatus("PROCESSING");
orderRepository.save(order);
// Step 2: Reducing stock in inventory
inventoryService.reduceStock(orderRequest.getProductId(), orderRequest.getQuantity());
// Step 3: Update order status to "COMPLETED"
order.setStatus("COMPLETED");
orderRepository.save(order);
}
}
What's happening here?
- We mark the
processOrder()method as transactional using the@Transactionalannotation. That means all operations inside the method run in a single transaction. - If an error happens at any point (for example, the product is out of stock), the changes will be automatically rolled back and the database will stay consistent.
Error example and rollback
Let's add an "artificial" error to check that the transaction actually rolls back.
public void reduceStock(String productId, int quantity) {
int currentStock = inventoryRepository.getStock(productId);
if (currentStock < quantity) {
throw new RuntimeException("Insufficient stock for product: " + productId);
}
inventoryRepository.updateStock(productId, currentStock - quantity);
}
If there's not enough product, the reduceStock method will throw an exception and the whole transaction will be canceled: the order won't be saved, and the inventory won't be changed.
Real-world usage
In real projects, transactions are used almost everywhere:
- Banking operations (transfers, deposits, withdrawals).
- Orders and bookings (buying tickets, hotel reservations).
- Managing data in enterprise systems (CRM, ERP).
Why this matters for you — if you're asked in an interview how to handle a bank operation or protect data from conflicts, as an experienced Java developer with Spring you won't just talk about transactions, you'll show code with @Transactional.
Now we're ready to move on to the next step: managing transactions in Spring using the @Transactional annotation and other tools.
GO TO FULL VERSION