or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-transaction-management.mddao-exceptions.mddeclarative-transactions.mdindex.mdjta-integration.mdprogrammatic-transactions.mdreactive-transactions.mdtransaction-synchronization.mdtransactional-events.md

programmatic-transactions.mddocs/

0

# Programmatic Transaction Management

1

2

Spring's programmatic transaction management provides explicit control over transaction boundaries through template classes and direct API usage, ideal for scenarios requiring fine-grained transaction control.

3

4

## TransactionTemplate

5

6

Template class for programmatic transaction management with consistent exception handling and resource management.

7

8

```java { .api }

9

public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations {

10

11

/**

12

* Construct a new TransactionTemplate using the given transaction manager.

13

* @param transactionManager the transaction manager to use

14

*/

15

public TransactionTemplate(PlatformTransactionManager transactionManager);

16

17

/**

18

* Construct a new TransactionTemplate using the given transaction manager,

19

* taking its default settings from the given transaction definition.

20

* @param transactionManager the transaction manager to use

21

* @param transactionDefinition transaction definition to copy from

22

*/

23

public TransactionTemplate(PlatformTransactionManager transactionManager,

24

TransactionDefinition transactionDefinition);

25

26

/**

27

* Set the transaction manager.

28

* @param transactionManager the transaction manager

29

*/

30

public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager);

31

32

/**

33

* Return the transaction manager.

34

* @return the transaction manager

35

*/

36

@Nullable

37

public PlatformTransactionManager getTransactionManager();

38

39

/**

40

* Execute the action specified by the given callback object within a transaction.

41

* @param action callback object that specifies the transactional action

42

* @return result object returned by the callback, or null if none

43

* @throws TransactionException in case of transaction failure

44

*/

45

@Override

46

@Nullable

47

public <T> T execute(TransactionCallback<T> action) throws TransactionException;

48

49

/**

50

* Execute the action specified by the given callback object within a transaction.

51

* @param action callback object that specifies the transactional action

52

* @throws TransactionException in case of transaction failure

53

*/

54

public void executeWithoutResult(TransactionCallbackWithoutResult action)

55

throws TransactionException;

56

}

57

```

58

59

## Transaction Operations Interface

60

61

```java { .api }

62

public interface TransactionOperations {

63

/**

64

* Execute the action specified by the given callback object within a transaction.

65

* @param action callback object that specifies the transactional action

66

* @return result object returned by the callback

67

* @throws TransactionException in case of transaction failure

68

*/

69

@Nullable

70

<T> T execute(TransactionCallback<T> action) throws TransactionException;

71

}

72

```

73

74

## Callback Interfaces

75

76

### TransactionCallback

77

78

Functional interface for transaction callbacks with return values.

79

80

```java { .api }

81

@FunctionalInterface

82

public interface TransactionCallback<T> {

83

/**

84

* Gets called by TransactionTemplate.execute within a transactional context.

85

* @param status associated transaction status

86

* @return result object, or null if none

87

*/

88

@Nullable

89

T doInTransaction(TransactionStatus status);

90

}

91

```

92

93

### TransactionCallbackWithoutResult

94

95

Abstract convenience class for transaction callbacks without return values.

96

97

```java { .api }

98

public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {

99

100

@Override

101

@Nullable

102

public final Object doInTransaction(TransactionStatus status) {

103

doInTransactionWithoutResult(status);

104

return null;

105

}

106

107

/**

108

* Gets called by TransactionTemplate.execute within a transactional context.

109

* @param status associated transaction status

110

*/

111

protected abstract void doInTransactionWithoutResult(TransactionStatus status);

112

}

113

```

114

115

## Usage Examples

116

117

### Basic TransactionTemplate Usage

118

119

```java

120

@Service

121

public class AccountService {

122

123

private final TransactionTemplate transactionTemplate;

124

private final AccountRepository accountRepository;

125

private final AuditRepository auditRepository;

126

127

public AccountService(PlatformTransactionManager transactionManager,

128

AccountRepository accountRepository,

129

AuditRepository auditRepository) {

130

this.transactionTemplate = new TransactionTemplate(transactionManager);

131

this.accountRepository = accountRepository;

132

this.auditRepository = auditRepository;

133

}

134

135

public Account transferMoney(Long fromId, Long toId, BigDecimal amount) {

136

return transactionTemplate.execute(status -> {

137

Account fromAccount = accountRepository.findById(fromId)

138

.orElseThrow(() -> new AccountNotFoundException(fromId));

139

Account toAccount = accountRepository.findById(toId)

140

.orElseThrow(() -> new AccountNotFoundException(toId));

141

142

if (fromAccount.getBalance().compareTo(amount) < 0) {

143

throw new InsufficientFundsException(fromId, amount);

144

}

145

146

fromAccount.debit(amount);

147

toAccount.credit(amount);

148

149

accountRepository.save(fromAccount);

150

accountRepository.save(toAccount);

151

152

// Audit the transaction

153

auditRepository.save(new AuditRecord("TRANSFER", fromId, toId, amount));

154

155

return fromAccount;

156

});

157

}

158

}

159

```

