private void CleanupAfterCompletion(DefaultTransactionStatus status) { status.IsCompleted = true; if (status.IsNewSynchronization) { TransactionSynchronizationManager.Clear(); } if (status.IsNewTransaction) { DoCleanupAfterCompletion(status.Transaction); } if (status.SuspendedResources != null) { _logger?.LogDebug("Resuming suspended transaction after completion of inner transaction"); var transaction = status.HasTransaction ? status.Transaction : null; Resume(transaction, (SuspendedResourcesHolder)status.SuspendedResources); } }
private void TriggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) { if (status.IsNewSynchronization) { var synchronizations = TransactionSynchronizationManager.GetSynchronizations(); TransactionSynchronizationManager.ClearSynchronization(); if (!status.HasTransaction || status.IsNewTransaction) { _logger?.LogTrace("Triggering afterCompletion synchronization"); // No transaction or new transaction for the current scope -> // invoke the afterCompletion callbacks immediately InvokeAfterCompletion(synchronizations, completionStatus); } else if (synchronizations.Count > 0) { // Existing transaction that we participate in, controlled outside // of the scope of this Spring transaction manager -> try to register // an afterCompletion callback with the existing (JTA) transaction. RegisterAfterCompletionWithExistingTransaction(status.Transaction, synchronizations); } } }
private void DoRollbackOnCommitException(DefaultTransactionStatus status, Exception exception) { try { if (status.IsNewTransaction) { _logger?.LogDebug(exception, "Initiating transaction rollback after commit exception"); DoRollback(status); } else if (status.HasTransaction && GlobalRollbackOnParticipationFailure) { _logger?.LogDebug(exception, "Marking existing transaction as rollback-only after commit exception"); DoSetRollbackOnly(status); } } catch (Exception ex) { _logger?.LogError(ex, "Commit exception overridden by rollback exception"); TriggerAfterCompletion(status, AbstractTransactionSynchronization.STATUS_UNKNOWN); throw; } TriggerAfterCompletion(status, AbstractTransactionSynchronization.STATUS_ROLLED_BACK); }
private void ProcessCommit(DefaultTransactionStatus status) { try { var beforeCompletionInvoked = false; try { var unexpectedRollback = false; PrepareForCommit(status); TriggerBeforeCommit(status); TriggerBeforeCompletion(status); beforeCompletionInvoked = true; if (status.HasSavepoint) { _logger?.LogDebug("Releasing transaction savepoint"); unexpectedRollback = status.IsGlobalRollbackOnly; status.ReleaseHeldSavepoint(); } else if (status.IsNewTransaction) { _logger?.LogDebug("Initiating transaction commit"); unexpectedRollback = status.IsGlobalRollbackOnly; DoCommit(status); } else if (FailEarlyOnGlobalRollbackOnly) { unexpectedRollback = status.IsGlobalRollbackOnly; } // Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (unexpectedRollback) { throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException) { // can only be caused by doCommit TriggerAfterCompletion(status, AbstractTransactionSynchronization.STATUS_ROLLED_BACK); throw; } catch (TransactionException ex) { // can only be caused by doCommit if (RollbackOnCommitFailure) { DoRollbackOnCommitException(status, ex); } else { TriggerAfterCompletion(status, AbstractTransactionSynchronization.STATUS_UNKNOWN); } throw; } catch (Exception ex) { if (!beforeCompletionInvoked) { TriggerBeforeCompletion(status); } DoRollbackOnCommitException(status, ex); throw; } // Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { TriggerAfterCommit(status); } finally { TriggerAfterCompletion(status, AbstractTransactionSynchronization.STATUS_COMMITTED); } } finally { CleanupAfterCompletion(status); } }
private void ProcessRollback(DefaultTransactionStatus status, bool unexpected) { try { var unexpectedRollback = unexpected; try { TriggerBeforeCompletion(status); if (status.HasSavepoint) { _logger?.LogDebug("Rolling back transaction to savepoint"); status.RollbackToHeldSavepoint(); } else if (status.IsNewTransaction) { _logger?.LogDebug("Initiating transaction rollback"); DoRollback(status); } else { // Participating in larger transaction if (status.HasTransaction) { if (status.IsLocalRollbackOnly || GlobalRollbackOnParticipationFailure) { _logger?.LogDebug("Participating transaction failed - marking existing transaction as rollback-only"); DoSetRollbackOnly(status); } else { _logger?.LogDebug("Participating transaction failed - letting transaction originator decide on rollback"); } } else { _logger?.LogDebug("Should roll back transaction but cannot - no transaction available"); } // Unexpected rollback only matters here if we're asked to fail early if (!FailEarlyOnGlobalRollbackOnly) { unexpectedRollback = false; } } } catch (Exception) { TriggerAfterCompletion(status, AbstractTransactionSynchronization.STATUS_UNKNOWN); throw; } TriggerAfterCompletion(status, AbstractTransactionSynchronization.STATUS_ROLLED_BACK); // Raise UnexpectedRollbackException if we had a global rollback-only marker if (unexpectedRollback) { throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only"); } } finally { CleanupAfterCompletion(status); } }
protected abstract void DoRollback(DefaultTransactionStatus status);
protected abstract void DoCommit(DefaultTransactionStatus status);
protected virtual void DoSetRollbackOnly(DefaultTransactionStatus status) { throw new IllegalTransactionStateException( "Participating in existing transactions is not supported - when 'isExistingTransaction' " + "returns true, appropriate 'doSetRollbackOnly' behavior must be provided"); }
protected virtual void PrepareForCommit(DefaultTransactionStatus status) { }