0
# Declarative Transaction Management
1
2
Spring's declarative transaction management enables transaction control through annotations and configuration, providing clean separation between business logic and transaction concerns using Aspect-Oriented Programming (AOP).
3
4
## Core Annotations
5
6
### @Transactional
7
8
The primary annotation for declarative transaction demarcation, applicable to classes and methods.
9
10
```java { .api }
11
@Target({ElementType.TYPE, ElementType.METHOD})
12
@Retention(RetentionPolicy.RUNTIME)
13
@Documented
14
public @interface Transactional {
15
/**
16
* Alias for transactionManager.
17
* @return the transaction manager bean name
18
*/
19
@AliasFor("transactionManager")
20
String value() default "";
21
22
/**
23
* Specify the transaction manager to use.
24
* @return the transaction manager bean name
25
*/
26
@AliasFor("value")
27
String transactionManager() default "";
28
29
/**
30
* Define transaction propagation behavior.
31
* @return the propagation behavior
32
*/
33
Propagation propagation() default Propagation.REQUIRED;
34
35
/**
36
* Define transaction isolation level.
37
* @return the isolation level
38
*/
39
Isolation isolation() default Isolation.DEFAULT;
40
41
/**
42
* Define transaction timeout in seconds.
43
* @return the timeout in seconds
44
*/
45
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
46
47
/**
48
* Define whether transaction is read-only.
49
* @return true if read-only
50
*/
51
boolean readOnly() default false;
52
53
/**
54
* Define exception types that trigger rollback.
55
* @return exception classes for rollback
56
*/
57
Class<? extends Throwable>[] rollbackFor() default {};
58
59
/**
60
* Define exception class names that trigger rollback.
61
* @return exception class names for rollback
62
*/
63
String[] rollbackForClassName() default {};
64
65
/**
66
* Define exception types that do NOT trigger rollback.
67
* @return exception classes to not rollback for
68
*/
69
Class<? extends Throwable>[] noRollbackFor() default {};
70
71
/**
72
* Define exception class names that do NOT trigger rollback.
73
* @return exception class names to not rollback for
74
*/
75
String[] noRollbackForClassName() default {};
76
}
77
```
78
79
### @EnableTransactionManagement
80
81
Configuration annotation to enable annotation-driven transaction management.
82
83
```java { .api }
84
@Target(ElementType.TYPE)
85
@Retention(RetentionPolicy.RUNTIME)
86
@Documented
87
@Import(TransactionManagementConfigurationSelector.class)
88
public @interface EnableTransactionManagement {
89
/**
90
* Indicate whether subclass-based (CGLIB) proxies should be created.
91
* @return true for CGLIB proxies, false for JDK dynamic proxies
92
*/
93
boolean proxyTargetClass() default false;
94
95
/**
96
* Indicate how transactional advice should be applied.
97
* @return advice mode (PROXY or ASPECTJ)
98
*/
99
AdviceMode mode() default AdviceMode.PROXY;
100
101
/**
102
* Indicate the order in which the TransactionInterceptor should be applied.
103
* @return the order value
104
*/
105
int order() default Ordered.LOWEST_PRECEDENCE;
106
}
107
108
public enum AdviceMode {
109
PROXY, ASPECTJ
110
}
111
```
112
113
## Propagation and Isolation Enums
114
115
### Propagation
116
117
Enumeration for transaction propagation behaviors corresponding to TransactionDefinition constants.
118
119
```java { .api }
120
public enum Propagation {
121
/**
122
* Support a current transaction; create a new one if none exists.
123
*/
124
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
125
126
/**
127
* Support a current transaction; execute non-transactionally if none exists.
128
*/
129
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
130
131
/**
132
* Support a current transaction; throw an exception if no current transaction exists.
133
*/
134
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
135
136
/**
137
* Create a new transaction, suspending the current transaction if one exists.
138
*/
139
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
140
141
/**
142
* Do not support a current transaction; rather always execute non-transactionally.
143
*/
144
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
145
146
/**
147
* Do not support a current transaction; throw an exception if a current transaction exists.
148
*/
149
NEVER(TransactionDefinition.PROPAGATION_NEVER),
150
151
/**
152
* Execute within a nested transaction if a current transaction exists.
153
*/
154
NESTED(TransactionDefinition.PROPAGATION_NESTED);
155
156
private final int value;
157
158
Propagation(int value) {
159
this.value = value;
160
}
161
162
public int value() {
163
return this.value;
164
}
165
}
166
```
167
168
### Isolation
169
170
Enumeration for transaction isolation levels corresponding to TransactionDefinition constants.
171
172
```java { .api }
173
public enum Isolation {
174
/**
175
* Use the default isolation level of the underlying datastore.
176
*/
177
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
178
179
/**
180
* Dirty reads, non-repeatable reads, and phantom reads can occur.
181
*/
182
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
183
184
/**
185
* Dirty reads are prevented; non-repeatable reads and phantom reads can occur.
186
*/
187
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
188
189
/**
190
* Dirty reads and non-repeatable reads are prevented; phantom reads can occur.
191
*/
192
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
193
194
/**
195
* Dirty reads, non-repeatable reads, and phantom reads are prevented.
196
*/
197
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
198
199
private final int value;
200
201
Isolation(int value) {
202
this.value = value;
203
}
204
205
public int value() {
206
return this.value;
207
}
208
}
209
```
210
211
## Configuration Classes
212
213
### TransactionManagementConfigurer
214
215
Interface for transaction management configuration customization.
216
217
```java { .api }
218
public interface TransactionManagementConfigurer {
219
/**
220
* Return the default transaction manager bean to use for annotation-driven database
221
* transaction management.
222
* @return the default transaction manager, or null if there is no single default
223
*/
224
@Nullable
225
PlatformTransactionManager annotationDrivenTransactionManager();
226
}
227
```
228
229
### ProxyTransactionManagementConfiguration
230
231
Configuration class for proxy-based transaction management.
232
233
```java { .api }
234
@Configuration
235
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
236
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
237
238
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
239
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
240
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
241
TransactionAttributeSource transactionAttributeSource,
242
TransactionInterceptor transactionInterceptor);
243
244
@Bean
245
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
246
public TransactionAttributeSource transactionAttributeSource();
247
248
@Bean
249
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
250
public TransactionInterceptor transactionInterceptor(
251
TransactionAttributeSource transactionAttributeSource);
252
}
253
```
254
255
## Transaction Attributes
256
257
### TransactionAttribute
258
259
Extended transaction definition interface for interceptor-based transactions.
260
261
```java { .api }
262
public interface TransactionAttribute extends TransactionDefinition {
263
/**
264
* Return a qualifier value associated with this transaction attribute.
265
* @return qualifier value, or null if none
266
*/
267
@Nullable
268
String getQualifier();
269
270
/**
271
* Return descriptive labels associated with this transaction attribute.
272
* @return label array, or null if none
273
*/
274
@Nullable
275
String[] getLabels();
276
277
/**
278
* Should we roll back on the given exception?
279
* @param ex the exception to evaluate
280
* @return whether to perform a rollback or not
281
*/
282
boolean rollbackOn(Throwable ex);
283
}
284
```
285
286
### RuleBasedTransactionAttribute
287
288
Rule-based transaction attribute implementation that handles rollback rules.
289
290
```java { .api }
291
public class RuleBasedTransactionAttribute extends DefaultTransactionAttribute implements Serializable {
292
293
public RuleBasedTransactionAttribute();
294
public RuleBasedTransactionAttribute(RuleBasedTransactionAttribute other);
295
public RuleBasedTransactionAttribute(int propagationBehavior, List<RollbackRuleAttribute> rollbackRules);
296
297
public void setRollbackRules(List<RollbackRuleAttribute> rollbackRules);
298
public List<RollbackRuleAttribute> getRollbackRules();
299
300
@Override
301
public boolean rollbackOn(Throwable ex);
302
}
303
```
304
305
### RollbackRuleAttribute
306
307
Rule for determining rollback behavior based on exception types.
308
309
```java { .api }
310
public class RollbackRuleAttribute implements Serializable {
311
public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS =
312
new RollbackRuleAttribute(RuntimeException.class);
313
314
public RollbackRuleAttribute(Class<?> clazz);
315
public RollbackRuleAttribute(String exceptionName);
316
317
public int getDepth(Throwable ex);
318
public Class<?> getExceptionType();
319
public String getExceptionName();
320
}
321
322
public class NoRollbackRuleAttribute extends RollbackRuleAttribute {
323
public NoRollbackRuleAttribute(Class<?> clazz);
324
public NoRollbackRuleAttribute(String exceptionName);
325
}
326
```
327
328
## Transaction Attribute Sources
329
330
### AnnotationTransactionAttributeSource
331
332
Transaction attribute source that reads transaction attributes from annotations.
333
334
```java { .api }
335
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource
336
implements Serializable {
337
338
public AnnotationTransactionAttributeSource();
339
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly);
340
public AnnotationTransactionAttributeSource(TransactionAnnotationParser... annotationParsers);
341
public AnnotationTransactionAttributeSource(Set<TransactionAnnotationParser> annotationParsers);
342
343
public void setAnnotationParsers(Set<TransactionAnnotationParser> annotationParsers);
344
public void setPublicMethodsOnly(boolean publicMethodsOnly);
345
346
@Override
347
@Nullable
348
protected TransactionAttribute findTransactionAttribute(Class<?> clazz);
349
350
@Override
351
@Nullable
352
protected TransactionAttribute findTransactionAttribute(Method method);
353
}
354
```
355
356
### CompositeTransactionAttributeSource
357
358
Composite transaction attribute source that delegates to multiple sources.
359
360
```java { .api }
361
public class CompositeTransactionAttributeSource implements TransactionAttributeSource, Serializable {
362
363
public CompositeTransactionAttributeSource(TransactionAttributeSource... transactionAttributeSources);
364
365
@Override
366
@Nullable
367
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);
368
}
369
```
370
371
## Usage Examples
372
373
### Basic Declarative Transactions
374
375
```java
376
@Service
377
@Transactional
378
public class UserService {
379
380
private final UserRepository userRepository;
381
private final EmailService emailService;
382
383
public UserService(UserRepository userRepository, EmailService emailService) {
384
this.userRepository = userRepository;
385
this.emailService = emailService;
386
}
387
388
// Read-only transaction for queries
389
@Transactional(readOnly = true)
390
public User findUser(Long id) {
391
return userRepository.findById(id)
392
.orElseThrow(() -> new UserNotFoundException(id));
393
}
394
395
// Default transaction settings (REQUIRED propagation, READ_COMMITTED isolation)
396
public User createUser(User user) {
397
User savedUser = userRepository.save(user);
398
emailService.sendWelcomeEmail(savedUser);
399
return savedUser;
400
}
401
402
// Custom isolation level and rollback rules
403
@Transactional(
404
isolation = Isolation.SERIALIZABLE,
405
rollbackFor = {DataIntegrityViolationException.class, ValidationException.class},
406
timeout = 10
407
)
408
public User updateUserEmail(Long userId, String newEmail) {
409
User user = userRepository.findById(userId)
410
.orElseThrow(() -> new UserNotFoundException(userId));
411
412
// Check if email already exists
413
if (userRepository.existsByEmail(newEmail)) {
414
throw new ValidationException("Email already in use");
415
}
416
417
user.setEmail(newEmail);
418
return userRepository.save(user);
419
}
420
}
421
```
422
423
### Transaction Propagation Examples
424
425
```java
426
@Service
427
@Transactional
428
public class OrderService {
429
430
private final OrderRepository orderRepository;
431
private final InventoryService inventoryService;
432
private final PaymentService paymentService;
433
private final AuditService auditService;
434
435
// REQUIRED: Join existing transaction or create new one
436
@Transactional(propagation = Propagation.REQUIRED)
437
public Order processOrder(Order order) {
438
Order savedOrder = orderRepository.save(order);
439
inventoryService.reserveItems(order.getItems()); // Joins this transaction
440
paymentService.processPayment(order.getPayment()); // Joins this transaction
441
return savedOrder;
442
}
443
444
// REQUIRES_NEW: Always create new transaction, suspend existing
445
@Transactional(propagation = Propagation.REQUIRES_NEW)
446
public void logOrderAttempt(Order order) {
447
// This runs in separate transaction, commits independently
448
auditService.logAttempt("order.process", order.getId());
449
}
450
451
// SUPPORTS: Join transaction if exists, run non-transactionally otherwise
452
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
453
public List<Order> findRecentOrders(Long userId) {
454
return orderRepository.findByUserIdAndCreatedAtAfter(
455
userId, LocalDateTime.now().minusDays(30));
456
}
457
458
// MANDATORY: Must have existing transaction, throw exception otherwise
459
@Transactional(propagation = Propagation.MANDATORY)
460
public void updateOrderStatus(Long orderId, OrderStatus status) {
461
Order order = orderRepository.findById(orderId)
462
.orElseThrow(() -> new OrderNotFoundException(orderId));
463
order.setStatus(status);
464
orderRepository.save(order);
465
}
466
467
// NESTED: Create nested transaction with savepoint
468
@Transactional(propagation = Propagation.NESTED)
469
public void processOptionalUpgrade(Order order, Upgrade upgrade) {
470
try {
471
// This runs in nested transaction
472
upgradeService.applyUpgrade(order, upgrade);
473
} catch (UpgradeException e) {
474
// Nested transaction rolls back, main transaction continues
475
log.warn("Upgrade failed for order {}, continuing without upgrade", order.getId());
476
}
477
}
478
}
479
```
480
481
### Configuration Examples
482
483
```java
484
@Configuration
485
@EnableTransactionManagement
486
public class TransactionConfig {
487
488
@Bean
489
public PlatformTransactionManager transactionManager(DataSource dataSource) {
490
return new DataSourceTransactionManager(dataSource);
491
}
492
}
493
494
// Custom transaction management configuration
495
@Configuration
496
@EnableTransactionManagement
497
public class CustomTransactionConfig implements TransactionManagementConfigurer {
498
499
@Bean
500
@Primary
501
public PlatformTransactionManager primaryTransactionManager(
502
@Qualifier("primaryDataSource") DataSource dataSource) {
503
return new DataSourceTransactionManager(dataSource);
504
}
505
506
@Bean
507
public PlatformTransactionManager secondaryTransactionManager(
508
@Qualifier("secondaryDataSource") DataSource dataSource) {
509
return new DataSourceTransactionManager(dataSource);
510
}
511
512
@Override
513
public PlatformTransactionManager annotationDrivenTransactionManager() {
514
return primaryTransactionManager(null); // Return primary by default
515
}
516
}
517
518
// Service using multiple transaction managers
519
@Service
520
public class MultiDataSourceService {
521
522
@Transactional("primaryTransactionManager")
523
public void saveToPrimary(Entity entity) {
524
primaryRepository.save(entity);
525
}
526
527
@Transactional("secondaryTransactionManager")
528
public void saveToSecondary(Entity entity) {
529
secondaryRepository.save(entity);
530
}
531
532
@Transactional("primaryTransactionManager")
533
public void saveToMultiple(Entity entity) {
534
primaryRepository.save(entity);
535
// This will not participate in the primary transaction
536
saveToSecondaryInNewTransaction(entity);
537
}
538
539
@Transactional(value = "secondaryTransactionManager", propagation = Propagation.REQUIRES_NEW)
540
private void saveToSecondaryInNewTransaction(Entity entity) {
541
secondaryRepository.save(entity);
542
}
543
}
544
```
545
546
### Advanced Rollback Rules
547
548
```java
549
@Service
550
@Transactional
551
public class PaymentService {
552
553
// Rollback for all exceptions except specific business exceptions
554
@Transactional(
555
rollbackFor = Exception.class,
556
noRollbackFor = {InsufficientFundsException.class, PaymentDeclinedException.class}
557
)
558
public PaymentResult processPayment(Payment payment) {
559
try {
560
return paymentGateway.charge(payment);
561
} catch (InsufficientFundsException | PaymentDeclinedException e) {
562
// These don't rollback transaction, log and return failure result
563
log.warn("Payment failed: {}", e.getMessage());
564
return PaymentResult.failed(e.getMessage());
565
}
566
// Other exceptions (network issues, etc.) will rollback transaction
567
}
568
569
// Custom rollback rules using class names
570
@Transactional(
571
rollbackForClassName = {"java.lang.Exception"},
572
noRollbackForClassName = {"com.example.business.BusinessException"}
573
)
574
public void processWithStringRules(ProcessingRequest request) {
575
// Processing logic
576
}
577
}
578
```
579
580
## Class-Level vs Method-Level Annotations
581
582
```java
583
@Service
584
@Transactional(readOnly = true) // Default for all methods
585
public class ReadHeavyService {
586
587
// Inherits class-level @Transactional(readOnly = true)
588
public List<User> findAllUsers() {
589
return userRepository.findAll();
590
}
591
592
// Overrides class-level settings
593
@Transactional(readOnly = false, rollbackFor = Exception.class)
594
public User createUser(User user) {
595
return userRepository.save(user);
596
}
597
598
// Combines with class-level settings (readOnly overridden, other settings inherited)
599
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
600
public User updateUserInNewTransaction(User user) {
601
return userRepository.save(user);
602
}
603
}
604
```