160

161

### TransactionTemplate with Custom Settings

162

163

```java

164

@Service

165

public class OrderService {

166

167

private final TransactionTemplate readOnlyTemplate;

168

private final TransactionTemplate writeTemplate;

169

private final TransactionTemplate longRunningTemplate;

170

171

public OrderService(PlatformTransactionManager transactionManager) {

172

// Read-only template for queries

173

this.readOnlyTemplate = new TransactionTemplate(transactionManager);

174

readOnlyTemplate.setReadOnly(true);

175

readOnlyTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);

176

177

// Standard write template

178

this.writeTemplate = new TransactionTemplate(transactionManager);

179

writeTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

180

writeTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);

181

182

// Long-running operations template

183

this.longRunningTemplate = new TransactionTemplate(transactionManager);

184

longRunningTemplate.setTimeout(300); // 5 minutes

185

longRunningTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

186

}

187

188

public List<Order> findOrdersByStatus(OrderStatus status) {

189

return readOnlyTemplate.execute(transactionStatus ->

190

orderRepository.findByStatus(status));

191

}

192

193

public Order createOrder(Order order) {

194

return writeTemplate.execute(status -> {

195

// Validate order

196

validateOrder(order);

197

198

// Save order

199

Order savedOrder = orderRepository.save(order);

200

201

// Reserve inventory

202

inventoryService.reserveItems(order.getItems());

203

204

// Send confirmation

205

notificationService.sendOrderConfirmation(savedOrder);

206

207

return savedOrder;

208

});

209

}

210

211

public void processBatchOrders(List<Order> orders) {

212

longRunningTemplate.executeWithoutResult(status -> {

213

for (Order order : orders) {

214

try {

215

processOrder(order);

216

} catch (Exception e) {

217

// Log error but continue with other orders

218

log.error("Failed to process order {}", order.getId(), e);

219

}

220

}

221

});

222

}

223

}

224

```

225

226

### Exception Handling and Rollback Control

227

228

```java

229

@Service

230

public class PaymentService {

231

232

private final TransactionTemplate transactionTemplate;

233

234

public PaymentResult processPayment(Payment payment) {

235

return transactionTemplate.execute(status -> {

236

try {

237

// Validate payment

238

validatePayment(payment);

239

240

// Charge credit card

241

ChargeResult chargeResult = creditCardService.charge(payment);

242

243

if (!chargeResult.isSuccessful()) {

244

// Business failure - rollback transaction

245

status.setRollbackOnly();

246

return PaymentResult.failed(chargeResult.getErrorMessage());

247

}

248

249

// Save payment record

250

Payment savedPayment = paymentRepository.save(payment);

251

252

// Send receipt

253

receiptService.sendReceipt(savedPayment);

254

255

return PaymentResult.successful(savedPayment);

256

257

} catch (ValidationException e) {

258

// Validation errors - rollback transaction

259

status.setRollbackOnly();

260

return PaymentResult.failed("Validation failed: " + e.getMessage());

261

262

} catch (CreditCardException e) {

263

// Card processing errors - rollback transaction

264

status.setRollbackOnly();

265

return PaymentResult.failed("Card processing failed: " + e.getMessage());

266

267

} catch (Exception e) {

268

// Unexpected errors - transaction will rollback automatically

269

log.error("Unexpected error processing payment", e);

270

throw e;

271

}

272

});

273

}

274

}

275

```

276

277

### Nested Transactions with TransactionTemplate

278

279

```java

280

@Service

281

public class ComplexOrderService {

282

283

private final TransactionTemplate outerTemplate;

284

private final TransactionTemplate innerTemplate;

285

286

public ComplexOrderService(PlatformTransactionManager transactionManager) {

287

this.outerTemplate = new TransactionTemplate(transactionManager);

288

outerTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

289

290

this.innerTemplate = new TransactionTemplate(transactionManager);

291

innerTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

292

}

293

294

public Order processComplexOrder(Order order) {

295

return outerTemplate.execute(outerStatus -> {

296

// Main order processing in outer transaction

297

Order savedOrder = orderRepository.save(order);

298

299

// Process each item in separate transactions

300

for (OrderItem item : order.getItems()) {

301

try {

302

processItemInSeparateTransaction(item);

303

} catch (ItemProcessingException e) {

304

// Item processing failures don't affect main order

305

log.warn("Failed to process item {}, continuing with order",

306

item.getId(), e);

307

item.setStatus(ItemStatus.FAILED);

308

item.setErrorMessage(e.getMessage());

309

}

310

}

311

312

// Update order with final status

313

savedOrder.updateStatus();

314

return orderRepository.save(savedOrder);

315

});

316

}

317

318

private void processItemInSeparateTransaction(OrderItem item) {

319

innerTemplate.executeWithoutResult(innerStatus -> {

320

// This runs in separate transaction

321

inventoryService.reserveItem(item);

322

shippingService.scheduleShipment(item);

323

item.setStatus(ItemStatus.PROCESSED);

324

});

325

}

326

}

327

```

