use of io.micronaut.transaction.exceptions.NoTransactionException in project micronaut-data by micronaut-projects.
the class DefaultR2dbcRepositoryOperations method withTransaction.
@Override
@NonNull
public <T> Flux<T> withTransaction(@NonNull TransactionDefinition definition, @NonNull ReactiveTransactionOperations.TransactionalCallback<Connection, T> handler) {
Objects.requireNonNull(definition, "Transaction definition cannot be null");
Objects.requireNonNull(handler, "Callback handler cannot be null");
return Flux.deferContextual(contextView -> {
Object o = contextView.getOrDefault(ReactiveTransactionStatus.STATUS, null);
TransactionDefinition.Propagation propagationBehavior = definition.getPropagationBehavior();
if (o instanceof ReactiveTransactionStatus) {
// existing transaction, use it
if (propagationBehavior == TransactionDefinition.Propagation.NOT_SUPPORTED || propagationBehavior == TransactionDefinition.Propagation.NEVER) {
return Flux.error(new TransactionUsageException("Found an existing transaction but propagation behaviour doesn't support it: " + propagationBehavior));
}
try {
return handler.doInTransaction(((ReactiveTransactionStatus<Connection>) o));
} catch (Exception e) {
return Flux.error(new TransactionSystemException("Error invoking doInTransaction handler: " + e.getMessage(), e));
}
} else {
if (propagationBehavior == TransactionDefinition.Propagation.MANDATORY) {
return Flux.error(new NoTransactionException("Expected an existing transaction, but none was found in the Reactive context."));
}
return withConnection(connection -> {
if (LOG.isDebugEnabled()) {
LOG.debug("Transaction Begin for DataSource: {}", dataSourceName);
}
DefaultReactiveTransactionStatus status = new DefaultReactiveTransactionStatus(connection, true);
Mono<Boolean> resourceSupplier;
if (definition.getIsolationLevel() != TransactionDefinition.DEFAULT.getIsolationLevel()) {
IsolationLevel isolationLevel = getIsolationLevel(definition);
if (LOG.isDebugEnabled()) {
LOG.debug("Setting Isolation Level ({}) for Transaction on DataSource: {}", isolationLevel, dataSourceName);
}
if (isolationLevel != null) {
resourceSupplier = Flux.from(connection.setTransactionIsolationLevel(isolationLevel)).thenMany(connection.beginTransaction()).hasElements();
} else {
resourceSupplier = Flux.from(connection.beginTransaction()).hasElements();
}
} else {
resourceSupplier = Flux.from(connection.beginTransaction()).hasElements();
}
return Flux.usingWhen(resourceSupplier, (b) -> {
try {
return Flux.from(handler.doInTransaction(status)).contextWrite(context -> context.put(ReactiveTransactionStatus.STATUS, status).put(ReactiveTransactionStatus.ATTRIBUTE, definition));
} catch (Exception e) {
return Mono.error(new TransactionSystemException("Error invoking doInTransaction handler: " + e.getMessage(), e));
}
}, (b) -> doCommit(status), (b, throwable) -> {
if (LOG.isWarnEnabled()) {
LOG.warn("Rolling back transaction on error: " + throwable.getMessage(), throwable);
}
return Flux.from(connection.rollbackTransaction()).hasElements().onErrorResume((rollbackError) -> {
if (rollbackError != throwable && LOG.isWarnEnabled()) {
LOG.warn("Error occurred during transaction rollback: " + rollbackError.getMessage(), rollbackError);
}
return Mono.error(throwable);
}).doFinally((sig) -> status.completed = true);
}, (b) -> doCommit(status));
});
}
});
}
use of io.micronaut.transaction.exceptions.NoTransactionException in project micronaut-data by micronaut-projects.
the class DefaultReactiveMongoRepositoryOperations method withTransaction.
@Override
@NonNull
public <T> Flux<T> withTransaction(@NonNull TransactionDefinition definition, @NonNull ReactiveTransactionOperations.TransactionalCallback<ClientSession, T> handler) {
Objects.requireNonNull(definition, "Transaction definition cannot be null");
Objects.requireNonNull(handler, "Callback handler cannot be null");
return Flux.deferContextual(contextView -> {
Object o = contextView.getOrDefault(ReactiveTransactionStatus.STATUS, null);
TransactionDefinition.Propagation propagationBehavior = definition.getPropagationBehavior();
if (o instanceof ReactiveTransactionStatus) {
// existing transaction, use it
if (propagationBehavior == TransactionDefinition.Propagation.NOT_SUPPORTED || propagationBehavior == TransactionDefinition.Propagation.NEVER) {
return Flux.error(new TransactionUsageException("Found an existing transaction but propagation behaviour doesn't support it: " + propagationBehavior));
}
try {
return handler.doInTransaction((ReactiveTransactionStatus<ClientSession>) o);
} catch (Exception e) {
return Flux.error(new TransactionSystemException("Error invoking doInTransaction handler: " + e.getMessage(), e));
}
} else {
if (propagationBehavior == TransactionDefinition.Propagation.MANDATORY) {
return Flux.error(new NoTransactionException("Expected an existing transaction, but none was found in the Reactive context."));
}
return withClientSessionMany(clientSession -> {
if (LOG.isDebugEnabled()) {
LOG.debug("Transaction Begin for MongoDB configuration: {}", serverName);
}
DefaultReactiveTransactionStatus status = new DefaultReactiveTransactionStatus(clientSession, true);
if (definition.getIsolationLevel() != TransactionDefinition.DEFAULT.getIsolationLevel()) {
throw new TransactionUsageException("Isolation level not supported");
} else {
clientSession.startTransaction();
}
return Flux.usingWhen(Mono.just(status), sts -> {
try {
return Flux.from(handler.doInTransaction(status)).contextWrite(context -> context.put(ReactiveTransactionStatus.STATUS, status).put(ReactiveTransactionStatus.ATTRIBUTE, definition));
} catch (Exception e) {
return Flux.error(new TransactionSystemException("Error invoking doInTransaction handler: " + e.getMessage(), e));
}
}, this::doCommit, (sts, throwable) -> {
if (LOG.isWarnEnabled()) {
LOG.warn("Rolling back transaction on error: " + throwable.getMessage(), throwable);
}
return Flux.from(sts.getConnection().abortTransaction()).hasElements().onErrorResume((rollbackError) -> {
if (rollbackError != throwable && LOG.isWarnEnabled()) {
LOG.warn("Error occurred during transaction rollback: " + rollbackError.getMessage(), rollbackError);
}
return Mono.error(throwable);
}).doFinally((sig) -> status.completed = true);
}, this::doCommit);
});
}
});
}
use of io.micronaut.transaction.exceptions.NoTransactionException in project micronaut-data by micronaut-projects.
the class TransactionalClientSessionInterceptor method intercept.
@Override
public Object intercept(MethodInvocationContext<ClientSession, Object> context) {
ClientSession clientSession = transactionManager.findClientSession();
if (clientSession == null) {
throw new NoTransactionException("No current transaction present. Consider declaring @Transactional on the surrounding method");
}
final ExecutableMethod<ClientSession, Object> method = context.getExecutableMethod();
if (method.getName().equals("close")) {
// Handle close method: only close if not within a transaction.
transactionManager.closeClientSession();
return null;
}
return method.invoke(clientSession, context.getParameterValues());
}
use of io.micronaut.transaction.exceptions.NoTransactionException in project micronaut-data by micronaut-projects.
the class TransactionalConnectionInterceptor method intercept.
@Override
public Object intercept(MethodInvocationContext<Connection, Object> context) {
Connection connection;
try {
connection = DataSourceUtils.getConnection(dataSource, false);
} catch (CannotGetJdbcConnectionException e) {
throw new NoTransactionException("No current transaction present. Consider declaring @Transactional on the surrounding method", e);
}
final ExecutableMethod<Connection, Object> method = context.getExecutableMethod();
if (method.getName().equals("close")) {
// Handle close method: only close if not within a transaction.
try {
DataSourceUtils.doReleaseConnection(connection, this.dataSource);
} catch (SQLException e) {
throw new CannotGetJdbcConnectionException("Failed to release connection: " + e.getMessage(), e);
}
return null;
}
return method.invoke(connection, context.getParameterValues());
}
Aggregations