JTA transaction support for Quarkus applications with programmatic and declarative transaction management
—
Core programmatic API providing simplified transaction control with lambda-style execution, comprehensive semantic options, and automatic leak prevention.
The main interface for programmatic transaction management with simplified exception handling.
/**
* Simplified transaction interface with no checked exceptions and automatic leak prevention
*/
interface QuarkusTransaction {
/**
* Starts a transaction with system default timeout
* Transaction is tied to request scope and auto-rolled back if not committed
*/
static void begin();
/**
* Starts a transaction with custom options
* @param options Configuration options for the new transaction
*/
static void begin(BeginOptions options);
/** Commits the current transaction */
static void commit();
/** Rolls back the current transaction */
static void rollback();
/** Marks transaction as rollback only - cannot be committed */
static void setRollbackOnly();
/**
* Returns transaction status using Jakarta Transaction Status constants
* @return Status constant (STATUS_ACTIVE, STATUS_COMMITTED, etc.)
*/
static int getStatus();
/**
* Checks if transaction is marked for rollback only
* @return true if transaction cannot be committed
*/
static boolean isRollbackOnly();
/**
* @deprecated Use getStatus() instead - the name is misleading
* @since 3.22 - scheduled for removal
*/
@Deprecated
static boolean isActive();
}Usage Examples:
import io.quarkus.narayana.jta.QuarkusTransaction;
import jakarta.transaction.Status;
@ApplicationScoped
public class AccountService {
public void transferFunds(Account from, Account to, BigDecimal amount) {
QuarkusTransaction.begin();
try {
validateSufficientFunds(from, amount);
from.withdraw(amount);
to.deposit(amount);
// Check transaction status
if (QuarkusTransaction.getStatus() == Status.STATUS_ACTIVE) {
QuarkusTransaction.commit();
}
} catch (InsufficientFundsException e) {
QuarkusTransaction.rollback();
throw e;
} catch (Exception e) {
QuarkusTransaction.setRollbackOnly(); // Mark for rollback
throw new TransactionException("Transfer failed", e);
}
}
}Options for customizing transaction begin behavior.
/**
* Configuration options for beginning transactions
*/
class BeginOptions {
/**
* Automatically commit transaction when request scope ends
* By default, transactions are rolled back when request scope is destroyed
* @return This BeginOptions instance for chaining
*/
public BeginOptions commitOnRequestScopeEnd();
/**
* Sets transaction timeout for this specific transaction
* @param seconds Timeout in seconds, 0 for system default
* @return This BeginOptions instance for chaining
* @throws IllegalArgumentException if seconds is negative
*/
public BeginOptions timeout(int seconds);
}Usage Examples:
// Transaction with custom timeout
QuarkusTransaction.begin(
QuarkusTransaction.beginOptions().timeout(30)
);
// Transaction that auto-commits on request scope end
QuarkusTransaction.begin(
QuarkusTransaction.beginOptions()
.commitOnRequestScopeEnd()
.timeout(60)
);Factory methods for creating transaction runners with different semantic behaviors.
/**
* Creates transaction runner with REQUIRE_NEW semantics
* Always creates new transaction, suspending existing if present
*/
static TransactionRunnerOptions requiringNew();
/**
* Creates transaction runner with JOIN_EXISTING semantics
* Joins existing transaction or creates new one if none exists
*/
static TransactionRunnerOptions joiningExisting();
/**
* Creates transaction runner with DISALLOW_EXISTING semantics
* Throws exception if transaction already exists, otherwise creates new
*/
static TransactionRunnerOptions disallowingExisting();
/**
* Creates transaction runner with SUSPEND_EXISTING semantics
* Suspends existing transaction and runs outside transaction scope
*/
static TransactionRunnerOptions suspendingExisting();
/**
* Creates transaction runner with specified semantics
* @param semantics The transaction propagation behavior to use
*/
static TransactionRunnerOptions runner(TransactionSemantics semantics);Usage Examples:
// Always create new transaction
QuarkusTransaction.requiringNew().run(() -> {
// This runs in a new transaction regardless of existing transaction
auditRepository.logOperation("sensitive_operation");
});
// Join existing or create new
int result = QuarkusTransaction.joiningExisting()
.timeout(15)
.call(() -> {
return performDatabaseOperation();
});
// Run outside transaction scope
QuarkusTransaction.suspendingExisting().run(() -> {
// This runs without any transaction
sendEmailNotification();
});Exception types and handling for programmatic transaction management.
/**
* Runtime exception wrapping checked exceptions from transaction operations
*/
class QuarkusTransactionException extends RuntimeException {
public QuarkusTransactionException(Throwable cause);
public QuarkusTransactionException(String message);
public QuarkusTransactionException(String message, Throwable cause);
public QuarkusTransactionException(String message, Throwable cause,
boolean enableSuppression, boolean writableStackTrace);
}Error Scenarios:
try {
QuarkusTransaction.begin();
// ... transaction work
QuarkusTransaction.commit();
} catch (QuarkusTransactionException e) {
// Handles wrapped SystemException, NotSupportedException, etc.
logger.error("Transaction failed", e.getCause());
// Transaction automatically rolled back
}⚠️ Legacy programmatic methods - Use TransactionRunnerOptions instead.
/**
* @deprecated Use requiringNew().run(task) instead
* Scheduled for removal in future version
*/
@Deprecated
static void run(Runnable task);
/**
* @deprecated Use runner(semantics).run(task) instead
* Scheduled for removal in future version
*/
@Deprecated
static void run(RunOptions options, Runnable task);
/**
* @deprecated Use requiringNew().call(task) instead
* Scheduled for removal in future version
*/
@Deprecated
static <T> T call(Callable<T> task);
/**
* @deprecated Use runner(semantics).call(task) instead
* Scheduled for removal in future version
*/
@Deprecated
static <T> T call(RunOptions options, Callable<T> task);
/**
* @deprecated Use runner() factory methods instead
* Scheduled for removal in future version
*/
@Deprecated
static RunOptions runOptions();Migration Guide for Deprecated Methods:
// ❌ Old deprecated approach
QuarkusTransaction.run(() -> {
// transaction work
});
// ✅ New recommended approach
QuarkusTransaction.requiringNew().run(() -> {
// transaction work
});
// ❌ Old deprecated approach with options
QuarkusTransaction.run(QuarkusTransaction.runOptions().semantic(REQUIRE_NEW), () -> {
// transaction work
});
// ✅ New recommended approach
QuarkusTransaction.runner(TransactionSemantics.REQUIRE_NEW).run(() -> {
// transaction work
});Transactions are automatically tied to the CDI request scope:
@RequestScoped
public class OrderProcessor {
public void processOrder(Order order) {
QuarkusTransaction.begin();
// If this method completes without commit/rollback,
// transaction is automatically rolled back when request scope ends
validateOrder(order);
persistOrder(order);
// Must explicitly commit
QuarkusTransaction.commit();
}
}public void complexOperation() {
QuarkusTransaction.begin();
try {
step1();
step2();
step3();
QuarkusTransaction.commit();
} catch (BusinessException e) {
QuarkusTransaction.rollback();
throw e; // Re-throw business exceptions
} catch (Exception e) {
QuarkusTransaction.rollback();
throw new ServiceException("Operation failed", e);
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-narayana-jta