Implementing Spring Transaction Management via Annotation Configuration
Transaction management logic should be implemented at the service layer in Spring applications. Spring provides two transaction management modes:
- Programmatic transaction management (for rare custom transaction control scenarios)
- Declarative transaction management (recommended for most production use cases)
- Annotation-based implementation (primary usage)
- XML-based implementation (for legacy system compatibility)
The underlying implementation of Spring declarative transactions relies on AOP, whose core mechanism is dynamic proxy.
Spring Transaction Management Core API
The top-level transaction manager interface is PlatformTransactionManager, with different implementation classes provided for different data access frameworks to adapt to varied persistence layer technology stacks.
Annotation-based Transaction Control Implementation
First add transaction related configurations in applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Enable Spring component scanning -->
<context:component-scan base-package="com.example"/>
<!-- Load external database configuration file -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- Configure Druid connection pool -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="url" value="${db.url}"/>
<property name="driverClassName" value="${db.driver}"/>
</bean>
<!-- Configure JdbcTemplate and inject data source -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Configure JDBC transaction manager -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Enable transaction annotation support -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
Add the @Transactional annotation to the corresponding service layer method or class:
package com.example.service.impl;
import com.example.repository.AccountRepository;
import com.example.service.FundTransferService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
// @Transactional // Uncomment to apply transaction rules to all public methods of this class
public class FundTransferServiceImpl implements FundTransferService {
@Autowired
private AccountRepository accountRepository;
@Override
@Transactional // Transaction control only applies to this current method
public int executeFundTransfer(int senderAccountId, int recipientAccountId, int transferAmount) {
int affectedRows = 0;
// Deduct transfer amount from sender account
affectedRows += accountRepository.adjustAccountBalance(senderAccountId, -transferAmount);
// Simulate unexpected runtime exception to validate rollback behavior
int mockException = 1 / 0;
// Add transfer amount to recipient account
affectedRows += accountRepository.adjustAccountBalance(recipientAccountId, transferAmount);
return affectedRows;
}
}
When the service method runs, if an exception occurs the transaction automatically rolls back, otherwise all changes are committed to the database after the method executes successfully.
Common Parameters of @Transactional Annotation
The @Transactional annotation supports multiple configurable parameters to customize transaction behavior, as shown in its definition:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
String timeoutString() default "";
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
propagation: Transaction Propagation Behavior
Defines how transactions are managed when multiple transaction-aware methods call each other. Common propagation strategies include:
REQUIRED: If the calling method already has a active transaction, the called methods join the existing transaction. If no active transaction exists, a new transaction is created for all participating methods.REQUIRES_NEW: Always create a new independent transaction regardless of whether an existing transaction is present, suspending any active parent transaction for the duration of the new transaction execution.
isolation: Transaction Isolation Level
Controls the visibility of uncommitted changes between concurrent transactions, available levels are:
DEFAULT: Uses the default isolation level of the underlying database. MySQL defaults toREPEATABLE_READ, Oracle defaults toREAD_COMMITTED.READ_UNCOMMITTED: Lowest isolation level, allows uncommitted changes of one transaction to be visible to others, risks dirty reads, non-repaetable reads and phantom reads.READ_COMMITTED: Ensures only committed changes are visible to other transactions, prevents dirty reads but still allows non-repeatable and phantom reads.REPEATABLE_READ: Prevents dirty reads and non-repeatable reads, but phantom reads may still occur.SERIALIZABLE: Highest isolation level, executes all transactions sequentially, eliminates all read anomalies but has significant performance overhead.
timeout: Transaction Timeout
Defines the maximum allowed execution time for a transaction. If the transaction is not completed within the specified time, it is automatically rolled back. The default value is -1, meaning no time limit.
readOnly: Read-only Transaction Flag
When set to true, marks the transaction as read-only, disallows insert, update and delete operations, and enables performance optimizations for pure read scenarios.
rollbackFor: Rollback Trigger Exceptions
Explicitly specifies which Throwable subtypes will trigger transaction rollback. By default Spring rolls back transactions only for runtime exceptions and Error types.
noRollbackFor: Non-rollback Exceptionss
Specifies exception types that will not trigger transaction rollback even if they are thrown during transaction execution.