0
# Transaction Semantics and Execution
1
2
Advanced transaction execution with customizable propagation behaviors, exception handling, timeout control, and lambda-style execution patterns.
3
4
## Capabilities
5
6
### TransactionSemantics Enum
7
8
Controls transaction behavior in the presence or absence of existing transactions.
9
10
```java { .api }
11
/**
12
* Enum controlling transaction propagation behavior
13
*/
14
enum TransactionSemantics {
15
16
/**
17
* Throws exception if transaction already exists
18
* Otherwise creates new transaction
19
*/
20
DISALLOW_EXISTING,
21
22
/**
23
* Joins existing transaction or creates new one
24
* Exception handler affects existing transaction differently than new
25
*/
26
JOIN_EXISTING,
27
28
/**
29
* Always creates new transaction
30
* Suspends existing transaction if present, resumes after completion
31
*/
32
REQUIRE_NEW,
33
34
/**
35
* Suspends existing transaction and runs without transaction
36
* No-op if no existing transaction
37
* Exception handler cannot be used with this semantic
38
*/
39
SUSPEND_EXISTING
40
}
41
```
42
43
**Usage Examples:**
44
45
```java
46
import io.quarkus.narayana.jta.QuarkusTransaction;
47
import io.quarkus.narayana.jta.TransactionSemantics;
48
49
@ApplicationScoped
50
public class OrderProcessingService {
51
52
public void processOrderWithAudit(Order order) {
53
// Main processing in existing or new transaction
54
QuarkusTransaction.runner(TransactionSemantics.JOIN_EXISTING)
55
.run(() -> {
56
validateOrder(order);
57
updateInventory(order);
58
chargeCustomer(order);
59
});
60
61
// Audit logging in separate transaction
62
QuarkusTransaction.runner(TransactionSemantics.REQUIRE_NEW)
63
.run(() -> {
64
auditRepository.log("Order processed: " + order.getId());
65
});
66
}
67
68
public void sensitiveOperation() {
69
// Must run without any existing transaction
70
QuarkusTransaction.runner(TransactionSemantics.DISALLOW_EXISTING)
71
.run(() -> {
72
performSecurityCheck();
73
updateSecurityLog();
74
});
75
}
76
77
public void sendNotification(String message) {
78
// Run outside transaction scope entirely
79
QuarkusTransaction.runner(TransactionSemantics.SUSPEND_EXISTING)
80
.run(() -> {
81
emailService.send(message);
82
// This runs without any transaction, even if called
83
// from within a transactional method
84
});
85
}
86
}
87
```
88
89
### TransactionRunner Interface
90
91
Base interface for executing tasks with transaction semantics.
92
93
```java { .api }
94
/**
95
* Interface for running tasks with specific transaction semantics
96
*/
97
interface TransactionRunner {
98
99
/**
100
* Execute runnable with selected transaction semantics
101
* @param task Task to execute
102
*/
103
void run(Runnable task);
104
105
/**
106
* Execute callable with selected transaction semantics
107
* Checked exceptions are wrapped in QuarkusTransactionException
108
* @param task Task to execute
109
* @return Value returned by task
110
* @throws QuarkusTransactionException if task throws checked exception
111
*/
112
<T> T call(Callable<T> task);
113
}
114
```
115
116
### TransactionRunnerOptions Interface
117
118
Enhanced transaction runner with configuration options.
119
120
```java { .api }
121
/**
122
* Builder interface for configuring transaction runners
123
* Extends TransactionRunner so it can execute tasks directly
124
*/
125
interface TransactionRunnerOptions extends TransactionRunner {
126
127
/**
128
* Set transaction timeout for this execution
129
* @param seconds Timeout in seconds, 0 for system default
130
* @return This builder for method chaining
131
* @throws IllegalArgumentException if seconds is negative
132
*/
133
TransactionRunnerOptions timeout(int seconds);
134
135
/**
136
* Set exception handler for controlling commit/rollback behavior
137
* Handler is called when task throws exception to decide transaction fate
138
* Exception is still propagated to caller after handler executes
139
* Cannot be used with SUSPEND_EXISTING semantics
140
*
141
* @param handler Function returning COMMIT or ROLLBACK decision
142
* @return This builder for method chaining
143
*/
144
TransactionRunnerOptions exceptionHandler(
145
Function<Throwable, TransactionExceptionResult> handler
146
);
147
148
// Inherited from TransactionRunner
149
void run(Runnable task);
150
<T> T call(Callable<T> task);
151
}
152
```
153
154
**Usage Examples:**
155
156
```java
157
import io.quarkus.narayana.jta.TransactionExceptionResult;
158
159
@ApplicationScoped
160
public class PaymentProcessor {
161
162
public boolean processPayment(Payment payment) {
163
return QuarkusTransaction.requiringNew()
164
.timeout(30) // 30 second timeout
165
.exceptionHandler(throwable -> {
166
// Custom exception handling logic
167
if (throwable instanceof InsufficientFundsException) {
168
return TransactionExceptionResult.ROLLBACK;
169
} else if (throwable instanceof PaymentWarningException) {
170
return TransactionExceptionResult.COMMIT; // Continue despite warning
171
}
172
return TransactionExceptionResult.ROLLBACK; // Default to rollback
173
})
174
.call(() -> {
175
validatePayment(payment);
176
chargeCard(payment);
177
updateAccount(payment);
178
return true; // Success
179
});
180
}
181
182
public void performBatchOperation(List<Operation> operations) {
183
QuarkusTransaction.joiningExisting()
184
.timeout(300) // 5 minute timeout for batch
185
.run(() -> {
186
for (Operation op : operations) {
187
processOperation(op);
188
}
189
});
190
}
191
}
192
```
193
194
### TransactionExceptionResult Enum
195
196
Controls transaction outcome based on exception handling.
197
198
```java { .api }
199
/**
200
* Decision enum for exception handler results
201
*/
202
enum TransactionExceptionResult {
203
/** Transaction should be committed despite exception */
204
COMMIT,
205
206
/** Transaction should be rolled back due to exception */
207
ROLLBACK
208
}
209
```
210
211
**Advanced Exception Handling:**
212
213
```java
214
@ApplicationScoped
215
public class DataMigrationService {
216
217
public void migrateDataWithRecovery(List<DataRecord> records) {
218
QuarkusTransaction.requiringNew()
219
.exceptionHandler(this::handleMigrationException)
220
.run(() -> {
221
for (DataRecord record : records) {
222
try {
223
migrateRecord(record);
224
} catch (DataCorruptionException e) {
225
logCorruptedRecord(record, e);
226
// Continue with other records
227
}
228
}
229
});
230
}
231
232
private TransactionExceptionResult handleMigrationException(Throwable throwable) {
233
if (throwable instanceof DataCorruptionException) {
234
// Log but don't fail entire batch
235
logger.warn("Data corruption detected", throwable);
236
return TransactionExceptionResult.COMMIT;
237
} else if (throwable instanceof DatabaseConnectionException) {
238
// Critical failure, rollback everything
239
logger.error("Database connection failed", throwable);
240
return TransactionExceptionResult.ROLLBACK;
241
} else if (throwable instanceof ValidationException) {
242
// Business logic error, rollback
243
return TransactionExceptionResult.ROLLBACK;
244
}
245
246
// Default to rollback for unknown exceptions
247
return TransactionExceptionResult.ROLLBACK;
248
}
249
}
250
```
251
252
### Semantic Behavior Details
253
254
#### JOIN_EXISTING Behavior
255
256
```java
257
public void demonstrateJoinExisting() {
258
// Scenario 1: No existing transaction
259
QuarkusTransaction.joiningExisting().run(() -> {
260
// New transaction created
261
// Exception handler affects this transaction directly
262
performWork();
263
});
264
265
// Scenario 2: Called within existing transaction
266
QuarkusTransaction.begin();
267
try {
268
QuarkusTransaction.joiningExisting()
269
.exceptionHandler(ex -> TransactionExceptionResult.ROLLBACK)
270
.run(() -> {
271
// Runs in existing transaction
272
// ROLLBACK result marks existing transaction as rollback-only
273
// COMMIT result takes no action on existing transaction
274
performWork();
275
});
276
QuarkusTransaction.commit(); // May fail if marked rollback-only
277
} catch (Exception e) {
278
QuarkusTransaction.rollback();
279
}
280
}
281
```
282
283
#### REQUIRE_NEW Behavior
284
285
```java
286
public void demonstrateRequireNew() {
287
QuarkusTransaction.begin();
288
try {
289
// This runs in the outer transaction
290
performInitialWork();
291
292
QuarkusTransaction.requiringNew().run(() -> {
293
// This runs in completely separate transaction
294
// Outer transaction is suspended
295
// Success/failure here doesn't affect outer transaction
296
performIndependentWork();
297
});
298
// Outer transaction resumed here
299
300
performFinalWork();
301
QuarkusTransaction.commit();
302
} catch (Exception e) {
303
QuarkusTransaction.rollback();
304
}
305
}
306
```
307
308
#### SUSPEND_EXISTING Behavior
309
310
```java
311
public void demonstrateSuspendExisting() {
312
QuarkusTransaction.begin();
313
try {
314
performTransactionalWork();
315
316
QuarkusTransaction.suspendingExisting().run(() -> {
317
// Runs completely outside any transaction
318
// Cannot use exception handler with this semantic
319
sendEmailNotification();
320
logToExternalSystem();
321
});
322
323
// Original transaction resumed
324
performMoreTransactionalWork();
325
QuarkusTransaction.commit();
326
} catch (Exception e) {
327
QuarkusTransaction.rollback();
328
}
329
}
330
```
331
332
### Timeout Configuration
333
334
```java
335
@ApplicationScoped
336
public class LongRunningOperationService {
337
338
public void performQuickOperation() {
339
QuarkusTransaction.requiringNew()
340
.timeout(10) // 10 seconds
341
.run(() -> {
342
quickDatabaseUpdate();
343
});
344
}
345
346
public void performBatchOperation() {
347
QuarkusTransaction.requiringNew()
348
.timeout(600) // 10 minutes
349
.run(() -> {
350
processBatchRecords();
351
});
352
}
353
354
public void performOperationWithDefaultTimeout() {
355
QuarkusTransaction.requiringNew()
356
.timeout(0) // Use system default timeout
357
.run(() -> {
358
performStandardOperation();
359
});
360
}
361
}
362
```
363
364
## Best Practices
365
366
### Choosing Transaction Semantics
367
368
```java
369
// ✅ Use REQUIRE_NEW for independent operations (auditing, logging)
370
QuarkusTransaction.requiringNew().run(() -> {
371
auditRepository.log("Operation completed");
372
});
373
374
// ✅ Use JOIN_EXISTING for operations that should be part of caller's transaction
375
QuarkusTransaction.joiningExisting().run(() -> {
376
updateRelatedData();
377
});
378
379
// ✅ Use SUSPEND_EXISTING for non-transactional operations (external APIs)
380
QuarkusTransaction.suspendingExisting().run(() -> {
381
callExternalAPI();
382
});
383
384
// ✅ Use DISALLOW_EXISTING for operations that must start fresh
385
QuarkusTransaction.disallowingExisting().run(() -> {
386
initializeSystemState();
387
});
388
```
389
390
### Exception Handler Guidelines
391
392
```java
393
// ✅ Good: Clear exception handling logic
394
.exceptionHandler(throwable -> {
395
if (throwable instanceof BusinessWarningException) {
396
logger.warn("Warning during processing", throwable);
397
return TransactionExceptionResult.COMMIT;
398
}
399
return TransactionExceptionResult.ROLLBACK;
400
})
401
402
// ❌ Avoid: Side effects in exception handler
403
.exceptionHandler(throwable -> {
404
// Don't do heavy work in exception handler
405
sendEmailAlert(); // ❌ Bad
406
return TransactionExceptionResult.ROLLBACK;
407
})
408
409
// ✅ Good: Exception handler focuses on transaction decision only
410
.exceptionHandler(throwable -> {
411
// Keep it simple and fast
412
return throwable instanceof WarningException ?
413
TransactionExceptionResult.COMMIT :
414
TransactionExceptionResult.ROLLBACK;
415
})
416
```
417
418
### Performance Considerations
419
420
```java
421
// ✅ Prefer declarative transactions for simple cases
422
@Transactional
423
public void simpleOperation() { }
424
425
// ✅ Use programmatic for complex logic
426
public void complexOperation() {
427
QuarkusTransaction.joiningExisting()
428
.timeout(customTimeout)
429
.exceptionHandler(this::handleComplexExceptions)
430
.run(() -> {
431
// Complex transaction logic
432
});
433
}
434
```