0
# Transaction Management
1
2
Spring JDBC provides comprehensive transaction management through integration with Spring's transaction infrastructure. The `DataSourceTransactionManager` enables declarative and programmatic transaction management for single-database applications, with support for savepoints, timeout handling, and read-only optimizations.
3
4
## Capabilities
5
6
### DataSourceTransactionManager
7
8
Primary transaction manager for single-JDBC DataSource applications with full Spring transaction support.
9
10
```java { .api }
11
/**
12
* PlatformTransactionManager implementation for single JDBC DataSource
13
* Provides declarative and programmatic transaction management
14
*/
15
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
16
implements ResourceTransactionManager, InitializingBean {
17
18
/** Create transaction manager with DataSource */
19
public DataSourceTransactionManager();
20
public DataSourceTransactionManager(DataSource dataSource);
21
22
// Configuration
23
public void setDataSource(DataSource dataSource);
24
public DataSource getDataSource();
25
public void setEnforceReadOnly(boolean enforceReadOnly);
26
public void setValidateExistingTransaction(boolean validateExistingTransaction);
27
public void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure);
28
29
// Resource management
30
public Object getResourceFactory();
31
protected Object doGetTransaction();
32
protected void doBegin(Object transaction, TransactionDefinition definition);
33
protected void doCommit(DefaultTransactionStatus status);
34
protected void doRollback(DefaultTransactionStatus status);
35
}
36
```
37
38
**Usage Examples:**
39
40
```java
41
// Basic transaction manager configuration
42
@Configuration
43
@EnableTransactionManagement
44
public class TransactionConfig {
45
46
@Bean
47
public DataSource dataSource() {
48
DriverManagerDataSource dataSource = new DriverManagerDataSource();
49
dataSource.setUrl("jdbc:postgresql://localhost:5432/mydb");
50
dataSource.setUsername("user");
51
dataSource.setPassword("password");
52
return dataSource;
53
}
54
55
@Bean
56
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
57
DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);
58
txManager.setEnforceReadOnly(true); // Optimize read-only transactions
59
txManager.setValidateExistingTransaction(true); // Strict validation
60
return txManager;
61
}
62
63
@Bean
64
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
65
return new JdbcTemplate(dataSource);
66
}
67
}
68
69
// Service with declarative transactions
70
@Service
71
@Transactional
72
public class UserService {
73
74
@Autowired
75
private JdbcTemplate jdbcTemplate;
76
77
@Transactional(readOnly = true)
78
public User findById(Long userId) {
79
return jdbcTemplate.queryForObject(
80
"SELECT * FROM users WHERE id = ?",
81
new BeanPropertyRowMapper<>(User.class),
82
userId
83
);
84
}
85
86
@Transactional
87
public User createUser(User user) {
88
KeyHolder keyHolder = new GeneratedKeyHolder();
89
jdbcTemplate.update(connection -> {
90
PreparedStatement ps = connection.prepareStatement(
91
"INSERT INTO users (name, email, department) VALUES (?, ?, ?)",
92
Statement.RETURN_GENERATED_KEYS
93
);
94
ps.setString(1, user.getName());
95
ps.setString(2, user.getEmail());
96
ps.setString(3, user.getDepartment());
97
return ps;
98
}, keyHolder);
99
100
user.setId(keyHolder.getKey().longValue());
101
102
// Log user creation (same transaction)
103
jdbcTemplate.update(
104
"INSERT INTO audit_log (user_id, action, timestamp) VALUES (?, ?, ?)",
105
user.getId(), "USER_CREATED", Timestamp.from(Instant.now())
106
);
107
108
return user;
109
}
110
111
@Transactional(rollbackFor = Exception.class, timeout = 30)
112
public void transferUsersBetweenDepartments(String fromDept, String toDept, int maxUsers) {
113
List<Long> userIds = jdbcTemplate.queryForList(
114
"SELECT id FROM users WHERE department = ? LIMIT ?",
115
Long.class, fromDept, maxUsers
116
);
117
118
for (Long userId : userIds) {
119
jdbcTemplate.update(
120
"UPDATE users SET department = ?, updated_at = ? WHERE id = ?",
121
toDept, Timestamp.from(Instant.now()), userId
122
);
123
124
// Log each transfer
125
jdbcTemplate.update(
126
"INSERT INTO audit_log (user_id, action, details, timestamp) VALUES (?, ?, ?, ?)",
127
userId, "DEPARTMENT_TRANSFER",
128
fromDept + " -> " + toDept, Timestamp.from(Instant.now())
129
);
130
}
131
}
132
}
133
```
134
135
### Programmatic Transaction Management
136
137
Using TransactionTemplate and PlatformTransactionManager directly for programmatic control.
138
139
```java { .api }
140
/**
141
* Template for programmatic transaction management
142
*/
143
public class TransactionTemplate extends DefaultTransactionDefinition
144
implements TransactionOperations, InitializingBean {
145
146
public TransactionTemplate();
147
public TransactionTemplate(PlatformTransactionManager transactionManager);
148
public TransactionTemplate(PlatformTransactionManager transactionManager,
149
TransactionDefinition transactionDefinition);
150
151
public void setPropagationBehavior(int propagationBehavior);
152
public void setIsolationLevel(int isolationLevel);
153
public void setTimeout(int timeout);
154
public void setReadOnly(boolean readOnly);
155
156
public <T> T execute(TransactionCallback<T> action) throws TransactionException;
157
public void executeWithoutResult(TransactionCallbackWithoutResult action) throws TransactionException;
158
}
159
160
/**
161
* Callback interface for programmatic transaction code
162
*/
163
@FunctionalInterface
164
public interface TransactionCallback<T> {
165
T doInTransaction(TransactionStatus status) throws TransactionException;
166
}
167
168
/**
169
* Convenience abstract class for TransactionCallback without return value
170
*/
171
public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {
172
public final Object doInTransaction(TransactionStatus status) throws TransactionException {
173
doInTransactionWithoutResult(status);
174
return null;
175
}
176
177
protected abstract void doInTransactionWithoutResult(TransactionStatus status)
178
throws TransactionException;
179
}
180
```
181
182
**Usage Examples:**
183
184
```java
185
@Service
186
public class ProgrammaticTransactionService {
187
188
private final TransactionTemplate transactionTemplate;
189
private final JdbcTemplate jdbcTemplate;
190
191
public ProgrammaticTransactionService(
192
PlatformTransactionManager transactionManager,
193
JdbcTemplate jdbcTemplate) {
194
this.transactionTemplate = new TransactionTemplate(transactionManager);
195
this.jdbcTemplate = jdbcTemplate;
196
}
197
198
public User createUserWithAudit(User user) {
199
return transactionTemplate.execute(status -> {
200
// Insert user
201
KeyHolder keyHolder = new GeneratedKeyHolder();
202
jdbcTemplate.update(connection -> {
203
PreparedStatement ps = connection.prepareStatement(
204
"INSERT INTO users (name, email) VALUES (?, ?)",
205
Statement.RETURN_GENERATED_KEYS
206
);
207
ps.setString(1, user.getName());
208
ps.setString(2, user.getEmail());
209
return ps;
210
}, keyHolder);
211
212
user.setId(keyHolder.getKey().longValue());
213
214
// Create audit record
215
jdbcTemplate.update(
216
"INSERT INTO audit_log (user_id, action) VALUES (?, ?)",
217
user.getId(), "USER_CREATED"
218
);
219
220
return user;
221
});
222
}
223
224
public void performBatchOperationWithCheckpoints(List<BatchOperation> operations) {
225
transactionTemplate.executeWithoutResult(status -> {
226
for (int i = 0; i < operations.size(); i++) {
227
BatchOperation op = operations.get(i);
228
229
try {
230
executeOperation(op);
231
} catch (Exception e) {
232
if (i > operations.size() / 2) {
233
// Rollback if more than halfway through
234
status.setRollbackOnly();
235
throw new RuntimeException("Critical failure in batch", e);
236
} else {
237
// Continue processing, log error
238
logError("Operation failed, continuing", e);
239
}
240
}
241
242
// Create savepoint every 100 operations
243
if (i % 100 == 0) {
244
Object savepoint = status.createSavepoint();
245
// Store savepoint reference if needed
246
}
247
}
248
});
249
}
250
251
public void performComplexTransaction() {
252
// Custom transaction configuration
253
TransactionTemplate customTemplate = new TransactionTemplate(transactionManager);
254
customTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
255
customTemplate.setTimeout(60); // 60 second timeout
256
customTemplate.setReadOnly(false);
257
customTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
258
259
customTemplate.executeWithoutResult(status -> {
260
// Complex business logic requiring serializable isolation
261
performSerializableOperations();
262
});
263
}
264
}
265
```
266
267
### Connection and Resource Management
268
269
Low-level transaction support classes for connection management and resource binding.
270
271
```java { .api }
272
/**
273
* Base class for JDBC transaction objects
274
*/
275
public abstract class JdbcTransactionObjectSupport
276
implements SavepointManager, SmartTransactionObject {
277
278
public void setConnectionHolder(ConnectionHolder connectionHolder);
279
public ConnectionHolder getConnectionHolder();
280
public boolean hasConnectionHolder();
281
public void setSavepointAllowed(boolean savepointAllowed);
282
public boolean isSavepointAllowed();
283
284
// SavepointManager implementation
285
public Object createSavepoint() throws TransactionException;
286
public void rollbackToSavepoint(Object savepoint) throws TransactionException;
287
public void releaseSavepoint(Object savepoint) throws TransactionException;
288
}
289
290
/**
291
* Resource holder wrapping JDBC Connection
292
*/
293
public class ConnectionHolder extends ResourceHolderSupport {
294
public ConnectionHolder(Connection connection);
295
public ConnectionHolder(ConnectionHandle connectionHandle);
296
297
public Connection getConnection();
298
public void setConnection(Connection connection);
299
public boolean supportsSavepoints();
300
public Object createSavepoint() throws SQLException;
301
public void rollbackToSavepoint(Object savepoint) throws SQLException;
302
public void releaseSavepoint(Object savepoint) throws SQLException;
303
}
304
305
/**
306
* Transaction-aware DataSource proxy
307
*/
308
public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
309
public TransactionAwareDataSourceProxy();
310
public TransactionAwareDataSourceProxy(DataSource targetDataSource);
311
312
public void setReobtainTransactionalConnections(boolean reobtainTransactionalConnections);
313
public boolean shouldObtainFixedConnection(Connection con);
314
}
315
```
316
317
**Usage Examples:**
318
319
```java
320
// Manual transaction management (advanced usage)
321
public class LowLevelTransactionService {
322
323
private final DataSource dataSource;
324
private final PlatformTransactionManager transactionManager;
325
326
public LowLevelTransactionService(DataSource dataSource,
327
PlatformTransactionManager transactionManager) {
328
this.dataSource = dataSource;
329
this.transactionManager = transactionManager;
330
}
331
332
public void performManualTransaction() {
333
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
334
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
335
def.setTimeout(30);
336
337
TransactionStatus status = transactionManager.getTransaction(def);
338
339
try {
340
Connection con = DataSourceUtils.getConnection(dataSource);
341
342
// Create savepoint
343
Object savepoint = status.createSavepoint();
344
345
try {
346
// Risky operation
347
executeRiskyOperation(con);
348
} catch (Exception e) {
349
// Rollback to savepoint
350
status.rollbackToSavepoint(savepoint);
351
executeAlternativeOperation(con);
352
} finally {
353
// Release savepoint
354
status.releaseSavepoint(savepoint);
355
}
356
357
transactionManager.commit(status);
358
} catch (Exception e) {
359
transactionManager.rollback(status);
360
throw new RuntimeException("Transaction failed", e);
361
}
362
}
363
}
364
365
// Transaction-aware DataSource setup
366
@Configuration
367
public class TransactionAwareConfig {
368
369
@Bean
370
public DataSource actualDataSource() {
371
return new DriverManagerDataSource(/*...*/);
372
}
373
374
@Bean
375
public DataSource dataSource(DataSource actualDataSource) {
376
TransactionAwareDataSourceProxy proxy =
377
new TransactionAwareDataSourceProxy(actualDataSource);
378
proxy.setReobtainTransactionalConnections(true);
379
return proxy;
380
}
381
382
@Bean
383
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
384
return new DataSourceTransactionManager(dataSource);
385
}
386
}
387
```
388
389
### Transaction Utilities
390
391
Utility methods for transaction status checking and resource management.
392
393
```java { .api }
394
/**
395
* Utility methods for DataSource access in transactional context
396
*/
397
public abstract class DataSourceUtils {
398
// Connection management
399
public static Connection getConnection(DataSource dataSource)
400
throws CannotGetJdbcConnectionException;
401
public static void releaseConnection(Connection con, DataSource dataSource);
402
403
// Transaction status
404
public static boolean isConnectionTransactional(Connection con, DataSource dataSource);
405
406
// Transaction timeout support
407
public static void applyTransactionTimeout(Statement stmt, DataSource dataSource)
408
throws SQLException;
409
public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout)
410
throws SQLException;
411
412
// Connection preparation for transactions
413
public static Integer prepareConnectionForTransaction(Connection con,
414
TransactionDefinition definition) throws SQLException;
415
public static void resetConnectionAfterTransaction(Connection con,
416
Integer previousIsolationLevel, boolean resetReadOnly);
417
}
418
419
/**
420
* Transaction synchronization utilities
421
*/
422
public abstract class TransactionSynchronizationManager {
423
// Resource binding
424
public static void bindResource(Object key, Object resource);
425
public static Object unbindResource(Object key);
426
public static Object getResource(Object key);
427
public static boolean hasResource(Object key);
428
429
// Transaction status
430
public static boolean isSynchronizationActive();
431
public static boolean isCurrentTransactionReadOnly();
432
public static String getCurrentTransactionName();
433
}
434
```
435
436
**Usage Examples:**
437
438
```java
439
// Custom resource management
440
@Component
441
public class CustomResourceManager {
442
443
public void performOperationWithCustomResource() {
444
// Check if we're in a transaction
445
if (TransactionSynchronizationManager.isSynchronizationActive()) {
446
// We're in a transaction, use transactional resource
447
CustomResource resource = getTransactionalResource();
448
try {
449
resource.performOperation();
450
} finally {
451
// Resource will be cleaned up by transaction synchronization
452
}
453
} else {
454
// No transaction, manage resource manually
455
CustomResource resource = createResource();
456
try {
457
resource.performOperation();
458
} finally {
459
resource.cleanup();
460
}
461
}
462
}
463
464
private CustomResource getTransactionalResource() {
465
CustomResource resource = (CustomResource)
466
TransactionSynchronizationManager.getResource("customResource");
467
468
if (resource == null) {
469
resource = createResource();
470
TransactionSynchronizationManager.bindResource("customResource", resource);
471
472
// Register synchronization for cleanup
473
TransactionSynchronizationManager.registerSynchronization(
474
new TransactionSynchronization() {
475
@Override
476
public void afterCompletion(int status) {
477
CustomResource res = (CustomResource)
478
TransactionSynchronizationManager.unbindResource("customResource");
479
if (res != null) {
480
res.cleanup();
481
}
482
}
483
}
484
);
485
}
486
487
return resource;
488
}
489
}
490
491
// Transaction status checking
492
@Service
493
public class TransactionAwareService {
494
495
private final JdbcTemplate jdbcTemplate;
496
497
public TransactionAwareService(JdbcTemplate jdbcTemplate) {
498
this.jdbcTemplate = jdbcTemplate;
499
}
500
501
public void performOptimizedOperation() {
502
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
503
// Use read-only optimized query
504
performReadOnlyQuery();
505
} else {
506
// Use regular query that can handle writes
507
performRegularQuery();
508
}
509
}
510
511
public void logTransactionInfo() {
512
if (TransactionSynchronizationManager.isSynchronizationActive()) {
513
String txName = TransactionSynchronizationManager.getCurrentTransactionName();
514
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
515
516
log.info("Transaction active: name={}, readOnly={}", txName, readOnly);
517
} else {
518
log.info("No active transaction");
519
}
520
}
521
}
522
```
523
524
## Core Interfaces
525
526
```java { .api }
527
/**
528
* Primary transaction manager for single DataSource
529
*/
530
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
531
implements ResourceTransactionManager {
532
533
public DataSourceTransactionManager(DataSource dataSource);
534
public void setDataSource(DataSource dataSource);
535
public void setEnforceReadOnly(boolean enforceReadOnly);
536
}
537
538
/**
539
* Template for programmatic transaction management
540
*/
541
public class TransactionTemplate implements TransactionOperations {
542
public TransactionTemplate(PlatformTransactionManager transactionManager);
543
public <T> T execute(TransactionCallback<T> action) throws TransactionException;
544
public void executeWithoutResult(TransactionCallbackWithoutResult action) throws TransactionException;
545
}
546
547
/**
548
* Callback interface for transactional code
549
*/
550
@FunctionalInterface
551
public interface TransactionCallback<T> {
552
T doInTransaction(TransactionStatus status) throws TransactionException;
553
}
554
555
/**
556
* Base class for JDBC transaction objects
557
*/
558
public abstract class JdbcTransactionObjectSupport implements SavepointManager, SmartTransactionObject {
559
public void setConnectionHolder(ConnectionHolder connectionHolder);
560
public ConnectionHolder getConnectionHolder();
561
public Object createSavepoint() throws TransactionException;
562
public void rollbackToSavepoint(Object savepoint) throws TransactionException;
563
}
564
```