Clean • Professional
A transaction is a unit of work that is executed atomically, meaning either all operations succeed or none are applied. Spring provides a declarative and programmatic transaction management abstraction for Java applications.
A transaction is a sequence of one or more database operations executed as a single unit of work.
It guarantees that:
In simple words:
A transaction ensures multiple database operations are executed atomically using commit and rollback mechanisms, maintaining data integrity.
Example: Transferring money between accounts must debit one account and credit another atomically.

Example: Bank Transfer
1. Debit ₹10,000 from Account A
2. Credit ₹10,000 to Account B
Example: Order Placement
Initial State:
Stock = 10
Transaction:
Order quantity = 3
New stock = 7
-1 → transaction failsExample: Two Users Booking Last Ticket
Transaction T1: checks available seats → 1
Transaction T2: checks available seats → 1
Without isolation:
With isolation:
Example: Payment Success
Payment marked as SUCCESS
Transaction committed
System crashes immediately
After restart:
Payment status is still SUCCESS
Durability ensures:
Spring provides declarative and programmatic transaction management.

1. Declarative Transactions
@Transactional annotation.Example:
@Service
publicclassUserService {
@Autowired
private UserRepository userRepository;
@Transactional
publicvoidcreateUserAndOrders(User user, List<Order> orders) {
userRepository.save(user);
orders.forEach(orderRepository::save);
// All changes committed together
}
}
Key Points:
2. Programmatic Transactions
TransactionTemplate or PlatformTransactionManager.Example:
transactionTemplate.execute(status -> {
userRepository.save(user);
orderRepository.save(order);
returnnull;
});
The @Transactional annotation is used to define transaction boundaries in Spring applications.
It tells Spring when to start, commit, or roll back a transaction automatically.
Spring manages transactions using AOP (proxy-based mechanism).
Where Can @Transactional Be Applied?
Method Level
Class Level
Example
@Service
publicclassUserService {
@Transactional
publicvoidtransferMoney(Long fromId, Long toId, BigDecimal amount) {
debitAccount(fromId, amount);
creditAccount(toId, amount);
}
}
Explanation:
transferMoney() begins
Propagation defines how transactions behave when calling another transactional method:
REQUIRED (Default Behavior)
REQUIRED means the method will use the existing transaction if one is already running.
If no transaction exists, Spring creates a new transaction.
Example:
Placing an order and saving order details.
@Transactional(propagation = Propagation.REQUIRED)
publicvoidsaveOrder() {
// joins existing transaction
}
REQUIRES_NEW
REQUIRES_NEW always starts a new transaction, even if another transaction already exists.
The existing transaction is temporarily suspended.
Example:
Saving audit logs while processing an order.
@Transactional(propagation = Propagation.REQUIRES_NEW)
publicvoidsaveAuditLog() {
// always runs in a new transaction
}
NESTED
NESTED creates a sub-transaction inside the main transaction using a savepoint.
If the nested transaction fails, it rolls back only its part, not the whole transaction.
Example:
Reserving inventory inside order processing.
@Transactional(propagation = Propagation.NESTED)
publicvoidreserveStock() {
// can rollback independently
}
SUPPORTS
SUPPORTS runs inside a transaction if one exists.
If no transaction exists, it runs without a transaction.
Example:
Sending a notification.
@Transactional(propagation = Propagation.SUPPORTS)
publicvoidsendNotification() {
// transactional only if caller is transactional
}
NOT_SUPPORTED
NOT_SUPPORTED always runs without a transaction.
If a transaction exists, Spring suspends it.
Example:
Calling an external API.
@Transactional(propagation = Propagation.NOT_SUPPORTED)
publicvoidcallExternalService() {
// no transaction
}
MANDATORY
MANDATORY requires an existing transaction.
If none exists, Spring throws an exception.
Example:
Security validation that must be transactional.
@Transactional(propagation = Propagation.MANDATORY)
publicvoidvalidateSecurity() {
// must run inside a transaction
}
NEVER
NEVER ensures the method must not run inside a transaction.
If a transaction exists, Spring throws an exception.
Example:
Health check or monitoring logic.
@Transactional(propagation = Propagation.NEVER)
publicvoidsystemHealthCheck() {
// no transaction allowed
}
| Propagation | Behavior |
|---|---|
| REQUIRED | Join existing transaction or create a new one if none exists (default) |
| REQUIRES_NEW | Suspend current transaction and start a new one |
| NESTED | Execute within a nested transaction (supports rollback without affecting outer transaction) |
| SUPPORTS | Execute within current transaction if exists, otherwise non-transactional |
| NOT_SUPPORTED | Suspend current transaction, run non-transactional |
| MANDATORY | Must run inside existing transaction; throws exception if none |
| NEVER | Must not run inside a transaction; throws exception if exists |
Example :
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void placeOrder(Order order) {
securityService.validate(order); // MANDATORY
orderRepository.save(order); // REQUIRED
inventoryService.reserveStock(order); // NESTED
paymentService.process(order); // REQUIRED
auditService.logAction(order); // REQUIRES_NEW
notificationService.notify(order); // SUPPORTS
shippingService.ship(order); // NOT_SUPPORTED
monitoringService.healthCheck(); // NEVER
}
}
Isolation defines how concurrent transactions interact:
| Isolation Level | What It Means | Problems Prevented | Problems Allowed | Common Use Case |
|---|---|---|---|---|
| READ_UNCOMMITTED | Can read data that is not yet saved | None | Dirty Read, Non-Repeatable Read, Phantom Read | Rarely used, temporary or non-critical reports |
| READ_COMMITTED | Can read only saved (committed) data | Dirty Read | Non-Repeatable Read, Phantom Read | Most web applications, CRUD systems |
| REPEATABLE_READ | Same data stays the same inside one transaction | Dirty Read, Non-Repeatable Read | Phantom Read (DB-dependent) | Banking, inventory systems |
| SERIALIZABLE | Transactions run one by one | Dirty Read, Non-Repeatable Read, Phantom Read | None | Financial systems, critical operations |
@Transactional rolls back on unchecked exceptions (RuntimeException, Error)@Transactional(rollbackFor = Exception.class)
publicvoidprocessPayment() { ... }
Marking a transaction as read-only can optimize performance for read queries:
@Transactional(readOnly = true)
public List<User>findAllActiveUsers() { ... }
Example:
@Transactional (Spring proxy limitation)readOnly=true@Transactional) and programmatic transaction management