How to implement Spring transactions
The transaction base is based on database transactions and AOP mechanisms.
2. First, for beans that use @Transactional annotation, Spring will create a proxy object as the bean
3. When calling the method of the proxy object, we will first determine whether the @Transactional annotation is added to the method.
4. If added, then use the transaction manager to create a database connection.
5. And modify the autocommit attribute of the database connection to false, and prohibit the automatic commit of this connection. This is a very important step in implementing Spring transactions.
6. Then execute the current method, and SQL will be executed in the method
7. After executing the current method, if no exception occurs, the transaction will be submitted directly.
8. If an exception occurs and this exception needs to be rolled back, the transaction will be rolled back, otherwise the transaction will still be submitted.
Note:
The isolation level of a transaction corresponds to the isolation level of a database
The transaction propagation mechanism is implemented by Spring transactions itself, and is also the most complex in Spring transactions.
The transaction propagation mechanism is based on a database connection. A database connection is a transaction. If the propagation mechanism is configured to require a new transaction, then it is actually to create a new database connection first and execute SQL on this new database connection.
Several ways to implement Spring transactions
Several ways to implement transactions
(1) Programming transaction management is the only choice for POJO-based applications. We need to call beginTransaction(), commit(), rollback() and other transaction management related methods in the code, which is programming transaction management.
(2) Declarative transaction management based on TransactionProxyFactoryBean
(3) Declarative transaction management based on @Transactional
(4) Configure transactions based on Aspectj AOP
Programmatic transaction management
1、transactionTemplate
This method is automatic transaction management without manually opening, committing, or rolling back.
Configure Transaction Manager
<!-- Configure Transaction Manager ,Encapsulate all transaction operations,Rely on connection pool --> <bean class=""> <property name="dataSource" ref="dataSource"></property> </bean>
Configure transaction template objects
<!-- Configure transaction template objects --> <bean class=""> <property name="transactionManager" ref="transactionManager"></property> </bean>
test
@Controller @RequestMapping("/tx") @RunWith() @ContextConfiguration(locations = {"classpath:"}) public class TransactionController { @Resource public TransactionTemplate transactionTemplate; @Resource public DataSource dataSource; private static JdbcTemplate jdbcTemplate; private static final String INSERT_SQL = "insert into cc(id) values(?)"; private static final String COUNT_SQL = "select count(*) from cc"; @Test public void TransactionTemplateTest(){ //Get jdbc core class object and then operate the database jdbcTemplate = new JdbcTemplate(dataSource); //Get the transaction template object configured in xml through annotation (TransactionDefinition.ISOLATION_READ_COMMITTED); //Rewrite the execute method to achieve transaction management (new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { (INSERT_SQL, "33"); //The field sd is int type, so the insertion will definitely fail and the exception will be reported, and it will automatically roll back, which means that TransactionTemplate will automatically manage transactions } }); int i = (COUNT_SQL); ("Total number of records in the table:"+i); } }
2、PlatformTransactionManager
Use the Transaction Manager PlatformTransactionManager object, PlatformTransactionManager is an interface class implemented by DataSourceTransactionManager
In this way, transactions can be manually opened, submitted, and rolled back.
Just need: Configure transaction management
<!-- Configure transaction management ,Encapsulate all transaction operations,Rely on connection pool --> <bean class=""> <property name="dataSource" ref="dataSource"></property> </bean>
test
@Controller @RequestMapping("/tx") @RunWith() @ContextConfiguration(locations = {"classpath:"}) public class TransactionController { @Resource public PlatformTransactionManager transactionManager;//This is to inject the configuration data management object into it. @Resource public DataSource dataSource; private static JdbcTemplate jdbcTemplate; private static final String INSERT_SQL = "insert into cc(id) values(?)"; private static final String COUNT_SQL = "select count(*) from cc"; @Test public void showTransaction(){ //Define the use of isolation level and propagate behavior DefaultTransactionDefinition def = new DefaultTransactionDefinition(); (TransactionDefinition.ISOLATION_READ_COMMITTED); (TransactionDefinition.PROPAGATION_REQUIRED); // Transaction state class, obtained according to the transaction definition through the getTransaction method of PlatformTransactionManager; after obtaining the transaction state, Spring decides how to start the transaction based on the propagation behavior. TransactionStatus transaction = (def); jdbcTemplate = new JdbcTemplate(dataSource); int i = (COUNT_SQL); ("Total number of records in the table:"+i); try { (INSERT_SQL,"2"); (INSERT_SQL,"whether");//Exception occurs because the field is of type int, an exception will be reported and will be automatically rolled back (transaction); }catch (Exception e){ (); (transaction); } int i1 = (COUNT_SQL); ("Total number of records in the table:"+i1); } }
Declarative transaction management
1. Start transactions based on Aspectj AOP
Configure transaction notifications
<!-- Configure transaction enhancement --> <tx:advice transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" /> </tx:attributes> </tx:advice>
Configuration weaving
<!-- aopAgent Affairs。scanning All methods under the path --> <aop:config> <!-- scanning All methods under the path,And join transactions --> <aop:pointcut expression="execution(* .*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="tx" /> </aop:config>
A complete example
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="/schema/beans" xmlns:xsi="http:///2001/XMLSchema-instance" xmlns:aop="/schema/aop" xmlns:context="/schema/context" xmlns:tx="/schema/tx" xsi:schemaLocation="/schema/beans /schema/beans/spring-beans-3. /schema/aop /schema/aop/spring-aop-3. /schema/context /schema/context/spring-context-3. /schema/tx /schema/tx/spring-tx-3."> <!-- Create a load externalPropertiesFile Object --> <bean class=""> <property name="location" value="classpath:"></property> </bean> <!-- IntroducedredisProperties Configuration File --> <import resource="classpath:"/> <!-- Configure database connection resources --> <bean class="" scope="singleton"> <property name="driverClassName" value="${driver}"></property> <property name="url" value="${url}"></property> <property name="username" value="${username}"></property> <property name="password" value="${password}"></property> <property name="maxActive" value="${maxActive}"></property> <property name="maxIdle" value="${maxIdle}"></property> <property name="minIdle" value="${minIdle}"></property> <property name="initialSize" value="${initialSize}"></property> <property name="maxWait" value="${maxWait}"></property> <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}"></property> <property name="removeAbandoned" value="${removeAbandoned}"></property> <!-- ConfigurationsqlHeartbeat pack --> <property name= "testWhileIdle" value="true"/> <property name= "testOnBorrow" value="false"/> <property name= "testOnReturn" value="false"/> <property name= "validationQuery" value="select 1"/> <property name= "timeBetweenEvictionRunsMillis" value="60000"/> <property name= "numTestsPerEvictionRun" value="${maxActive}"/> </bean> <!--createSQLSessionFactoryObject --> <bean class=""> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:MyBatis_config.xml"></property> </bean> <!-- createMapperScannerConfigurerObject --> <bean class=""> <property name="basePackage" value=""></property> </bean> <!-- Configuration扫描器 IOC annotation --> <context:component-scan base-package="" /> <!-- Configuration事务管理 ,Encapsulate all transaction operations,Rely on connection pool --> <bean class=""> <property name="dataSource" ref="dataSource"></property> </bean> <!-- Configuration事务模板Object --> <bean class=""> <property name="transactionManager" ref="transactionManager"></property> </bean> <!-- Configuration事务增强 --> <tx:advice transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" rollback-for="Exception" /> </tx:attributes> </tx:advice> <!-- aopAgent Affairs --> <aop:config> <aop:pointcut expression="execution(* .*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="tx" /> </aop:config> </beans>
This way, even if all the methods are added to the transaction
You can also use the springboot configuration class method:
package ; @Configurationpublic class TxAnoConfig { /*Transaction Intercept Type*/ @Bean("txSource") public TransactionAttributeSource transactionAttributeSource() { NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); /*Read-only transaction, no update operation*/ RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, (new RollbackRuleAttribute())); (60); Map<String, TransactionAttribute> txMap = new HashMap<>(); ("*", requiredTx); (txMap); return source; } /** * Face interception rules Parameters will be automatically injected from the container */ @Bean public AspectJExpressionPointcutAdvisor pointcutAdvisor(TransactionInterceptor txInterceptor) { AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor(); (txInterceptor); ("execution (* ..*Controller.*(..))"); return pointcutAdvisor; } /*Transaction Interceptor*/ @Bean("txInterceptor") TransactionInterceptor getTransactionInterceptor(PlatformTransactionManager tx) { return new TransactionInterceptor(tx, transactionAttributeSource()); } }
2. Declarative transaction management based on annotation by @Transactional
@Transactional public int saveRwHist(List list) { return (list); }
To enable this annotation, you need to add a configuration to enable annotation transaction.
The above transaction opening method only requires understanding. Nowadays, these methods are generally not used in work, which is too cumbersome. Generally, you can complete these transaction management operations by directly using the @Transactional annotation provided by springboot. However, if you want to know the underlying implementation principle of the transaction, you can still refer to the above primitive methods.
Summarize
These are just personal experience. I hope you can give me a reference and I hope you can support me more.