Mastering Transactions in Spring: Enabling, Using, and Monitoring
- michalkrysiuk64
- Feb 19
- 4 min read
Updated: Feb 20

Transactions are a fundamental part of modern applications, ensuring data integrity and consistency. Spring provides a powerful and flexible transaction management system that abstracts the complexities of working directly with JDBC, JPA, or Hibernate transactions. In this post, we’ll explore how to enable transaction management in a Spring application, perform actions within transactions, and monitor transaction behavior.
1. Enabling Transactions in Spring
To use transactions in a Spring application, you need to enable transaction management. This can be done in two ways:
Add Dependencies
If you are using Spring Boot with JPA/Hibernate, include the following dependencies in pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
If using a standalone Spring project, include: <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
Enable Transaction Management
Add @EnableTransactionManagement to your configuration class:
@Configuration
@EnableTransactionManagement
public class AppConfig {
}
2. Using Transactions in Spring
Once transaction management is enabled, we can use the @Transactional annotation to define transactional boundaries.
Applying @Transactional on a Service Layer
Spring allows you to apply transactions at different levels:
On a class (all methods will be transactional):
@Service
@Transactional
public class UserService {
public void registerUser(User user) {
// All database operations in this method will be in the same transaction
}
}
On a method (only the annotated method will be transactional):
@Service
public class UserService {
@Transactional
public void registerUser(User user) {
// This method runs within a transaction
}
}
Programmatic Transactions (Using TransactionTemplate)
If you prefer programmatic transaction management, use TransactionTemplate:
@Service
public class UserService {
private final TransactionTemplate transactionTemplate;
public UserService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void registerUser(User user) {
transactionTemplate.execute(status -> {
// Perform database operations inside a manually controlled transaction
return null;
});
}
}
3. Controlling and Monitoring Transactions
A. Configuring Transaction Propagation and Isolation Levels
You can fine-tune transactional behavior using attributes:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, timeout = 5, rollbackFor = Exception.class)
public void performOperation() {
// This method will respect the specified transactional properties
}
Propagation.REQUIRED → Uses the current transaction if available; otherwise, creates a new one.
Isolation.READ_COMMITTED → Ensures uncommitted changes from other transactions are not visible.
timeout = 5 → The transaction will be rolled back if it takes longer than 5 seconds.
rollbackFor = Exception.class → Explicitly rolls back if an exception occurs.
B. Logging Transactions
To enable transaction logging, configure Spring Logging in application.properties:
logging.level.org.springframework.transaction=DEBUG
logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG
This allows you to trace transactional behavior in the logs.
Example Transaction Logs
After enabling transaction logging, you will see logs similar to these:
1. Transaction Start
DEBUG o.s.orm.jpa.JpaTransactionManager - Creating new transaction with name [com.example.service.UserService.registerUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG o.s.orm.jpa.JpaTransactionManager - Opened new EntityManager [SessionImpl(1234)] for JPA transaction
DEBUG o.s.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl@5678]
2. Database Operation Inside the Transaction
DEBUG o.h.SQL - insert into users (id, name, email) values (?, ?, ?)
TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [1001]
TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - [John Doe]
TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [john.doe@example.com]
3. Transaction Commit
DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit
DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [SessionImpl(1234)]
DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [SessionImpl(1234)] after transaction
4. Transaction Rollback (In Case of an Exception)
DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction rollback
WARN o.s.orm.jpa.JpaTransactionManager - Rolling back JPA transaction on EntityManager [SessionImpl(1234)]
DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [SessionImpl(1234)] after transaction
These logs provide insights into transaction start, execution, commit, and rollback events.
C. Checking the Current Transaction Context
If you need to check the active transaction during runtime, you can inject TransactionSynchronizationManager:
import org.springframework.transaction.support.TransactionSynchronizationManager;
public void checkTransaction() {
boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
System.out.println("Transaction active: " + isActive);
}
4. Best Practices for Transaction Management
Use @Transactional only at the service layer, not in DAOs or controllers.This ensures clear separation of concerns. The service layer handles business logic and transactions, while DAOs focus only on database interactions. Keeping transactions out of controllers prevents unnecessary transaction management at the presentation layer.
Ensure proper exception handling to prevent silent transaction failures.Without proper handling, unchecked exceptions can cause transactions to roll back unexpectedly or, worse, leave the database in an inconsistent state. Catching and logging exceptions helps diagnose issues and maintain data integrity.
Be mindful of lazy-loading issues when using transactions with JPA.Transactions should remain open while accessing lazy-loaded entities. If a transaction closes too early, it can lead to LazyInitializationException. Designing transactions carefully prevents unexpected data access failures.
Set explicit rollback conditions to control transaction behavior.By default, Spring rolls back only on unchecked exceptions. Explicitly defining rollback rules using @Transactional(rollbackFor = Exception.class) ensures that even checked exceptions trigger rollbacks when needed, preventing partial updates to the database.
Following these best practices enhances application reliability, improves maintainability, and prevents data inconsistencies.
Conclusion
Spring’s transaction management is powerful and flexible, making it easy to ensure data integrity and consistency. By enabling transaction management with @EnableTransactionManagement, applying @Transactional at the service layer, and using logging and monitoring tools, you can effectively control transactional behavior in your application.
Happy Coding!
Comments