328

329

### Direct PlatformTransactionManager Usage

330

331

For maximum control, you can use the PlatformTransactionManager directly:

332

333

```java

334

@Service

335

public class LowLevelTransactionService {

336

337

private final PlatformTransactionManager transactionManager;

338

339

public LowLevelTransactionService(PlatformTransactionManager transactionManager) {

340

this.transactionManager = transactionManager;

341

}

342

343

public void performComplexOperation() {

344

DefaultTransactionDefinition def = new DefaultTransactionDefinition();

345

def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

346

def.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);

347

def.setTimeout(30);

348

def.setName("ComplexOperation");

349

350

TransactionStatus status = transactionManager.getTransaction(def);

351

352

try {

353

// Perform operations

354

performStep1();

355

356

// Create savepoint

357

Object savepoint = status.createSavepoint();

358

359

try {

360

performRiskyStep2();

361

} catch (RiskyOperationException e) {

362

// Rollback to savepoint

363

status.rollbackToSavepoint(savepoint);

364

performAlternativeStep2();

365

} finally {

366

// Release savepoint

367

status.releaseSavepoint(savepoint);

368

}

369

370

performStep3();

371

372

// Commit transaction

373

transactionManager.commit(status);

374

375

} catch (Exception e) {

376

// Rollback on any error

377

transactionManager.rollback(status);

378

throw e;

379

}

380

}

381

}

382

```

383

384

### Multiple Transaction Managers

385

386

```java

387

@Service

388

public class MultiDataSourceService {

389

390

private final TransactionTemplate primaryTemplate;

391

private final TransactionTemplate secondaryTemplate;

392

393

public MultiDataSourceService(

394

@Qualifier("primaryTransactionManager") PlatformTransactionManager primaryTxManager,

395

@Qualifier("secondaryTransactionManager") PlatformTransactionManager secondaryTxManager) {

396

397

this.primaryTemplate = new TransactionTemplate(primaryTxManager);

398

this.secondaryTemplate = new TransactionTemplate(secondaryTxManager);

399

}

400

401

public void synchronizeData(SyncData data) {

402

// Save to primary database

403

User user = primaryTemplate.execute(status -> {

404

User savedUser = primaryUserRepository.save(data.getUser());

405

// Other primary DB operations

406

return savedUser;

407

});

408

409

// Save to secondary database in separate transaction

410

secondaryTemplate.executeWithoutResult(status -> {

411

UserProfile profile = new UserProfile(user);

412

secondaryUserProfileRepository.save(profile);

413

// Other secondary DB operations

414

});

415

}

416

}

417

```

418

419

## Best Practices

420

421

### Error Handling Patterns

422

423

```java

424

public class TransactionBestPractices {

425

426

private final TransactionTemplate transactionTemplate;

427

428

// Pattern 1: Return success/failure indicators

429

public OperationResult performOperation(OperationData data) {

430

return transactionTemplate.execute(status -> {

431

try {

432

// Perform operation

433

performBusinessLogic(data);

434

return OperationResult.success();

435

} catch (BusinessException e) {

436

// Business logic failure - rollback

437

status.setRollbackOnly();

438

return OperationResult.failure(e.getMessage());

439

}

440

// Runtime exceptions will cause automatic rollback

441

});

442

}

443

444

// Pattern 2: Use Optional for null handling

445

public Optional<User> findAndUpdateUser(Long userId, UserUpdate update) {

446

return transactionTemplate.execute(status -> {

447

return userRepository.findById(userId)

448

.map(user -> {

449

user.apply(update);

450

return userRepository.save(user);

451

});

452

});

453

}

454

455

// Pattern 3: Collect and return multiple results

456

public BatchResult processBatch(List<BatchItem> items) {

457

return transactionTemplate.execute(status -> {

458

List<BatchItem> processed = new ArrayList<>();

459

List<String> errors = new ArrayList<>();

460

461

for (BatchItem item : items) {

462

try {

463

BatchItem result = processItem(item);

464

processed.add(result);

465

} catch (ItemProcessingException e) {

466

errors.add("Item " + item.getId() + ": " + e.getMessage());

467

}

468

}

469

470

return new BatchResult(processed, errors);

471

});

472

}

473

}

474

```