Spring Transaction Management
Spring provides powerful declarative and programmatic transaction management using the @Transactional annotation or TransactionTemplate. It simplifies managing transactions, ensuring data consistency, flexibility and rollback on errors.
Declarative Transaction Management
Use @EnableTransactionManagement to enable annotation-driven transaction management.
@Configuration
@EnableTransactionManagement
public class AppConfig {
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource("jdbc:mysql://localhost:3306/mydb", "user", "pass");
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
Using @Transactional
Add @Transactional to methods/classes to manage transactions declaratively. The transaction begins when the method is called and in case an exception is being thrown a rollback occurs.
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void createUser(String name) {
jdbcTemplate.update("INSERT INTO users (name) VALUES (?)", name);
// If an exception occurs, the transaction is rolled back.
if (name == null) throw new IllegalArgumentException("Name cannot be null");
}
}
Transaction Propagation
Propagation determines how transactions behave when a transactional method calls another transactional method. Common options:
- REQUIRED (default): Joins an existing transaction or starts a new one.
- REQUIRES_NEW: Suspends the current transaction and starts a new one.
- NESTED: Executes in a nested transaction.
- SUPPORTS: First checks if an active transaction exists, if so the existing transaction will be used, otherwise it is executed non-transactional".
- MANDATORY: If there is a transaction it will be used, otherwise an exception will be thrown
- NEVER: In case a transaction is active, an exception will be thrown.
- NOT_SUPPORTED: If a transaction exists, then it will be removed and afterwards the business logic will be executed.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logTransaction(String message) {
jdbcTemplate.update("INSERT INTO logs (message) VALUES (?)", message);
}
Rollback Rules
You can configure rollback behavior with @Transactional. By default rollbacks occur on unchecked exceptions (RuntimeException or Error). Checked exceptions like Exception don't trigger a rollback by default.
The rollback behavior can be controlled using rollbackFor and noRollbackFor arguments in the @Transactional annotation.
@Transactional(rollbackFor = Exception.class)
public void saveUser(String name) throws Exception {
jdbcTemplate.update("INSERT INTO users (name) VALUES (?)", name);
throw new Exception("Force rollback");
}
@Transactional(noRollbackFor = IllegalArgumentException.class)
public void saveUser(String name) {
jdbcTemplate.update("INSERT INTO users (name) VALUES (?)", name);
throw new IllegalArgumentException("No rollback for this exception");
}
Using Transactions in Tests
Spring supports transactions in tests with @Transactional. By default, transactions roll back after the test completes, which is different from the usual behavior.
@SpringBootTest
@Transactional
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testCreateUser() {
userService.createUser("John");
// Transaction automatically rolls back after the test.
}
}
Key Takeaways
- Use @Transactional for declarative transaction management.
- Configure propagation based on how nested transactions should behave.
- Define rollback rules to handle exceptions precisely.
- Use @Transactional in tests to ensure isolation and automatic rollback.
