@Transactional(rollbackFor = Exception.class) principle
summary
1. Dynamic agent
Generation of proxy classes by cglib dynamic proxy
Click to view the code//Proxy object: //org.springframework.jdbc.datasource.DataSourceTransactionManager //Open transaction target.invoke1(){ insertMethod1() insertMethod2() updateMethod1() } //Transaction commit
Transaction operations must use the same connection object. How to ensure
spring needs to obtain a connection connection every time it operates db, and each connection is placed in ThreadLocal.
DataSourceTransactionManager class doGetTransaction protected Object doGetTransaction() { DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject(); txObject.setSavepointAllowed(this.isNestedTransactionAllowed()); //Here is the connection to get the threadLocal thread binding in the transaction synchronization manager ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource()); txObject.setConnectionHolder(conHolder, false); return txObject; }
2. Transactional principle
Click to view the code1.Introducing data sources, database drivers Spring-jdbc modular 2.Transaction operations plus@Transactional 3.Turn on transaction function @EnableTransactionManagement 4.EnableTransactionManagement Introduced a ImportSelector 5.selector to spring Two components are registered in the container: AutoProxyRegistrar and ProxyTransactionManagementConfiguration 5.1.AutoProxyRegistrar Main utilization ImporyBeanDefinitionRegistrar Registered one InfrastructureAdvisorAutoProxyCreator assembly InfrastructureAdvisorAutoProxyCreator Realized spring The post processor interface enables the methods that need transaction processing to have the ability of transaction (i.e. to spring Added 5 to container.2 Inside BeanFactoryTransactionAttributeSourceAdvisor,(enhanced) 5.2 ProxyTransactionManagementConfiguration Two are registered with the container bean: AnnotationTransactionAttributeSource and TransactionInterceptor put to BeanFactoryTransactionAttributeSourceAdvisor in 5.2.1 AnnotationTransactionAttributeSource Is used to parse business code@Transactional Annotation meta information, propagation attribute, timeout, isolation level 5.2.2 TransactionInterceptor The transaction attribute information and the transaction manager are saved; It's a MethodInterceptor; 6.When the business code is executed, the registered interceptor TransactionInterceptor Carry out invoke method 7.TransactionAspectSupport Under class invokeWithinTransaction The method is a concrete implementation
Transactional principle
1. Use annotation @ Transactional
Click to view the code//Step 1: import related database dependencies; //Step 2: add transaction annotation; @Transactional(rollbackFor = {Exception.class}) public void updateAccount(int id) { int rows = accounMapper.deduction(id); if (rows > 0) { System.out.println("Seckill inventory modified successfully"); insertGoodOrder(); } else { System.out.println("Second kill modification failed"); } }
2. Open the transaction manager and register the transaction manager
Click to view the code@Configuration @ComponentScan("top.yonyong.db") //Step 3: enable the transaction management function to make @ Transactional effective @EnableTransactionManagement public class DataSourceConfig { //Creating a data source this c3p0 encapsulates the implementation of JDBC and datasource interfaces @Bean public DataSource dataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); dataSource.setPassword("root"); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql//localhost:3306/order"); return dataSource; } @Bean //Step 4: register the transaction manager bean public PlatformTransactionManager platformTransactionManager() throws PropertyVetoException { return new DataSourceTransactionManager(dataSource()); } @Bean //Step 5: JDBC template can simplify the operation of adding, querying, modifying and deleting public JdbcTemplate jdbcTemplate() throws PropertyVetoException { return new JdbcTemplate(dataSource()); } }
3. Give code transaction capability
Click to view the codepackage org.springframework.transaction.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.Import; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented //Use Import to add a Selector component to the container; @Import({TransactionManagementConfigurationSelector.class}) public @interface EnableTransactionManagement { //Use JDK or Cglib dynamic proxy //true: Ciglib dynamic proxy false JDK dynamic proxy, false by default boolean proxyTargetClass() default false; //What is the default transaction enhancer mode: agent AdviceMode mode() default AdviceMode.PROXY; //Lowest priority int order() default 2147483647; }
4. @Import and [@ importselector + @ importbeandefinitionregister]
These two annotations often appear in the spring source code:
ImportSelector is an interface. You only need to implement the selectImport() method and return an array to batch register Bean instances for the container;
Importbeandefinitionregister is also an interface. You only need to implement the registorBeanDefinition() method to add bean instances to the container;
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { /** * TransactionManagementConfigurationSelector Implements the AdviceModeImportSelector * The AdviceModeImportSelector implements the ImportSelector, so it has the ability to register bean s with the spring container */ @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: //Default proxy mode //Register autoproxyregister and ProxyTransactionManagementConfiguration classes return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } } private String determineTransactionAspectClass() { return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ? TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME : TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME); } }
4.1 autoproxyregister class
//Add a component to the container through ImportBeanDefinitionRegistrar: infrastructure advisor autoproxycreator
Click to view the codepublic class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { private final Log logger = LogFactory.getLog(getClass()); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean candidateFound = false; Set<String> annoTypes = importingClassMetadata.getAnnotationTypes(); for (String annoType : annoTypes) { if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) { candidateFound = true; if (mode == AdviceMode.PROXY) {//See what components it registers in the container AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); if ((Boolean) proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } @Nullable //Add the infrastructure advisor autoproxycreator component to the container public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,Object source) { //Here we begin to register the container: infrastructure advisor autoproxycreator transaction dynamic agent creator component return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source); }
After clicking on it, I found that the infrastructure advisor autoproxycreator actually implements the spring post processor to create enhanced bean s
Click to view the codepublic class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator { public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator { public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {} public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor { public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { @Nullable //Bean instance pre enhancement default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable //Bean instance post enhancement default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
4.2 ProxyTransactionManagementConfiguration class
Click to view the codeClick to view the code@Configuration(proxyBeanMethods = false) public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { //Start metadata attribute resolution for the transaction @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) //Some enhancements to attribute meta information, such as some parameters set in the annotation: propagation attribute, rollback condition, rollback for, etc //@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor( TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) { //Attribute enhancement of our transactions; BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource); advisor.setAdvice(transactionInterceptor); if (this.enableTx != null) { advisor.setOrder(this.enableTx.<Integer>getNumber("order")); } return advisor; } //Encapsulate the attribute value of the transaction, register it as a bean, and use it as a formal parameter for the above method @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { //Created this object return new AnnotationTransactionAttributeSource(); } //It is mainly used to save the information of transaction configuration attributes and encapsulate them into a TransactionInterceptor @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor( TransactionAttributeSource transactionAttributeSource) { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } }
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable { public AnnotationTransactionAttributeSource() { this(true); } //I don't know what to do, but I will eventually add a spring transaction annotation parser public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { this.publicMethodsOnly = publicMethodsOnly; if (jta12Present || ejb3Present) { this.annotationParsers = new LinkedHashSet<>(4); this.annotationParsers.add(new SpringTransactionAnnotationParser()); if (jta12Present) { this.annotationParsers.add(new JtaTransactionAnnotationParser()); } if (ejb3Present) { this.annotationParsers.add(new Ejb3TransactionAnnotationParser()); } } else { this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser()); } } } public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable { @Override public boolean isCandidateClass(Class<?> targetClass) { return AnnotationUtils.isCandidateClass(targetClass, Transactional.class); } @Override @Nullable public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) { AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( element, Transactional.class, false, false); if (attributes != null) { return parseTransactionAnnotation(attributes); } else { return null; } } public TransactionAttribute parseTransactionAnnotation(Transactional ann) { return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false)); } protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); //Setting of transaction propagation properties Propagation propagation = attributes.getEnum("propagation"); rbta.setPropagationBehavior(propagation.value()); //Setting of isolation property of transaction Isolation isolation = attributes.getEnum("isolation"); rbta.setIsolationLevel(isolation.value()); //Timeout setting for transactions rbta.setTimeout(attributes.getNumber("timeout").intValue()); rbta.setReadOnly(attributes.getBoolean("readOnly")); rbta.setQualifier(attributes.getString("value")); //Rollback condition settings for transactions List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(); for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } //Set the exception class name array that needs to be rolled back. When an exception in the specified exception name array is thrown in the method, the transaction is rolled back for (String rbRule : attributes.getStringArray("rollbackForClassName")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } //Set the exception class name array that will not be rolled back. When an exception in the specified exception name array is thrown in the method, the transaction will not be rolled back for (String rbRule : attributes.getStringArray("noRollbackForClassName")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } rbta.setRollbackRules(rollbackRules); return rbta; } @Override public boolean equals(@Nullable Object other) { return (this == other || other instanceof SpringTransactionAnnotationParser); } @Override public int hashCode() { return SpringTransactionAnnotationParser.class.hashCode(); } }
Here: the initialization task of the first stage is completed. The core tasks are as follows:
Use the transaction management configuration selector to import two components into the container:
(1)InfrastructureAdvisorAutoProxyCreator
Autoproxyregister registers an infrastructure advisor autoproxycreator component in the container, which is actually a post processor and a dynamic agent creator. The post processor and dynamic agent are used to enhance the target method, return an enhanced instance object, and the agent object execution method is called by the interceptor chain;
(2)ProxyTransactionManagementConfiguration
Obtain the transaction manager, process the meta information of the transaction, execute the target method itself, mainly the proxy implementation of the transaction capability details, and then register the transaction enhancer Bean generated by the configuration in the container;
5. Call execution
Start calling transactioninterceptor Invoke () method, which is the core of transaction execution
5.1 TransactionInterceptor class
Click to view the code//After interception, it is encapsulated into a MethodInterceptor, which saves the transaction information, which is the same as the logic of aop public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { public TransactionInterceptor() { } public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) { setTransactionManager(ptm); setTransactionAttributes(attributes); } public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) { setTransactionManager(ptm); setTransactionAttributeSource(tas); } //Call of dynamic proxy @Override @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... //The method of TransactionAspectSupport class is called here return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); } //--------------------------------------------------------------------- // Serialization support //--------------------------------------------------------------------- private void writeObject(ObjectOutputStream oos) throws IOException { // Rely on default serialization, although this class itself doesn't carry state anyway... oos.defaultWriteObject(); // Deserialize superclass fields. oos.writeObject(getTransactionManagerBeanName()); oos.writeObject(getTransactionManager()); oos.writeObject(getTransactionAttributeSource()); oos.writeObject(getBeanFactory()); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); setTransactionManagerBeanName((String) ois.readObject()); setTransactionManager((PlatformTransactionManager) ois.readObject()); setTransactionAttributeSource((TransactionAttributeSource) ois.readObject()); setBeanFactory((BeanFactory) ois.readObject()); } }
5.2 TransactionAspectSupport class
Click to view the code//TransactionAspectSupport class invokeWithinTransaction method @Nullable protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. //(1) : get the transaction attribute information (propagation = Propagation.REQUIRED, rollbackFor = Exception.class) set (in the second step of practice) and load it directly from memory; TransactionAttributeSource tas = getTransactionAttributeSource(); final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); //(2) : get the registered transaction manager - PlatformTransactionManager and load it into the container; final TransactionManager tm = determineTransactionManager(txAttr); if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) { ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> { if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) { throw new TransactionUsageException( "Unsupported annotated transaction on suspending function detected: " + method + ". Use TransactionalOperator.transactional extensions instead."); } ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType()); if (adapter == null) { throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " + method.getReturnType()); } return new ReactiveTransactionSupport(adapter); }); return txSupport.invokeWithinTransaction( method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm); } PlatformTransactionManager ptm = asPlatformTransactionManager(tm); final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. //Get the transaction manager and close the automatic transaction submission; TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification); Object retVal; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. //(4) : start executing the target method itself doBusiness(); retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception //(4.1): rollback if an exception is thrown during execution completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... TransactionStatus status = txInfo.getTransactionStatus(); if (status != null && txAttr != null) { retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } } //(4.2) if the execution is successful, submit the transaction; commitTransactionAfterReturning(txInfo); return retVal; } else { final ThrowableHolder throwableHolder = new ThrowableHolder(); // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status); try { Object retVal = invocation.proceedWithInvocation(); if (vavrPresent && VavrDelegate.isVavrTry(retVal)) { // Set rollback-only in case of Vavr failure matching our rollback rules... retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status); } return retVal; } 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. throwableHolder.throwable = ex; return null; } } finally { cleanupTransactionInfo(txInfo); } }); // Check result state: It might indicate a Throwable to rethrow. if (throwableHolder.throwable != null) { throw throwableHolder.throwable; } return result; } catch (ThrowableHolderException ex) { throw ex.getCause(); } catch (TransactionSystemException ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); ex2.initApplicationException(throwableHolder.throwable); } throw ex2; } catch (Throwable ex2) { if (throwableHolder.throwable != null) { logger.error("Application exception overridden by commit exception", throwableHolder.throwable); } throw ex2; } } }
5.3 transaction manager registered when initializing datasource
Click to view the codepublic class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { //Injection data source private DataSource dataSource; //The default construction method of the data source transaction processor is to create an instance of the data source transaction processor and set to allow nested transactions public DataSourceTransactionManager() { setNestedTransactionAllowed(true); } //Create a data source transaction processor instance based on the given data source public DataSourceTransactionManager(DataSource dataSource) { this(); setDataSource(dataSource); afterPropertiesSet(); } //set up data sources public void setDataSource(DataSource dataSource) { if (dataSource instanceof TransactionAwareDataSourceProxy) { //If the data source is a transaction wrapper data source agent, get the target data source of the transaction wrapper agent this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); } else { this.dataSource = dataSource; } } //Get data source public DataSource getDataSource() { return this.dataSource; } //Callback function of data source transaction processor object construction method public void afterPropertiesSet() { if (getDataSource() == null) { throw new IllegalArgumentException("Property 'dataSource' is required"); } } public Object getResourceFactory() { return getDataSource(); } //To create a transaction, the Connection completes the transaction work for the database. This method puts the / / Connection object of the database into a ConnectionHolder object, and then encapsulates it into a //In DataSourceTransactionObject object protected Object doGetTransaction() { //Create data source transaction object DataSourceTransactionObject txObject = new DataSourceTransactionObject(); //Set the data source transaction object to use savepoints for nested transactions txObject.setSavepointAllowed(isNestedTransactionAllowed()); //Get the object storing the database Connection from the transaction management container ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource); txObject.setConnectionHolder(conHolder, false); return txObject; } //Determine whether a transaction already exists protected boolean isExistingTransaction(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; //Judge according to the isTransactionActive property of the ConnectionHolder where the database connection is stored return (txObject.getConnectionHolder() != null && txObject.getConnectionHolder().isTransactionActive()); } //Method of processing transaction start protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { //If the ConnectionHolder of the data source transaction object is null or the transaction is synchronized if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { //Gets the database connection for the current data source Connection newCon = this.dataSource.getConnection(); if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } //Setting ConnectionHolder for data source transaction object txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } //Set the transaction synchronization txobject of the data source transaction object getConnectionHolder(). setSynchronizedWithTransaction(true); //Gets the database connection of the data source transaction object con = txObject.getConnectionHolder().getConnection(); //Obtain the transaction isolation level of the database connection according to the data connection and transaction attributes Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); //Set the transaction isolation level for the data source transaction object txObject.setPreviousIsolationLevel(previousIsolationLevel); //Turn off automatic transaction commit if the database connection has the automatic transaction commit attribute set if (con.getAutoCommit()) { //Automatically connect to the data source transaction object that saves the database connection settings txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } //Set the automatic transaction submission property of database connection to false, that is, automatic transaction submission is prohibited con.setAutoCommit(false); } //Activates the transaction configuration of the current data source transaction object txObject.getConnectionHolder().setTransactionActive(true); //Gets the timeout length of the transaction configuration int timeout = determineTimeout(definition); //If the timeout length configured by the transaction is not equal to the default timeout length of the transaction if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { //Set timeout duration for data source transaction object txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } //Bind the current database Connection to the thread if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); } } catch (Exception ex) { DataSourceUtils.releaseConnection(con, this.dataSource); throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } } //Transaction pending protected Object doSuspend(Object transaction) { //Get transaction object DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; //Set ConnectionHolders in the transaction object to null txObject.setConnectionHolder(null); ConnectionHolder conHolder = (ConnectionHolder) //Unbind the transaction object from the current thread transactionsynchronizationmanager unbindResource(this.dataSource); return conHolder; } //Transaction recovery protected void doResume(Object transaction, Object suspendedResources) { //Gets the ConnectionHolder of the suspended transaction ConnectionHolder conHolder = (ConnectionHolder) suspendedResources; //Rebind the transaction object to the current thread TransactionSynchronizationManager.bindResource(this.dataSource, conHolder); } //Transaction commit protected void doCommit(DefaultTransactionStatus status) { //Get transaction object DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); //Get database connection through transaction object Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Committing JDBC transaction on Connection [" + con + "]"); } try { //Manual transaction commit using database connection con.commit(); } catch (SQLException ex) { throw new TransactionSystemException("Could not commit JDBC transaction", ex); } } //Transaction rollback protected void doRollback(DefaultTransactionStatus status) { //Get transaction object DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); //Get database connection through transaction object Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug("Rolling back JDBC transaction on Connection [" + con + "]"); } try { //Complete the transaction rollback operation by calling the rollback method of the database connection con.rollback(); } catch (SQLException ex) { throw new TransactionSystemException("Could not roll back JDBC transaction", ex); } } //Set rollback protected void doSetRollbackOnly(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); if (status.isDebug()) { logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only"); } txObject.setRollbackOnly(); } //Clear operation after operation protected void doCleanupAfterCompletion(Object transaction) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; //Remove the ConnectionHolder bound by the current thread if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.unbindResource(this.dataSource); } Connection con = txObject.getConnectionHolder().getConnection(); try { //If the transaction object saves the automatic transaction commit attribute, set the automatic transaction commit attribute of the database connection if (txObject.isMustRestoreAutoCommit()) { con.setAutoCommit(true); } //Reset database connection after transaction DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel()); } catch (Throwable ex) { logger.debug("Could not reset JDBC Connection after transaction", ex); } //If there is a new ConnectionHolder in the transaction object if (txObject.isNewConnectionHolder()) { if (logger.isDebugEnabled()) { logger.debug("Releasing JDBC Connection [" + con + "] after transaction"); } //Release database connection DataSourceUtils.releaseConnection(con, this.dataSource); } //Clear the ConnectionHolder of the transaction object txObject.getConnectionHolder().clear(); } //Data source, transaction object, internal class private static class DataSourceTransactionObject extends JdbcTransactionObjectSupport { //Is there a new ConnectionHolder private boolean newConnectionHolder; //Save auto submit private boolean mustRestoreAutoCommit; //Set ConnectionHolder public void setConnectionHolder(ConnectionHolder connectionHolder, boolean newConnectionHolder) { //Setting ConnectionHolder for parent class JdbcTransactionObjectSupport super.setConnectionHolder(connectionHolder); this.newConnectionHolder = newConnectionHolder; } public boolean isNewConnectionHolder() { return this.newConnectionHolder; } //Call the relevant methods of the parent class JdbcTransactionObjectSupport to query the existence of charging transactions public boolean hasTransaction() { return (getConnectionHolder() != null && getConnectionHolder().isTransactionActive()); } //Set whether to save auto submit public void setMustRestoreAutoCommit(boolean mustRestoreAutoCommit) { this.mustRestoreAutoCommit = mustRestoreAutoCommit; } public boolean isMustRestoreAutoCommit() { return this.mustRestoreAutoCommit; } //Set whether to roll back the processing only if the database connection fails public void setRollbackOnly() { getConnectionHolder().setRollbackOnly(); } public boolean isRollbackOnly() { return getConnectionHolder().isRollbackOnly(); } } }