Declarative transaction management uses annotations to define transaction boundaries and properties. This is the most common and recommended approach for transaction management in Spring applications.
Describes transaction attributes on methods or classes. When applied to a class, it provides default transaction attributes for all methods in the class. Method-level annotations override class-level settings.
/**
* Describes transaction attributes on a method or class.
* When applied at the class level, serves as a default for all methods.
* When applied at the method level, overrides any class-level settings.
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* Alias for transactionManager().
*/
@AliasFor("transactionManager")
String value() default "";
/**
* Qualifier for the transaction manager to use.
* Can be a bean name or qualifier value.
*/
@AliasFor("value")
String transactionManager() default "";
/**
* Labels to associate with the transaction for monitoring/diagnostics.
*/
String[] label() default {};
/**
* Transaction propagation behavior.
* Default is REQUIRED.
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* Transaction isolation level.
* Default is DEFAULT (use underlying datastore's default).
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* Transaction timeout in seconds.
* Default is -1 (use underlying transaction system's default).
*/
int timeout() default -1;
/**
* Transaction timeout as a String expression (supports property placeholders).
*/
String timeoutString() default "";
/**
* Whether transaction is read-only.
* Default is false.
*/
boolean readOnly() default false;
/**
* Exception types that trigger rollback.
* By default, only unchecked exceptions cause rollback.
*/
Class<? extends Throwable>[] rollbackFor() default {};
/**
* Exception class name patterns that trigger rollback.
*/
String[] rollbackForClassName() default {};
/**
* Exception types that do NOT trigger rollback.
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* Exception class name patterns that do NOT trigger rollback.
*/
String[] noRollbackForClassName() default {};
}Usage Examples:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Isolation;
@Service
public class OrderService {
// Basic transactional method with default settings
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
inventoryService.reserveItems(order.getItems());
}
// Read-only transaction for query operations
@Transactional(readOnly = true)
public Order getOrder(Long orderId) {
return orderRepository.findById(orderId);
}
// Custom propagation behavior - always create new transaction
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAuditEvent(AuditEvent event) {
auditRepository.save(event);
}
// Custom isolation level for critical operations
@Transactional(isolation = Isolation.SERIALIZABLE, timeout = 30)
public void processPayment(Payment payment) {
paymentRepository.save(payment);
accountService.debit(payment.getAmount());
}
// Rollback on checked exceptions
@Transactional(rollbackFor = Exception.class)
public void importData(File dataFile) throws IOException {
List<Record> records = parser.parse(dataFile);
recordRepository.saveAll(records);
}
// Multiple transaction managers
@Transactional("orderTransactionManager")
public void createOrderWithSpecificTxManager(Order order) {
orderRepository.save(order);
}
// Transaction labels for monitoring
@Transactional(label = {"critical", "payment-processing"})
public void processHighValuePayment(Payment payment) {
paymentProcessor.process(payment);
}
}Enables Spring's annotation-driven transaction management capability. Must be placed on a @Configuration class.
/**
* Enables Spring's annotation-driven transaction management capability.
* Place on @Configuration classes.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
/**
* Whether to create class-based (CGLIB) proxies instead of interface-based (JDK) proxies.
* Default is false (JDK proxies).
* Set to true if your transactional classes don't implement interfaces.
*/
boolean proxyTargetClass() default false;
/**
* How transactions should be applied: PROXY (default) or ASPECTJ.
* PROXY uses Spring AOP proxying.
* ASPECTJ uses load-time or compile-time weaving.
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Order for transaction advisor when multiple aspects are applied.
* Default is LOWEST_PRECEDENCE.
*/
int order() default Ordered.LOWEST_PRECEDENCE;
/**
* Global rollback behavior.
* RUNTIME_EXCEPTIONS: rollback only on unchecked exceptions (default)
* ALL_EXCEPTIONS: rollback on all exceptions including checked
*/
RollbackOn rollbackOn() default RollbackOn.RUNTIME_EXCEPTIONS;
}Usage Examples:
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
// Basic configuration
@Configuration
@EnableTransactionManagement
public class AppConfig {
// Define transaction manager bean
}
// With CGLIB proxies for classes without interfaces
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
public class AppConfig {
// Configuration beans
}
// With AspectJ weaving for advanced scenarios
@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
public class AppConfig {
// Configuration beans
}
// With custom advisor order
@Configuration
@EnableTransactionManagement(order = 100)
public class AppConfig {
// Configuration beans
}
// Rollback on all exceptions
@Configuration
@EnableTransactionManagement(rollbackOn = RollbackOn.ALL_EXCEPTIONS)
public class AppConfig {
// Configuration beans
}Defines transaction propagation behavior - how transactions relate to each other.
/**
* Enumeration representing transaction propagation behaviors.
*/
public enum Propagation {
/**
* Support a current transaction; create a new one if none exists.
* This is the default setting.
*/
REQUIRED,
/**
* Support a current transaction; execute non-transactionally if none exists.
*/
SUPPORTS,
/**
* Support a current transaction; throw exception if no transaction exists.
*/
MANDATORY,
/**
* Create a new transaction, suspending the current transaction if one exists.
*/
REQUIRES_NEW,
/**
* Do not support a current transaction; execute non-transactionally.
* If a transaction exists, suspend it.
*/
NOT_SUPPORTED,
/**
* Do not support a current transaction; throw exception if transaction exists.
*/
NEVER,
/**
* Execute within a nested transaction if a current transaction exists.
* Behaves like REQUIRED if no transaction exists.
* Uses savepoints on single transaction with rollback to savepoint on nested failure.
*/
NESTED;
/**
* Returns the TransactionDefinition constant value.
*/
public int value();
}Usage Examples:
@Service
public class TransactionPropagationExamples {
// REQUIRED (default): Join existing or create new
@Transactional(propagation = Propagation.REQUIRED)
public void processOrder(Order order) {
orderRepository.save(order);
inventoryService.updateStock(order); // Joins this transaction
}
// REQUIRES_NEW: Always create independent transaction
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAudit(String message) {
auditLog.save(message); // Committed independently
}
// NESTED: Use savepoint for partial rollback
@Transactional(propagation = Propagation.NESTED)
public void processLineItem(LineItem item) {
// Can rollback just this item without affecting outer transaction
lineItemRepository.save(item);
}
// SUPPORTS: Use transaction if available
@Transactional(propagation = Propagation.SUPPORTS)
public Order findOrder(Long id) {
return orderRepository.findById(id);
}
// MANDATORY: Requires existing transaction
@Transactional(propagation = Propagation.MANDATORY)
public void validateInTransaction(Order order) {
// Must be called within existing transaction
validator.validate(order);
}
// NOT_SUPPORTED: Always execute non-transactionally
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendEmail(String message) {
emailService.send(message); // Outside transaction
}
// NEVER: Fail if transaction exists
@Transactional(propagation = Propagation.NEVER)
public void reportGenerator() {
// Must never be called within a transaction
reportService.generate();
}
}Defines transaction isolation levels controlling visibility of concurrent changes.
/**
* Enumeration representing transaction isolation levels.
*/
public enum Isolation {
/**
* Use the default isolation level of the underlying datastore.
* All other values are translated to standard JDBC isolation levels.
*/
DEFAULT,
/**
* Lowest isolation level. Permits dirty reads, non-repeatable reads, and phantom reads.
* Allows reading uncommitted changes from other transactions.
*/
READ_UNCOMMITTED,
/**
* Prevents dirty reads; non-repeatable reads and phantom reads can occur.
* Guarantees that any data read was committed at the moment it was read.
*/
READ_COMMITTED,
/**
* Prevents dirty reads and non-repeatable reads; phantom reads can occur.
* Guarantees that if a row is read twice in the same transaction,
* it will have the same values both times.
*/
REPEATABLE_READ,
/**
* Highest isolation level. Prevents dirty reads, non-repeatable reads, and phantom reads.
* Guarantees complete isolation from other transactions.
* Most expensive in terms of performance.
*/
SERIALIZABLE;
/**
* Returns the TransactionDefinition constant value.
*/
public int value();
}Usage Examples:
@Service
public class IsolationLevelExamples {
// Default isolation (database default, usually READ_COMMITTED)
@Transactional(isolation = Isolation.DEFAULT)
public void standardOperation() {
// Uses database's default isolation
}
// Read committed for most business operations
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processPayment(Payment payment) {
// Prevents reading uncommitted data
paymentRepository.save(payment);
}
// Repeatable read for consistent queries
@Transactional(isolation = Isolation.REPEATABLE_READ)
public Report generateConsistentReport() {
// Multiple reads of same data return same values
List<Order> orders = orderRepository.findAll();
Statistics stats = calculateStats(orders);
return new Report(orders, stats);
}
// Serializable for critical financial operations
@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferFunds(Account from, Account to, BigDecimal amount) {
// Complete isolation, prevents all concurrency anomalies
from.debit(amount);
to.credit(amount);
accountRepository.save(from);
accountRepository.save(to);
}
// Read uncommitted for reporting (accepts risk of dirty reads)
@Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = true)
public List<Order> generateApproximateReport() {
// May see uncommitted changes, but faster
return orderRepository.findAllForReport();
}
}Defines global rollback behavior for all exceptions.
/**
* Enumeration representing global rollback behavior.
*/
public enum RollbackOn {
/**
* Default behavior: rollback only on runtime (unchecked) exceptions.
* Checked exceptions do not trigger rollback.
*/
RUNTIME_EXCEPTIONS,
/**
* Rollback on all exceptions, including checked exceptions.
*/
ALL_EXCEPTIONS
}Usage Example:
@Configuration
@EnableTransactionManagement(rollbackOn = RollbackOn.ALL_EXCEPTIONS)
public class AllExceptionsRollbackConfig {
// All @Transactional methods will rollback on any exception
}Additional classes supporting declarative transaction configuration.
/**
* Callback interface for custom transaction manager selection.
*/
public interface TransactionManagementConfigurer {
/**
* Returns the default transaction manager bean to use for @Transactional methods.
*/
TransactionManager annotationDrivenTransactionManager();
}
/**
* Strategy interface for parsing transaction annotations.
*/
public interface TransactionAnnotationParser {
/**
* Determine whether the given class is a candidate for transaction attributes.
*/
default boolean isCandidateClass(Class<?> targetClass);
/**
* Parse the transaction attribute for the given method or class.
*/
TransactionAttribute parseTransactionAnnotation(AnnotatedElement element);
}Usage Example:
@Configuration
@EnableTransactionManagement
public class CustomTransactionManagerConfig implements TransactionManagementConfigurer {
@Bean
public PlatformTransactionManager primaryTransactionManager() {
return new DataSourceTransactionManager(primaryDataSource());
}
@Bean
public PlatformTransactionManager secondaryTransactionManager() {
return new DataSourceTransactionManager(secondaryDataSource());
}
@Override
public TransactionManager annotationDrivenTransactionManager() {
// Return the default transaction manager for @Transactional
return primaryTransactionManager();
}
}@Service
@Transactional
public class UserService {
// All public methods are transactional by default
public void createUser(User user) {
userRepository.save(user);
eventPublisher.publishEvent(new UserCreatedEvent(user));
}
@Transactional(readOnly = true)
public User getUser(Long id) {
// Override class-level settings for read operations
return userRepository.findById(id);
}
@Transactional(rollbackFor = Exception.class)
public void importUsers(File csvFile) throws IOException {
List<User> users = csvParser.parse(csvFile);
userRepository.saveAll(users);
}
}@Service
public class OrderService {
// Rollback on specific exception
@Transactional(rollbackFor = PaymentException.class)
public void processOrder(Order order) throws PaymentException {
orderRepository.save(order);
paymentService.charge(order.getTotal());
}
// Don't rollback on specific exception
@Transactional(noRollbackFor = ValidationException.class)
public void validateAndSave(Order order) {
try {
validator.validate(order);
} catch (ValidationException e) {
// Log but don't rollback
logger.warn("Validation warning", e);
}
orderRepository.save(order);
}
// Rollback on all exceptions (including checked)
@Transactional(rollbackFor = Exception.class)
public void criticalOperation() throws Exception {
// Any exception causes rollback
performCriticalWork();
}
}@Service
public class MultiDataSourceService {
@Transactional("orderTransactionManager")
public void saveOrder(Order order) {
orderRepository.save(order);
}
@Transactional("customerTransactionManager")
public void saveCustomer(Customer customer) {
customerRepository.save(customer);
}
}@Transactional only works on public methods of Spring-managed beans