use of io.micronaut.transaction.TransactionDefinition in project micronaut-sql by micronaut-projects.
the class MicronautTransactionProvider method begin.
@Override
public void begin(TransactionContext context) throws DataAccessException {
TransactionDefinition definition = TransactionDefinition.DEFAULT;
TransactionStatus<Connection> status = transactionManager.getTransaction(definition);
context.transaction(new MicronautTransaction(status));
}
use of io.micronaut.transaction.TransactionDefinition in project micronaut-data by micronaut-projects.
the class HibernateTransactionManager method doBegin.
@Override
@SuppressWarnings("deprecation")
protected void doBegin(Object transaction, TransactionDefinition definition) {
HibernateTransactionObject txObject = (HibernateTransactionManager.HibernateTransactionObject) transaction;
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException("Pre-bound JDBC Connection found! HibernateTransactionManager does not support " + "running within DataSourceTransactionManager if told to manage the DataSource itself. " + "It is recommended to use a single HibernateTransactionManager for all transactions " + "on a single DataSource, no matter whether Hibernate or JDBC access.");
}
Session session = null;
try {
if (!txObject.hasSessionHolder() || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = (entityInterceptor != null ? getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() : getSessionFactory().openSession());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}
txObject.setSession(newSession);
}
session = txObject.getSessionHolder().getSession();
boolean holdabilityNeeded = this.allowResultAccessAfterCompletion && !txObject.isNewSession();
boolean isolationLevelNeeded = (definition.getIsolationLevel() != TransactionDefinition.Isolation.DEFAULT);
if (holdabilityNeeded || isolationLevelNeeded || definition.isReadOnly()) {
if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
// We're allowed to change the transaction settings of the JDBC Connection.
if (logger.isDebugEnabled()) {
logger.debug("Preparing JDBC Connection of Hibernate Session [" + session + "]");
}
Connection con = ((SessionImplementor) session).connection();
TransactionDefinition.Isolation previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
if (this.allowResultAccessAfterCompletion && !txObject.isNewSession()) {
int currentHoldability = con.getHoldability();
if (currentHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
txObject.setPreviousHoldability(currentHoldability);
con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
}
}
} else {
// Not allowed to change the transaction settings of the JDBC Connection.
if (isolationLevelNeeded) {
// We should set a specific isolation level but are not allowed to...
throw new InvalidIsolationLevelException("HibernateTransactionManager is not allowed to support custom isolation levels: " + "make sure that its 'prepareConnection' flag is on (the default) and that the " + "Hibernate connection release mode is set to 'on_close' (the default for JDBC).");
}
if (logger.isDebugEnabled()) {
logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]");
}
}
}
if (definition.isReadOnly() && txObject.isNewSession()) {
// Just set to MANUAL in case of a new Session for this transaction.
session.setFlushMode(FlushMode.MANUAL);
// As of 5.1, we're also setting Hibernate's read-only entity mode by default.
session.setDefaultReadOnly(true);
}
if (!definition.isReadOnly() && !txObject.isNewSession()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getHibernateFlushMode();
if (FlushMode.MANUAL.equals(flushMode)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
Transaction hibTx;
// Register transaction timeout.
Duration timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
// Applies to all statements, also to inserts, updates and deletes!
hibTx = session.getTransaction();
hibTx.setTimeout(((int) timeout.toMillis() / 1000));
hibTx.begin();
} else {
// Open a plain Hibernate transaction without specified timeout.
hibTx = session.beginTransaction();
}
// Add the Hibernate transaction to the session holder.
txObject.getSessionHolder().setTransaction(hibTx);
// Register the Hibernate Session's JDBC Connection for the DataSource, if set.
if (getDataSource() != null) {
SessionImplementor sessionImpl = (SessionImplementor) session;
// The following needs to use a lambda expression instead of a method reference
// for compatibility with Hibernate ORM <5.2 where connection() is defined on
// SessionImplementor itself instead of on SharedSessionContractImplementor...
ConnectionHolder conHolder = new ConnectionHolder(sessionImpl::connection);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeout(timeout);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing Hibernate transaction as JDBC [" + conHolder.getConnectionHandle() + "]");
}
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
}
// Bind the session holder to the thread.
if (txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
}
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
} catch (Throwable ex) {
if (txObject.isNewSession()) {
try {
if (session != null && session.getTransaction().getStatus() == TransactionStatus.ACTIVE) {
session.getTransaction().rollback();
}
} catch (Throwable ex2) {
logger.debug("Could not rollback Session after failed transaction begin", ex);
} finally {
SessionFactoryUtils.closeSession(session);
txObject.setSessionHolder(null);
}
}
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
}
}
use of io.micronaut.transaction.TransactionDefinition 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.TransactionDefinition in project micronaut-sql by micronaut-projects.
the class MicronautDataTransactionHandler method begin.
@Override
public void begin(Handle handle) {
TransactionDefinition definition = new DefaultTransactionDefinition(TransactionDefinition.Propagation.NESTED);
TransactionStatus<Connection> status = this.transactionManager.getTransaction(definition);
this.localTransactions.putIfAbsent(handle, new LocalStuff(status));
}
use of io.micronaut.transaction.TransactionDefinition 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);
});
}
});
}
Aggregations