or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-template.mddatasource-management.mdexception-handling.mdfluent-client.mdindex.mdnamed-parameter.mdsimple-operations.mdtransaction-management.md

transaction-management.mddocs/

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

```