- 事務的特性(ACID):
- 原子性(atomicity):事務是一個原子操作,是有一系類的動作組成,事務的原子性是確保這一系列動作要麼全部完成,要麼全部不起作用。
- 一致性(consistency):一旦事務完成(不管是成功還是失敗),系統必須確保他所建模的業務處於一致的狀態,在現實的數據中不應該被破壞。
- 隔離性(isolation):在多事務的操作相同的數據時每個事物都應該與其他事物隔離開來,防止數據破壞。
- 持久性(durability): 一旦事務完成。無論系統發生什麼錯誤,他的結果都不應該收到影響,這樣就能從任何系統崩潰中回覆過來,通常情況下,事物的結果被持久化存儲。
- 核心接口
Spring事務管理的實現有許多細節,如果對整個接口框架有個大體瞭解會非常有利於我們理解事務,下面通過講解Spring的事務接口來了解Spring實現事務的具體策略。 Spring事務管理涉及的接口的聯繫如下: - 事物管理器
spring並不是直接管理事務,而是提供事務管理器給第三方去實現。spring提供的事務管理接口爲org.springframework.transaction.PlatformTransactionManager,此接口的具體內容爲:
public interface PlatformTransactionManager { /** * Return a currently active transaction or create a new one, according to * the specified propagation behavior. * <p>Note that parameters like isolation level or timeout will only be applied * to new transactions, and thus be ignored when participating in active ones. * <p>Furthermore, not all transaction definition settings will be supported * by every transaction manager: A proper transaction manager implementation * should throw an exception when unsupported settings are encountered. * <p>An exception to the above rule is the read-only flag, which should be * ignored if no explicit read-only mode is supported. Essentially, the * read-only flag is just a hint for potential optimization. * @param definition TransactionDefinition instance (can be {@code null} for defaults), * describing propagation behavior, isolation level, timeout etc. * @return transaction status object representing the new or current transaction * @throws TransactionException in case of lookup, creation, or system errors * @throws IllegalTransactionStateException if the given transaction definition * cannot be executed (for example, if a currently active transaction is in * conflict with the specified propagation behavior) * @see TransactionDefinition#getPropagationBehavior * @see TransactionDefinition#getIsolationLevel * @see TransactionDefinition#getTimeout * @see TransactionDefinition#isReadOnly */ TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; /** * Commit the given transaction, with regard to its status. If the transaction * has been marked rollback-only programmatically, perform a rollback. * <p>If the transaction wasn't a new one, omit the commit for proper * participation in the surrounding transaction. If a previous transaction * has been suspended to be able to create a new one, resume the previous * transaction after committing the new one. * <p>Note that when the commit call completes, no matter if normally or * throwing an exception, the transaction must be fully completed and * cleaned up. No rollback call should be expected in such a case. * <p>If this method throws an exception other than a TransactionException, * then some before-commit error caused the commit attempt to fail. For * example, an O/R Mapping tool might have tried to flush changes to the * database right before commit, with the resulting DataAccessException * causing the transaction to fail. The original exception will be * propagated to the caller of this commit method in such a case. * @param status object returned by the {@code getTransaction} method * @throws UnexpectedRollbackException in case of an unexpected rollback * that the transaction coordinator initiated * @throws HeuristicCompletionException in case of a transaction failure * caused by a heuristic decision on the side of the transaction coordinator * @throws TransactionSystemException in case of commit or system errors * (typically caused by fundamental resource failures) * @throws IllegalTransactionStateException if the given transaction * is already completed (that is, committed or rolled back) * @see TransactionStatus#setRollbackOnly */ void commit(TransactionStatus status) throws TransactionException; /** * Perform a rollback of the given transaction. * <p>If the transaction wasn't a new one, just set it rollback-only for proper * participation in the surrounding transaction. If a previous transaction * has been suspended to be able to create a new one, resume the previous * transaction after rolling back the new one. * <p><b>Do not call rollback on a transaction if commit threw an exception.</b> * The transaction will already have been completed and cleaned up when commit * returns, even in case of a commit exception. Consequently, a rollback call * after commit failure will lead to an IllegalTransactionStateException. * @param status object returned by the {@code getTransaction} method * @throws TransactionSystemException in case of rollback or system errors * (typically caused by fundamental resource failures) * @throws IllegalTransactionStateException if the given transaction * is already completed (that is, committed or rolled back) */ void rollback(TransactionStatus status) throws TransactionException; }
從這裏可知具體的具體的事務管理機制對Spring來說是透明的,它並不關心那些,那些是對應各個平臺需要關心的,所以Spring事務管理的一個優點就是爲不同的事務API提供一致的編程模型,如JTA、JDBC、Hibernate、JPA。下面分別介紹各個平臺框架實現事務管理的機制。
一下是一些實現的事務管理類:
- jdbc事務管理
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable { public static final int SYNCHRONIZATION_ALWAYS = 0; public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1; /** * Never active transaction synchronization, not even for actual transactions. */ public static final int SYNCHRONIZATION_NEVER = 2; /** * Constants instance for AbstractPlatformTransactionManager */ private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class); protected transient Log logger = LogFactory.getLog(getClass()); private int transactionSynchronization = SYNCHRONIZATION_ALWAYS; private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT; private boolean nestedTransactionAllowed = false; private boolean validateExistingTransaction = false; private boolean globalRollbackOnParticipationFailure = true; private boolean failEarlyOnGlobalRollbackOnly = false; private boolean rollbackOnCommitFailure = false; public final void setTransactionSynchronizationName(String constantName) { setTransactionSynchronization(constants.asNumber(constantName).intValue()); } public final void setTransactionSynchronization(int transactionSynchronization) { this.transactionSynchronization = transactionSynchronization; } public final int getTransactionSynchronization() { return this.transactionSynchronization; } public final void setDefaultTimeout(int defaultTimeout) { if (defaultTimeout < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid default timeout", defaultTimeout); } this.defaultTimeout = defaultTimeout; } public final int getDefaultTimeout() { return this.defaultTimeout; } public final void setNestedTransactionAllowed(boolean nestedTransactionAllowed) { this.nestedTransactionAllowed = nestedTransactionAllowed; } /** * Return whether nested transactions are allowed. */ public final boolean isNestedTransactionAllowed() { return this.nestedTransactionAllowed; } public final void setValidateExistingTransaction(boolean validateExistingTransaction) { this.validateExistingTransaction = validateExistingTransaction; } /** * Return whether existing transactions should be validated before participating * in them. */ public final boolean isValidateExistingTransaction() { return this.validateExistingTransaction; } public final void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure) { this.globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure; } /** * Return whether to globally mark an existing transaction as rollback-only * after a participating transaction failed. */ public final boolean isGlobalRollbackOnParticipationFailure() { return this.globalRollbackOnParticipationFailure; } public final void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) { this.failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly; } /** * Return whether to fail early in case of the transaction being globally marked * as rollback-only. */ public final boolean isFailEarlyOnGlobalRollbackOnly() { return this.failEarlyOnGlobalRollbackOnly; } public final void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure) { this.rollbackOnCommitFailure = rollbackOnCommitFailure; } /** * Return whether {@code doRollback} should be performed on failure of the * {@code doCommit} call. */ public final boolean isRollbackOnCommitFailure() { return this.rollbackOnCommitFailure; } //--------------------------------------------------------------------- // Implementation of PlatformTransactionManager //--------------------------------------------------------------------- public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { Object transaction = doGetTransaction(); // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) { // Use defaults if no transaction definition given. definition = new DefaultTransactionDefinition(); } if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } // Check definition settings for new transaction. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // No existing transaction found -> check propagation behavior to find out how to proceed. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); } try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } } private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'"); } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (debugEnabled) { logger.debug("Suspending current transaction"); } Object suspendedResources = suspend(transaction); boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources); } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { if (debugEnabled) { logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]"); } SuspendedResourcesHolder suspendedResources = suspend(transaction); try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException beginEx) { resumeAfterBeginException(transaction, suspendedResources, beginEx); throw beginEx; } catch (Error beginErr) { resumeAfterBeginException(transaction, suspendedResources, beginErr); throw beginErr; } } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { if (!isNestedTransactionAllowed()) { throw new NestedTransactionNotSupportedException( "Transaction manager does not allow nested transactions by default - " + "specify 'nestedTransactionAllowed' property with value 'true'"); } if (debugEnabled) { logger.debug("Creating nested transaction with name [" + definition.getName() + "]"); } if (useSavepointForNestedTransaction()) { // Create savepoint within existing Spring-managed transaction, // through the SavepointManager API implemented by TransactionStatus. // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); status.createAndHoldSavepoint(); return status; } else { // Nested transaction through nested begin and commit/rollback calls. // Usually only for JTA: Spring synchronization might get activated here // in case of a pre-existing JTA transaction. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, null); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } } // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. if (debugEnabled) { logger.debug("Participating in existing transaction"); } if (isValidateExistingTransaction()) { if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) { Constants isoConstants = DefaultTransactionDefinition.constants; throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (currentIsolationLevel != null ? isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) : "(unknown)")); } } if (!definition.isReadOnly()) { if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is"); } } } boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); } protected final DefaultTransactionStatus prepareTransactionStatus( TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) { DefaultTransactionStatus status = newTransactionStatus( definition, transaction, newTransaction, newSynchronization, debug, suspendedResources); prepareSynchronization(status, definition); return status; } protected DefaultTransactionStatus newTransactionStatus( TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) { boolean actualNewSynchronization = newSynchronization && !TransactionSynchronizationManager.isSynchronizationActive(); return new DefaultTransactionStatus( transaction, newTransaction, actualNewSynchronization, definition.isReadOnly(), debug, suspendedResources); } protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) { if (status.isNewSynchronization()) { TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction()); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel( (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) ? definition.getIsolationLevel() : null); TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly()); TransactionSynchronizationManager.setCurrentTransactionName(definition.getName()); TransactionSynchronizationManager.initSynchronization(); } } protected int determineTimeout(TransactionDefinition definition) { if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) { return definition.getTimeout(); } return this.defaultTimeout; } protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException { if (TransactionSynchronizationManager.isSynchronizationActive()) { List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization(); try { Object suspendedResources = null; if (transaction != null) { suspendedResources = doSuspend(transaction); } String name = TransactionSynchronizationManager.getCurrentTransactionName(); TransactionSynchronizationManager.setCurrentTransactionName(null); boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); TransactionSynchronizationManager.setActualTransactionActive(false); return new SuspendedResourcesHolder( suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); } catch (RuntimeException ex) { // doSuspend failed - original transaction is still active... doResumeSynchronization(suspendedSynchronizations); throw ex; } catch (Error err) { // doSuspend failed - original transaction is still active... doResumeSynchronization(suspendedSynchronizations); throw err; } } else if (transaction != null) { // Transaction active but no synchronization active. Object suspendedResources = doSuspend(transaction); return new SuspendedResourcesHolder(suspendedResources); } else { // Neither transaction nor synchronization active. return null; } } protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder) throws TransactionException { if (resourcesHolder != null) { Object suspendedResources = resourcesHolder.suspendedResources; if (suspendedResources != null) { doResume(transaction, suspendedResources); } List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations; if (suspendedSynchronizations != null) { TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel); TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly); TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name); doResumeSynchronization(suspendedSynchronizations); } } } private void resumeAfterBeginException( Object transaction, SuspendedResourcesHolder suspendedResources, Throwable beginEx) { String exMessage = "Inner transaction begin exception overridden by outer transaction resume exception"; try { resume(transaction, suspendedResources); } catch (RuntimeException resumeEx) { logger.error(exMessage, beginEx); throw resumeEx; } catch (Error resumeErr) { logger.error(exMessage, beginEx); throw resumeErr; } } private List<TransactionSynchronization> doSuspendSynchronization() { List<TransactionSynchronization> suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.suspend(); } TransactionSynchronizationManager.clearSynchronization(); return suspendedSynchronizations; } private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) { TransactionSynchronizationManager.initSynchronization(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.resume(); TransactionSynchronizationManager.registerSynchronization(synchronization); } } public final void commit(TransactionStatus status) throws TransactionException { if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; if (defStatus.isLocalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Transactional code has requested rollback"); } processRollback(defStatus); return; } if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); } processRollback(defStatus); // Throw UnexpectedRollbackException only at outermost transaction boundary // or if explicitly asked to. if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { throw new UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } return; } processCommit(defStatus); } private void processCommit(DefaultTransactionStatus status) throws TransactionException { try { boolean beforeCompletionInvoked = false; try { prepareForCommit(status); triggerBeforeCommit(status); triggerBeforeCompletion(status); beforeCompletionInvoked = true; boolean globalRollbackOnly = false; if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { globalRollbackOnly = status.isGlobalRollbackOnly(); } if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Releasing transaction savepoint"); } status.releaseHeldSavepoint(); } else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction commit"); } doCommit(status); } // Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (globalRollbackOnly) { throw new UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // can only be caused by doCommit triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; } catch (Error err) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, err); throw err; } // Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); } } finally { cleanupAfterCompletion(status); } } public final void rollback(TransactionStatus status) throws TransactionException { if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; processRollback(defStatus); } private void processRollback(DefaultTransactionStatus status) { try { try { triggerBeforeCompletion(status); if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Rolling back transaction to savepoint"); } status.rollbackToHeldSavepoint(); } else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback"); } doRollback(status); } else if (status.hasTransaction()) { if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { if (status.isDebug()) { logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); } doSetRollbackOnly(status); } else { if (status.isDebug()) { logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); } } } else { logger.debug("Should roll back transaction but cannot - no transaction available"); } } catch (RuntimeException ex) { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw ex; } catch (Error err) { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw err; } triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); } finally { cleanupAfterCompletion(status); } } private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable ex) throws TransactionException { try { if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback after commit exception", ex); } doRollback(status); } else if (status.hasTransaction() && isGlobalRollbackOnParticipationFailure()) { if (status.isDebug()) { logger.debug("Marking existing transaction as rollback-only after commit exception", ex); } doSetRollbackOnly(status); } } catch (RuntimeException rbex) { logger.error("Commit exception overridden by rollback exception", ex); triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw rbex; } catch (Error rberr) { logger.error("Commit exception overridden by rollback exception", ex); triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw rberr; } triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); } protected final void triggerBeforeCommit(DefaultTransactionStatus status) { if (status.isNewSynchronization()) { if (status.isDebug()) { logger.trace("Triggering beforeCommit synchronization"); } TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly()); } } protected final void triggerBeforeCompletion(DefaultTransactionStatus status) { if (status.isNewSynchronization()) { if (status.isDebug()) { logger.trace("Triggering beforeCompletion synchronization"); } TransactionSynchronizationUtils.triggerBeforeCompletion(); } } private void triggerAfterCommit(DefaultTransactionStatus status) { if (status.isNewSynchronization()) { if (status.isDebug()) { logger.trace("Triggering afterCommit synchronization"); } TransactionSynchronizationUtils.triggerAfterCommit(); } } private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) { if (status.isNewSynchronization()) { List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations(); if (!status.hasTransaction() || status.isNewTransaction()) { if (status.isDebug()) { logger.trace("Triggering afterCompletion synchronization"); } // No transaction or new transaction for the current scope -> // invoke the afterCompletion callbacks immediately invokeAfterCompletion(synchronizations, completionStatus); } else if (!synchronizations.isEmpty()) { // 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.getTransaction(), synchronizations); } } } protected final void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) { TransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus); } private void cleanupAfterCompletion(DefaultTransactionStatus status) { status.setCompleted(); if (status.isNewSynchronization()) { TransactionSynchronizationManager.clear(); } if (status.isNewTransaction()) { doCleanupAfterCompletion(status.getTransaction()); } if (status.getSuspendedResources() != null) { if (status.isDebug()) { logger.debug("Resuming suspended transaction after completion of inner transaction"); } resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources()); } } //--------------------------------------------------------------------- // Template methods to be implemented in subclasses //--------------------------------------------------------------------- protected abstract Object doGetTransaction() throws TransactionException; protected boolean isExistingTransaction(Object transaction) throws TransactionException { return false; } protected boolean useSavepointForNestedTransaction() { return true; } protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException; protected Object doSuspend(Object transaction) throws TransactionException { throw new TransactionSuspensionNotSupportedException( "Transaction manager [" + getClass().getName() + "] does not support transaction suspension"); } protected void doResume(Object transaction, Object suspendedResources) throws TransactionException { throw new TransactionSuspensionNotSupportedException( "Transaction manager [" + getClass().getName() + "] does not support transaction suspension"); } protected boolean shouldCommitOnGlobalRollbackOnly() { return false; } protected void prepareForCommit(DefaultTransactionStatus status) { } protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException; protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException; protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException { throw new IllegalTransactionStateException( "Participating in existing transactions is not supported - when 'isExistingTransaction' " + "returns true, appropriate 'doSetRollbackOnly' behavior must be provided"); } protected void registerAfterCompletionWithExistingTransaction( Object transaction, List<TransactionSynchronization> synchronizations) throws TransactionException { logger.debug("Cannot register Spring after-completion synchronization with existing transaction - " + "processing Spring after-completion callbacks immediately, with outcome status 'unknown'"); invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); } protected void doCleanupAfterCompletion(Object transaction) { } //--------------------------------------------------------------------- // Serialization support //--------------------------------------------------------------------- private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // Rely on default serialization; just initialize state after deserialization. ois.defaultReadObject(); // Initialize transient fields. this.logger = LogFactory.getLog(getClass()); } /** * Holder for suspended resources. * Used internally by {@code suspend} and {@code resume}. */ protected static class SuspendedResourcesHolder { private final Object suspendedResources; private List<TransactionSynchronization> suspendedSynchronizations; private String name; private boolean readOnly; private Integer isolationLevel; private boolean wasActive; private SuspendedResourcesHolder(Object suspendedResources) { this.suspendedResources = suspendedResources; } private SuspendedResourcesHolder( Object suspendedResources, List<TransactionSynchronization> suspendedSynchronizations, String name, boolean readOnly, Integer isolationLevel, boolean wasActive) { this.suspendedResources = suspendedResources; this.suspendedSynchronizations = suspendedSynchronizations; this.name = name; this.readOnly = readOnly; this.isolationLevel = isolationLevel; this.wasActive = wasActive; } } }
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable { public static final int SYNCHRONIZATION_ALWAYS = 0; public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1; /** * Never active transaction synchronization, not even for actual transactions. */ public static final int SYNCHRONIZATION_NEVER = 2; /** * Constants instance for AbstractPlatformTransactionManager */ private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class); protected transient Log logger = LogFactory.getLog(getClass()); private int transactionSynchronization = SYNCHRONIZATION_ALWAYS; private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT; private boolean nestedTransactionAllowed = false; private boolean validateExistingTransaction = false; private boolean globalRollbackOnParticipationFailure = true; private boolean failEarlyOnGlobalRollbackOnly = false; private boolean rollbackOnCommitFailure = false; public final void setTransactionSynchronizationName(String constantName) { setTransactionSynchronization(constants.asNumber(constantName).intValue()); } public final void setTransactionSynchronization(int transactionSynchronization) { this.transactionSynchronization = transactionSynchronization; } public final int getTransactionSynchronization() { return this.transactionSynchronization; } public final void setDefaultTimeout(int defaultTimeout) { if (defaultTimeout < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid default timeout", defaultTimeout); } this.defaultTimeout = defaultTimeout; } public final int getDefaultTimeout() { return this.defaultTimeout; } public final void setNestedTransactionAllowed(boolean nestedTransactionAllowed) { this.nestedTransactionAllowed = nestedTransactionAllowed; } /** * Return whether nested transactions are allowed. */ public final boolean isNestedTransactionAllowed() { return this.nestedTransactionAllowed; } public final void setValidateExistingTransaction(boolean validateExistingTransaction) { this.validateExistingTransaction = validateExistingTransaction; } /** * Return whether existing transactions should be validated before participating * in them. */ public final boolean isValidateExistingTransaction() { return this.validateExistingTransaction; } public final void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure) { this.globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure; } /** * Return whether to globally mark an existing transaction as rollback-only * after a participating transaction failed. */ public final boolean isGlobalRollbackOnParticipationFailure() { return this.globalRollbackOnParticipationFailure; } public final void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) { this.failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly; } /** * Return whether to fail early in case of the transaction being globally marked * as rollback-only. */ public final boolean isFailEarlyOnGlobalRollbackOnly() { return this.failEarlyOnGlobalRollbackOnly; } public final void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure) { this.rollbackOnCommitFailure = rollbackOnCommitFailure; } /** * Return whether {@code doRollback} should be performed on failure of the * {@code doCommit} call. */ public final boolean isRollbackOnCommitFailure() { return this.rollbackOnCommitFailure; } //--------------------------------------------------------------------- // Implementation of PlatformTransactionManager //--------------------------------------------------------------------- public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { Object transaction = doGetTransaction(); // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) { // Use defaults if no transaction definition given. definition = new DefaultTransactionDefinition(); } if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } // Check definition settings for new transaction. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // No existing transaction found -> check propagation behavior to find out how to proceed. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); } try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } } private TransactionStatus handleExistingTransaction( TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException { if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) { throw new IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'"); } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) { if (debugEnabled) { logger.debug("Suspending current transaction"); } Object suspendedResources = suspend(transaction); boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus( definition, null, false, newSynchronization, debugEnabled, suspendedResources); } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { if (debugEnabled) { logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]"); } SuspendedResourcesHolder suspendedResources = suspend(transaction); try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException beginEx) { resumeAfterBeginException(transaction, suspendedResources, beginEx); throw beginEx; } catch (Error beginErr) { resumeAfterBeginException(transaction, suspendedResources, beginErr); throw beginErr; } } if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { if (!isNestedTransactionAllowed()) { throw new NestedTransactionNotSupportedException( "Transaction manager does not allow nested transactions by default - " + "specify 'nestedTransactionAllowed' property with value 'true'"); } if (debugEnabled) { logger.debug("Creating nested transaction with name [" + definition.getName() + "]"); } if (useSavepointForNestedTransaction()) { // Create savepoint within existing Spring-managed transaction, // through the SavepointManager API implemented by TransactionStatus. // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. DefaultTransactionStatus status = prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null); status.createAndHoldSavepoint(); return status; } else { // Nested transaction through nested begin and commit/rollback calls. // Usually only for JTA: Spring synchronization might get activated here // in case of a pre-existing JTA transaction. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, null); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } } // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. if (debugEnabled) { logger.debug("Participating in existing transaction"); } if (isValidateExistingTransaction()) { if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) { Constants isoConstants = DefaultTransactionDefinition.constants; throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (currentIsolationLevel != null ? isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) : "(unknown)")); } } if (!definition.isReadOnly()) { if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is"); } } } boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null); } protected final DefaultTransactionStatus prepareTransactionStatus( TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) { DefaultTransactionStatus status = newTransactionStatus( definition, transaction, newTransaction, newSynchronization, debug, suspendedResources); prepareSynchronization(status, definition); return status; } protected DefaultTransactionStatus newTransactionStatus( TransactionDefinition definition, Object transaction, boolean newTransaction, boolean newSynchronization, boolean debug, Object suspendedResources) { boolean actualNewSynchronization = newSynchronization && !TransactionSynchronizationManager.isSynchronizationActive(); return new DefaultTransactionStatus( transaction, newTransaction, actualNewSynchronization, definition.isReadOnly(), debug, suspendedResources); } protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) { if (status.isNewSynchronization()) { TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction()); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel( (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) ? definition.getIsolationLevel() : null); TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly()); TransactionSynchronizationManager.setCurrentTransactionName(definition.getName()); TransactionSynchronizationManager.initSynchronization(); } } protected int determineTimeout(TransactionDefinition definition) { if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) { return definition.getTimeout(); } return this.defaultTimeout; } protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException { if (TransactionSynchronizationManager.isSynchronizationActive()) { List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization(); try { Object suspendedResources = null; if (transaction != null) { suspendedResources = doSuspend(transaction); } String name = TransactionSynchronizationManager.getCurrentTransactionName(); TransactionSynchronizationManager.setCurrentTransactionName(null); boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); TransactionSynchronizationManager.setActualTransactionActive(false); return new SuspendedResourcesHolder( suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); } catch (RuntimeException ex) { // doSuspend failed - original transaction is still active... doResumeSynchronization(suspendedSynchronizations); throw ex; } catch (Error err) { // doSuspend failed - original transaction is still active... doResumeSynchronization(suspendedSynchronizations); throw err; } } else if (transaction != null) { // Transaction active but no synchronization active. Object suspendedResources = doSuspend(transaction); return new SuspendedResourcesHolder(suspendedResources); } else { // Neither transaction nor synchronization active. return null; } } protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder) throws TransactionException { if (resourcesHolder != null) { Object suspendedResources = resourcesHolder.suspendedResources; if (suspendedResources != null) { doResume(transaction, suspendedResources); } List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations; if (suspendedSynchronizations != null) { TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel); TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly); TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name); doResumeSynchronization(suspendedSynchronizations); } } } private void resumeAfterBeginException( Object transaction, SuspendedResourcesHolder suspendedResources, Throwable beginEx) { String exMessage = "Inner transaction begin exception overridden by outer transaction resume exception"; try { resume(transaction, suspendedResources); } catch (RuntimeException resumeEx) { logger.error(exMessage, beginEx); throw resumeEx; } catch (Error resumeErr) { logger.error(exMessage, beginEx); throw resumeErr; } } private List<TransactionSynchronization> doSuspendSynchronization() { List<TransactionSynchronization> suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.suspend(); } TransactionSynchronizationManager.clearSynchronization(); return suspendedSynchronizations; } private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) { TransactionSynchronizationManager.initSynchronization(); for (TransactionSynchronization synchronization : suspendedSynchronizations) { synchronization.resume(); TransactionSynchronizationManager.registerSynchronization(synchronization); } } public final void commit(TransactionStatus status) throws TransactionException { if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; if (defStatus.isLocalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Transactional code has requested rollback"); } processRollback(defStatus); return; } if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); } processRollback(defStatus); // Throw UnexpectedRollbackException only at outermost transaction boundary // or if explicitly asked to. if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { throw new UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } return; } processCommit(defStatus); } private void processCommit(DefaultTransactionStatus status) throws TransactionException { try { boolean beforeCompletionInvoked = false; try { prepareForCommit(status); triggerBeforeCommit(status); triggerBeforeCompletion(status); beforeCompletionInvoked = true; boolean globalRollbackOnly = false; if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { globalRollbackOnly = status.isGlobalRollbackOnly(); } if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Releasing transaction savepoint"); } status.releaseHeldSavepoint(); } else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction commit"); } doCommit(status); } // Throw UnexpectedRollbackException if we have a global rollback-only // marker but still didn't get a corresponding exception from commit. if (globalRollbackOnly) { throw new UnexpectedRollbackException( "Transaction silently rolled back because it has been marked as rollback-only"); } } catch (UnexpectedRollbackException ex) { // can only be caused by doCommit triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); throw ex; } catch (TransactionException ex) { // can only be caused by doCommit if (isRollbackOnCommitFailure()) { doRollbackOnCommitException(status, ex); } else { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); } throw ex; } catch (RuntimeException ex) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, ex); throw ex; } catch (Error err) { if (!beforeCompletionInvoked) { triggerBeforeCompletion(status); } doRollbackOnCommitException(status, err); throw err; } // Trigger afterCommit callbacks, with an exception thrown there // propagated to callers but the transaction still considered as committed. try { triggerAfterCommit(status); } finally { triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED); } } finally { cleanupAfterCompletion(status); } } public final void rollback(TransactionStatus status) throws TransactionException { if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; processRollback(defStatus); } private void processRollback(DefaultTransactionStatus status) { try { try { triggerBeforeCompletion(status); if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Rolling back transaction to savepoint"); } status.rollbackToHeldSavepoint(); } else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback"); } doRollback(status); } else if (status.hasTransaction()) { if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { if (status.isDebug()) { logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); } doSetRollbackOnly(status); } else { if (status.isDebug()) { logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); } } } else { logger.debug("Should roll back transaction but cannot - no transaction available"); } } catch (RuntimeException ex) { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw ex; } catch (Error err) { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw err; } triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); } finally { cleanupAfterCompletion(status); } } private void doRollbackOnCommitException(DefaultTransactionStatus status, Throwable ex) throws TransactionException { try { if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback after commit exception", ex); } doRollback(status); } else if (status.hasTransaction() && isGlobalRollbackOnParticipationFailure()) { if (status.isDebug()) { logger.debug("Marking existing transaction as rollback-only after commit exception", ex); } doSetRollbackOnly(status); } } catch (RuntimeException rbex) { logger.error("Commit exception overridden by rollback exception", ex); triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw rbex; } catch (Error rberr) { logger.error("Commit exception overridden by rollback exception", ex); triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw rberr; } triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); } protected final void triggerBeforeCommit(DefaultTransactionStatus status) { if (status.isNewSynchronization()) { if (status.isDebug()) { logger.trace("Triggering beforeCommit synchronization"); } TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly()); } } protected final void triggerBeforeCompletion(DefaultTransactionStatus status) { if (status.isNewSynchronization()) { if (status.isDebug()) { logger.trace("Triggering beforeCompletion synchronization"); } TransactionSynchronizationUtils.triggerBeforeCompletion(); } } private void triggerAfterCommit(DefaultTransactionStatus status) { if (status.isNewSynchronization()) { if (status.isDebug()) { logger.trace("Triggering afterCommit synchronization"); } TransactionSynchronizationUtils.triggerAfterCommit(); } } private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) { if (status.isNewSynchronization()) { List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations(); if (!status.hasTransaction() || status.isNewTransaction()) { if (status.isDebug()) { logger.trace("Triggering afterCompletion synchronization"); } // No transaction or new transaction for the current scope -> // invoke the afterCompletion callbacks immediately invokeAfterCompletion(synchronizations, completionStatus); } else if (!synchronizations.isEmpty()) { // 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.getTransaction(), synchronizations); } } } protected final void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) { TransactionSynchronizationUtils.invokeAfterCompletion(synchronizations, completionStatus); } private void cleanupAfterCompletion(DefaultTransactionStatus status) { status.setCompleted(); if (status.isNewSynchronization()) { TransactionSynchronizationManager.clear(); } if (status.isNewTransaction()) { doCleanupAfterCompletion(status.getTransaction()); } if (status.getSuspendedResources() != null) { if (status.isDebug()) { logger.debug("Resuming suspended transaction after completion of inner transaction"); } resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources()); } } //--------------------------------------------------------------------- // Template methods to be implemented in subclasses //--------------------------------------------------------------------- protected abstract Object doGetTransaction() throws TransactionException; protected boolean isExistingTransaction(Object transaction) throws TransactionException { return false; } protected boolean useSavepointForNestedTransaction() { return true; } protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException; protected Object doSuspend(Object transaction) throws TransactionException { throw new TransactionSuspensionNotSupportedException( "Transaction manager [" + getClass().getName() + "] does not support transaction suspension"); } protected void doResume(Object transaction, Object suspendedResources) throws TransactionException { throw new TransactionSuspensionNotSupportedException( "Transaction manager [" + getClass().getName() + "] does not support transaction suspension"); } protected boolean shouldCommitOnGlobalRollbackOnly() { return false; } protected void prepareForCommit(DefaultTransactionStatus status) { } protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException; protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException; protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException { throw new IllegalTransactionStateException( "Participating in existing transactions is not supported - when 'isExistingTransaction' " + "returns true, appropriate 'doSetRollbackOnly' behavior must be provided"); } protected void registerAfterCompletionWithExistingTransaction( Object transaction, List<TransactionSynchronization> synchronizations) throws TransactionException { logger.debug("Cannot register Spring after-completion synchronization with existing transaction - " + "processing Spring after-completion callbacks immediately, with outcome status 'unknown'"); invokeAfterCompletion(synchronizations, TransactionSynchronization.STATUS_UNKNOWN); } protected void doCleanupAfterCompletion(Object transaction) { } //--------------------------------------------------------------------- // Serialization support //--------------------------------------------------------------------- private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // Rely on default serialization; just initialize state after deserialization. ois.defaultReadObject(); // Initialize transient fields. this.logger = LogFactory.getLog(getClass()); } /** * Holder for suspended resources. * Used internally by {@code suspend} and {@code resume}. */ protected static class SuspendedResourcesHolder { private final Object suspendedResources; private List<TransactionSynchronization> suspendedSynchronizations; private String name; private boolean readOnly; private Integer isolationLevel; private boolean wasActive; private SuspendedResourcesHolder(Object suspendedResources) { this.suspendedResources = suspendedResources; } private SuspendedResourcesHolder( Object suspendedResources, List<TransactionSynchronization> suspendedSynchronizations, String name, boolean readOnly, Integer isolationLevel, boolean wasActive) { this.suspendedResources = suspendedResources; this.suspendedSynchronizations = suspendedSynchronizations; this.name = name; this.readOnly = readOnly; this.isolationLevel = isolationLevel; this.wasActive = wasActive; } } }
- hibernate事務管理
hibernate的事務實現類爲:HibernateTransactionManager - java持久化api事務
java 持久化事務的實現類爲:JpaTransactionManager - java原生api事務
java原生事務的實現類爲:JtaTransactionManager
- 事務的基本屬性:
- 事務的傳播行爲
spring的傳播行爲有七種,其分別爲:
PROPAGATION_REQUIRED :表示當前方法必須運行在事務中,如果當前有事務,則在當前事務中運行,如果不存在測新建事務。
PROPAGATION_SUPPORTS :表示當前方法不需要在事務中,如果當前存在事務,則在當前事務中運行,如果不存在,則不會啓動新事務,會正常運行。
PROPAGATION_MANDATORY :表示當前方法必須在事務中運行,如果當前不存在事務,則會拋出異常。
PROPAGATION_REQUIRED_NEW :表示當前方法必須在自己的事務中運行,首先會啓動自己的事務,如果當前存在事務,則會在執行該方法時掛起當前事務,
PROPAGATION_NOT_SUPPORTED :表示當前事務不支持事務,如果執行該方法並且當前存在事務,則會掛起當前事務。
PROPAGATION_NEVER :表示當前方法不支持事務,如果當前存在事務則會拋出異常。
PROPAGATION_NESTED :表示當前方法必須運行在事務中,如果當前存在事務則會在當前事務中運行,但是該方法的有自己獨立的事務管理,就是該方法的commit和rollback。如果不存在會啓動新的事務。 - 事務的隔離級別
ISOLATION_DEFAULT :數據庫默認的隔離級別。
ISOLATION_READ_UNCOMMITTED :最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致髒讀、幻讀或不可重複讀
ISOLATION_READ_COMMITTED :允許讀取併發事務已經提交的數據,可以阻止髒讀,但是幻讀或不可重複讀仍有可能發生
ISOLATION_REPEATABLE_READ :讀已提交導致的幻讀。對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止髒讀和不可重複讀,但幻讀仍有可能發生
ISOLATION_SERIALIZABLE :最高的隔離級別,完全服從ACID的隔離級別,確保阻止髒讀、不可重複讀以及幻讀,也是最慢的事務隔離級別,因爲它通常是通過完全鎖定事務相關的數據庫表來實現的 - 只讀
事務的第三個特性是它是否爲只讀事務。如果事務只對後端的數據庫進行該操作,數據庫可以利用事務的只讀特性來進行一些特定的優化。通過將事務設置爲只讀,你就可以給數據庫一個機會,讓它應用它認爲合適的優化措施。、 - 事務超時
爲了使應用程序很好地運行,事務不能運行太長的時間。因爲事務可能涉及對後端數據庫的鎖定,所以長時間的事務會不必要的佔用數據庫資源。事務超時就是事務的一個定時器,在特定時間內事務如果沒有執行完畢,那麼就會自動回滾,而不是一直等待其結束。 - 回滾事務
事務五邊形的最後一個方面是一組規則,這些規則定義了哪些異常會導致事務回滾而哪些不會。默認情況下,事務只有遇到運行期異常時纔會回滾,而在遇到檢查型異常時不會回滾(這一行爲與EJB的回滾行爲是一致的)
但是你可以聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。同樣,你還可以聲明事務遇到特定的異常不回滾,即使這些異常是運行期異常。
- 事務狀態
上面講到的調用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現,這個接口的內容如下:public interface TransactionStatus{ boolean isNewTransaction(); // 是否是新的事物 boolean hasSavepoint(); // 是否有恢復點 void setRollbackOnly(); // 設置爲只回滾 boolean isRollbackOnly(); // 是否爲只回滾 boolean isCompleted; // 是否已完成 }