Last active
May 31, 2018 07:23
-
-
Save vijayrawatsan/753f2896d18fd982453a3c3b99867ffd to your computer and use it in GitHub Desktop.
Spring @transaction handling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean { | |
... | |
... | |
/** | |
* General delegate for around-advice-based subclasses, delegating to several other template | |
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager} | |
* as well as regular {@link PlatformTransactionManager} implementations. | |
* @param method the Method being invoked | |
* @param targetClass the target class that we're invoking the method on | |
* @param invocation the callback to use for proceeding with the target invocation | |
* @return the return value of the method, if any | |
* @throws Throwable propagated from the target invocation | |
*/ | |
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) | |
throws Throwable { | |
// If the transaction attribute is null, the method is non-transactional. | |
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); | |
final PlatformTransactionManager tm = determineTransactionManager(txAttr); | |
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); | |
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { | |
// Standard transaction demarcation with getTransaction and commit/rollback calls. | |
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); | |
Object retVal = null; | |
try { | |
// This is an around advice: Invoke the next interceptor in the chain. | |
// This will normally result in a target object being invoked. | |
retVal = invocation.proceedWithInvocation(); | |
} | |
catch (Throwable ex) { | |
// target invocation exception | |
completeTransactionAfterThrowing(txInfo, ex); | |
throw ex; | |
} | |
finally { | |
cleanupTransactionInfo(txInfo); | |
} | |
commitTransactionAfterReturning(txInfo); | |
return retVal; | |
} | |
else { | |
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. | |
try { | |
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, | |
new TransactionCallback<Object>() { | |
@Override | |
public Object doInTransaction(TransactionStatus status) { | |
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); | |
try { | |
return invocation.proceedWithInvocation(); | |
} | |
catch (Throwable ex) { | |
if (txAttr.rollbackOn(ex)) { | |
// A RuntimeException: will lead to a rollback. | |
if (ex instanceof RuntimeException) { | |
throw (RuntimeException) ex; | |
} | |
else { | |
throw new ThrowableHolderException(ex); | |
} | |
} | |
else { | |
// A normal return value: will lead to a commit. | |
return new ThrowableHolder(ex); | |
} | |
} | |
finally { | |
cleanupTransactionInfo(txInfo); | |
} | |
} | |
}); | |
// Check result: It might indicate a Throwable to rethrow. | |
if (result instanceof ThrowableHolder) { | |
throw ((ThrowableHolder) result).getThrowable(); | |
} | |
else { | |
return result; | |
} | |
} | |
catch (ThrowableHolderException ex) { | |
throw ex.getCause(); | |
} | |
} | |
} | |
/** | |
* Execute after successful completion of call, but not after an exception was handled. | |
* Do nothing if we didn't create a transaction. | |
* @param txInfo information about the current transaction | |
*/ | |
protected void commitTransactionAfterReturning(TransactionInfo txInfo) { | |
if (txInfo != null && txInfo.hasTransaction()) { | |
if (logger.isTraceEnabled()) { | |
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); | |
} | |
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); | |
} | |
} | |
/** | |
* Handle a throwable, completing the transaction. | |
* We may commit or roll back, depending on the configuration. | |
* @param txInfo information about the current transaction | |
* @param ex throwable encountered | |
*/ | |
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) { | |
if (txInfo != null && txInfo.hasTransaction()) { | |
if (logger.isTraceEnabled()) { | |
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + | |
"] after exception: " + ex); | |
} | |
if (txInfo.transactionAttribute.rollbackOn(ex)) { | |
try { | |
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); | |
} | |
catch (TransactionSystemException ex2) { | |
logger.error("Application exception overridden by rollback exception", ex); | |
ex2.initApplicationException(ex); | |
throw ex2; | |
} | |
catch (RuntimeException ex2) { | |
logger.error("Application exception overridden by rollback exception", ex); | |
throw ex2; | |
} | |
catch (Error err) { | |
logger.error("Application exception overridden by rollback error", ex); | |
throw err; | |
} | |
} | |
else { | |
// We don't roll back on this exception. | |
// Will still roll back if TransactionStatus.isRollbackOnly() is true. | |
try { | |
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); | |
} | |
catch (TransactionSystemException ex2) { | |
logger.error("Application exception overridden by commit exception", ex); | |
ex2.initApplicationException(ex); | |
throw ex2; | |
} | |
catch (RuntimeException ex2) { | |
logger.error("Application exception overridden by commit exception", ex); | |
throw ex2; | |
} | |
catch (Error err) { | |
logger.error("Application exception overridden by commit error", ex); | |
throw err; | |
} | |
} | |
} | |
} | |
/** | |
* Reset the TransactionInfo ThreadLocal. | |
* <p>Call this in all cases: exception or normal return! | |
* @param txInfo information about the current transaction (may be {@code null}) | |
*/ | |
protected void cleanupTransactionInfo(TransactionInfo txInfo) { | |
if (txInfo != null) { | |
txInfo.restoreThreadLocalStatus(); | |
} | |
} | |
... | |
... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { | |
... | |
... | |
@Override | |
public Object invoke(final MethodInvocation invocation) throws Throwable { | |
// Work out the target class: may be {@code null}. | |
// The TransactionAttributeSource should be passed the target class | |
// as well as the method, which may be from an interface. | |
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); | |
// Adapt to TransactionAspectSupport's invokeWithinTransaction... | |
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() { | |
@Override | |
public Object proceedWithInvocation() throws Throwable { | |
return invocation.proceed(); | |
} | |
}); | |
} | |
... | |
... | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment