1. Locate transaction invalidation at the source level

We know that Spring uses Spring AOP to implement the rollback of transactions, and Spring AOP uses dynamic proxies. At this point, we know that there are several conditions for transactions to be effective. The first is that there is a chain of enhancers that can be executed, which is similar to the various notifications we usually use. A default enhancer, TransactionInterceptor, is defined in Spring transactions. In this class @ EnableTransactionManagement annotation ProxyTransactionManagementConfiguration is registered. The second is that classes can be dynamically proxied. Let’s focus on specific classes to analyze the problem.

1.1 transaction classes cannot be proxied

Transaction classes can be created agency, the key is AbstractAutoProxyCreator postProcessAfterInitialization method of a class:

@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean ! = null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) ! = bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }Copy the code

If the Bean does not go this far, the current Bean cannot be dynamically proxied. Usually the console prints a statement is not eligible for Getting Processed by all BeanPostProcessors. Not eligible for auto-proxying). Under this information by PostProcessorRegistrationDelegate BeanPostProcessorChecker postProcessAfterInitialization printed, why will print this? The notes are clear:

BeanPostProcessor that logs an info message when a bean is created during
 BeanPostProcessor instantiation, i.e. when a bean is not eligible for
getting processed by all BeanPostProcessors.
Copy the code

BeanPostProcessor (BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor, BeanPostProcessor) The most common place is when Shiro configuration references normal beans ahead of time so that they cannot be proxied later, resulting in transaction invalidation.

1.2 the class does not have a qualified enhancer

We navigate to the transaction enhancer, the Invoke method of the TransactionInterceptor, and then to the specific processing details of the invokeWithinTransaction method of TransactionAspectSupport, This method is used to perform the rollback of the transaction. If debug finds that the method did not execute here, then the transaction is invalidated, most commonly by not adding transaction annotations to the method. Naturally, the current class does not meet the interception point for transaction notification, and the enhancer will not execute.

Here are some common reasons for transaction failure:

2. Reason of Spring transaction failure

2.1. Not Annotated Transactional

2.2 method classes are not added to the IOC container

The entire transaction environment is dependent on the IOC container and the classes are not handled.

2.3. Exceptions generated by methods are not RuntimeExceptions

We will gaze orientation to the following agent, TransactionAspectSupport completeTransactionAfterThrowing method

if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
}
Copy the code

DefaultTransactionAttribute rollbackOn method:

public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}
Copy the code

2.4. Classes are preinitialized

If the console prints is not eligible for getting Processed by all BeanPostProcessors (for example: If you eligible for auto-proxying, watch out for this Bean because it cannot be dynamically proxied. The solution is to remove this Bean from some special classes, such as configuration classes.

3. Processing of Spring propagation behavior

The specific treatment in AbstractPlatformTransactionManager AbstractPlatformTransactionManager method.

4. Spring isolation level

We know that Spring delegates isolation levels to the underlying database, so in Spring the details are to set the isolation level, The concrete method in AbstractPlatformTransactionManager getTransaction doBegin (transaction, definition); Inside, you can find an implementation class to take a look.

Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
Copy the code

The Transactional annotation rollbackFor

If configuration rollbackFor properties in the annotations, then walking to txInfo. The transactionAttribute. RollbackOn (ex) here, It will into RuleBasedTransactionAttribute rollbackOn method:

public boolean rollbackOn(Throwable ex) { if (logger.isTraceEnabled()) { logger.trace("Applying rules to determine whether transaction should rollback on " + ex); } RollbackRuleAttribute winner = null; int deepest = Integer.MAX_VALUE; // If rollbackFor is configured, there is a rollback rule. if (this.rollbackRules ! = null) { for (RollbackRuleAttribute rule : {this.rollbackrules) {this.rollbackrules) {this.rollbackrules () {this.rollbackrules () {this.rollbackrules () {this.rollbackrules () {this.rollbackrules () {this.rollbackrules (); Int depth = rule-depth (ex); if (depth >= 0 && depth < deepest) { deepest = depth; winner = rule; } } } if (logger.isTraceEnabled()) { logger.trace("Winning rollback rule is: " + winner); } // User superclass behavior (rollback on unchecked) if no rule matches. If (winner == null) { logger.trace("No relevant rollback rule found: applying default rules"); return super.rollbackOn(ex); } return ! (winner instanceof NoRollbackRuleAttribute); }Copy the code

So what happens if we configure it as Throwable?

// If the rule is Throwable, it will return a positive number on success. If no match is found, it will go to -1. private int getDepth(Class<? > exceptionClass, int depth) { if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; } // If we've gone as far as we can go and haven't found it... if (exceptionClass == Throwable.class) { return -1; } return getDepth(exceptionClass.getSuperclass(), depth + 1); }Copy the code

Pre-knowledge complements Spring transaction principles