The emergence of SpringBoot makes it very easy to use transactions in projects. There are two ways to use them, which are suitable for annotation transactions (declarative transaction management) of small projects and global transactions of large projects.
1. Annotation transactions. (secondary)
There are only two steps to annotate transactions. Turn on the transaction annotation function and use the transaction annotation function, and only one annotation is used in each step.
Step 1: enable transaction annotation function @ EnableTransactionManagement
Add the annotation @ EnableTransactionManagement in the main startup class.
package com.gx; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableTransactionManagement //Enable transaction annotation @SpringBootApplication public class Ch09SpringbootTransAnnoApplication { public static void main(String[] args) { SpringApplication.run(Ch09SpringbootTransAnnoApplication.class, args); } }
Step 2: use the transaction annotation function @ Transactional
Add @ Transactional to the service interface implementation class or interface implementation class method.
package com.gx.service.impl; import com.gx.service.StudentService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.gx.domain.Student; @Service public class StudentServiceImpl implements StudentService { @Transactional //Use annotation transactions @Override public String addStudent(Student student) { //Business method } }
Note: @ Transactional must be added to the public modified method.
2. Global transactions. (main)
SpringBoot global transactions mainly use AOP aspect programming.
Step 1: create the facet class @ Aspect.
Create an ordinary class. After adding @ Aspect, this class is a faceted class, which is used to write transaction functions. At the same time, you also need to define the section class as a Configuration class and add the annotation @ Configuration.
Notes: 1, @ Aspect defines this class as a facet class, and the current class is read by the container as a facet.
2. @ Configuration defines this class as a Configuration class, configures the spring container, and injects bean s
package com.gx.config; import org.aspectj.lang.annotation.Aspect; import org.springframework.context.annotation.Configuration; @Aspect //Define the facet class and identify the current class as a facet for the container to read @Configuration //Define configuration class public class TransactionAdviceConfig { //Enhancement method }
Step 2: create the first method, return the transaction interceptor, declare the transaction attribute of the business method, and register it in the bean.
-
If you need to return the transaction interceptor, you need a new transaction interceptor. According to the class of TransactionInterceptor, there are only two methods to create a TransactionInterceptor.
public TransactionInterceptor() { } public TransactionInterceptor(TransactionManager ptm, TransactionAttributeSource tas) { this.setTransactionManager(ptm); this.setTransactionAttributeSource(tas); }
To use transactions, there must be transaction manager and transaction attribute transaction attributesource. When configuring the transaction attribute, it is generally filtered by the name of the method, such as add *, save *, delete *, etc., so the transaction attribute uses its subclass NameMatchTransactionAttributeSource.
//Transaction manager @Autowired private TransactionManager transactionManager; @Bean public TransactionInterceptor txAdvice() { //Declare an object that configures transaction properties by method name NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); //Returns a transaction interceptor return new TransactionInterceptor(transactionManager, source); }
-
Set the transaction attribute of business method by method name.
There are only two methods we use frequently in the NameMatchTransactionAttributeSource class. setNameMap is actually a collection of addtransactionalmethods.
//Set transaction attributes for multiple methods or multi class methods through the map set public void setNameMap(Map<String, TransactionAttribute> nameMap) { nameMap.forEach(this::addTransactionalMethod); } //Set the transaction attribute of a method or a class of methods through the method name or a class of method name and transaction attribute public void addTransactionalMethod(String methodName, TransactionAttribute attr) { if (logger.isDebugEnabled()) { logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]"); } if (this.embeddedValueResolver != null && attr instanceof DefaultTransactionAttribute) { ((DefaultTransactionAttribute) attr).resolveAttributeStrings(this.embeddedValueResolver); } this.nameMap.put(methodName, attr); }
-
Set transaction properties.
TransactionAttribute: transaction attribute. There are many implementation classes. Generally, the rule-based transaction attribute RuleBasedTransactionAttribute is used. Most of the functions are in its parent class DefaultTransactionDefinition. Only a small part can be written, and other transaction attributes can be written according to business requirements.
//Configure a transaction attribute (read-only) RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute(); //Read only readOnlyTx.setReadOnly(true); //Transaction propagation behavior readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); //Set business method transaction attributes by method name Map<String, TransactionAttribute> txMap = new HashMap<>(); txMap.put("get*", readOnlyTx);
Then add it to NameMatchTransactionAttributeSource
source.setNameMap(txMap);
Method of setting transaction properties
1,Transaction propagation behavior setPropagationBehavior(); 2,Transaction isolation level setIsolationLevel(); 3,Transaction timeout setTimeout(); 4,Transaction read only setReadOnly(); 5,Set transaction name setName(); 6,Set rollback rules setRollbackRules();
-
Transaction properties. (Extended)
Transaction propagation behavior
Transaction behavior explain PROPAGATION_REQUIRED Current transaction is supported. It is assumed that there is no current transaction. Just create a new transaction PROPAGATION_SUPPORTS Supports the current transaction. If there is no transaction at present, it will run in a non transaction mode PROPAGATION_MANDATORY Supports the current transaction. If there is no transaction, an exception will be thrown PROPAGATION_REQUIRES_NEW Create a new transaction, assuming that there is a transaction currently. Suspend the current transaction PROPAGATION_NOT_SUPPORTED Run the operation in a non transactional manner. If there is a current transaction, suspend the current transaction PROPAGATION_NEVER Run in non transactional mode. If there is a transaction currently, an exception will be thrown PROPAGATION_NESTED If a transaction currently exists, it is executed within a nested transaction. If there is no current transaction, execute the same as the deployment_ Required similar operations. There are only four transaction isolation levels, but spring provides five. (the isolation level name of spring is different from that in the database)
Isolation level explain Dirty reading Unreal reading Non repeatable reading ISOLATION_DEFAULT The default isolation level. The transaction isolation level supported by each database is different, which changes according to the database used. - - - ISOLATION_READ_UNCOMMITTED Read uncommitted, that is, data that has not been submitted can be read. yes yes yes ISOLATION_READ_COMMITTED Read submitted, that is, you can read the submitted data. no yes yes ISOLATION_REPEATABLE_READ Repeat reading, that is, lock after the data is read out. If this transaction does not end, other transactions cannot operate this data. no no yes ISOLATION_SERIALIZABLE Serialization is the highest transaction isolation level. No matter how many transactions, all sub transactions of one transaction can be executed after running all sub transactions of another transaction one by one. no no no
Step 3: configure the Advisor to enhance the transaction.
-
Create an adapter (a normal method returns to Advisor).
Advisor is composed of pointcut and Advice, but advisor is an interface. It needs to implement the class DefaultPointcutAdvisor and pass in parameter pointcut and Adivce.
@Bean public Advisor txAdviceAdvisor() { //Enhance transactions to associate pointcuts and transaction attributes return new DefaultPointcutAdvisor(breakthrough point, Advice); }
-
Configure pointcuts.
//Configure pointcut expression: specify which classes in the package use transactions and set them as static class constants private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.***.service.*.*(..))"; //Put the following contents in the adapter method //Configure transaction pointcut expression AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
-
Associate pointcuts and Advice.
//Enhance transactions to associate pointcuts and transaction attributes return new DefaultPointcutAdvisor(pointcut, txAdvice());
Step 4: restart the test.
Finally, I present all the codes of aop global transaction
package com.gx.config; import org.aspectj.lang.annotation.Aspect; import org.springframework.aop.Advisor; import org.springframework.aop.aspectj.AspectJExpressionPointcut; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionManager; import org.springframework.transaction.interceptor.*; import javax.sql.DataSource; import java.util.Collections; import java.util.HashMap; import java.util.Map; @Aspect //Define the facet class and identify the current class as a facet for the container to read @Configuration //Define configuration class public class TransactionAdviceConfig { //The transaction timeout is 10 seconds private static final int TX_METHOD_TIMEOUT = 10; //Configure pointcut expressions: specify which classes in the package use transactions private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.***.service.*.*(..))"; //Transaction manager @Autowired private TransactionManager transactionManager; /** * Declare transaction properties of business methods */ @Bean public TransactionInterceptor txAdvice() { /** * Read only transactions are configured here */ RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute(); readOnlyTx.setReadOnly(true);//Read only readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);//Propagation behavior of transactions /** * Transaction is required * If there is a current transaction, use the current transaction. If there is no current transaction, start a new transaction */ RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(); //Check exceptions are also rolled back requiredTx.setRollbackRules( Collections.singletonList(new RollbackRuleAttribute(Exception.class))); requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); requiredTx.setTimeout(TX_METHOD_TIMEOUT); /** * Execute without transactions and suspend any existing transactions */ RuleBasedTransactionAttribute noTx = new RuleBasedTransactionAttribute(); noTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED); /** * Transaction corresponding to setting method */ Map<String, TransactionAttribute> txMap = new HashMap<>(); //Read only transaction txMap.put("get*", readOnlyTx); txMap.put("query*", readOnlyTx); txMap.put("find*", readOnlyTx); txMap.put("list*", readOnlyTx); txMap.put("count*", readOnlyTx); txMap.put("exist*", readOnlyTx); txMap.put("search*", readOnlyTx); txMap.put("fetch*", readOnlyTx); //No transaction txMap.put("noTx*", noTx); //Write transaction txMap.put("add*", requiredTx); txMap.put("save*", requiredTx); txMap.put("insert*", requiredTx); txMap.put("update*", requiredTx); txMap.put("modify*", requiredTx); txMap.put("delete*", requiredTx); NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); source.setNameMap(txMap); //Return transaction interceptor return new TransactionInterceptor(transactionManager, source); } @Bean public Advisor txAdviceAdvisor() { //Configure transaction pointcut expression AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(AOP_POINTCUT_EXPRESSION); //Enhance transactions to associate pointcuts and transaction attributes return new DefaultPointcutAdvisor(pointcut, txAdvice()); } }