Transactions in Spring
Spring's transaction is actually the transaction operation of the database. It conforms to the ACID standard and also has a standard transaction isolation level.
Transactions in Spring only encapsulate and extend JDBC transactions, and the underlying layer will eventually use this set of JDBC API. However, Spring transactions have their own characteristics, that is, transaction propagation mechanism.
The so-called transaction propagation mechanism is how the transaction is passed in the call of multiple methods. Is it to recreate the transaction or use the transaction of the parent method? Does the rollback of the parent method affect the transactions of the child method? These can be determined through the transaction propagation mechanism.
preparation
Entity class
Area
package com.morris.spring.entity; import lombok.Data; import java.io.Serializable; @Data public class Area implements Serializable { private Integer id; private String areaName; private Integer areaCode; }
Good
package com.morris.spring.entity; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; @Data public class Good implements Serializable { private Integer id; private String goodName; private BigDecimal price; }
DAO layer
AreaDao
package com.morris.spring.dao; import com.morris.spring.entity.Area; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; public class AreaDao { @Autowired private JdbcTemplate jdbcTemplate; public boolean insert(Area area) { String sql = "insert into t_area(area_name, area_code) values(?,?)"; return jdbcTemplate.update(sql, area.getAreaName(), area.getAreaCode()) > 0; } }
GoodDao
package com.morris.spring.dao; import com.morris.spring.entity.Good; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; public class GoodDao { @Autowired private JdbcTemplate jdbcTemplate; public boolean insert(Good good) { String sql = "insert into t_good(good_name, price) values(?,?)"; return jdbcTemplate.update(sql, good.getGoodName(), good.getPrice()) > 0; } }
Service layer
AreaServiceImpl
package com.morris.spring.service; import com.morris.spring.dao.AreaDao; import com.morris.spring.entity.Area; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public class AreaServiceImpl implements AreaService { @Autowired private AreaDao areaDao; @Transactional(propagation = Propagation.REQUIRED) @Override public boolean addArea(int i) { int y = 1000000 / i; Area area = new Area(); area.setAreaCode(y); area.setAreaName("shenzhen"); return areaDao.insert(area); } }
GoodServiceImpl
package com.morris.spring.service; import com.morris.spring.dao.GoodDao; import com.morris.spring.entity.Good; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; public class GoodServiceImpl implements GoodService { @Autowired private GoodDao goodDao; @Transactional(propagation = Propagation.REQUIRED) @Override public boolean addGood() { Good good = new Good(); good.setGoodName("iphone"); good.setPrice(BigDecimal.valueOf(99999)); return goodDao.insert(good); } }
TransactionService
package com.morris.spring.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Component public class TransactionService { @Autowired private GoodService goodService; @Autowired private AreaService areaService; @Transactional(propagation = Propagation.REQUIRED) public void addGoodAndArea() { System.out.println("------addGoodAndArea-------"); areaService.addArea(10); goodService.addGood(); } }
Test class
TransactionPropagationDemo
package com.morris.spring.demo.jdbc; import com.morris.spring.config.JdbcConfig; import com.morris.spring.dao.AreaDao; import com.morris.spring.dao.GoodDao; import com.morris.spring.service.AreaServiceImpl; import com.morris.spring.service.GoodServiceImpl; import com.morris.spring.service.TransactionService; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * Communication mechanism of transaction */ public class TransactionPropagationDemo { @Test public void test() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(GoodDao.class); applicationContext.register(AreaDao.class); applicationContext.register(GoodServiceImpl.class); applicationContext.register(AreaServiceImpl.class); applicationContext.register(JdbcConfig.class); applicationContext.register(TransactionService.class); applicationContext.refresh(); TransactionService transactionService = applicationContext.getBean(TransactionService.class); transactionService.addGoodAndArea(); } }
Communication mechanism
For specific options, refer to enumeration class org Springframework transaction. annotation. Propagation.
option | explain |
---|---|
REQUIRED | Default options. If there is no current transaction, create a new transaction. If there is already a transaction, join it. |
SUPPORTS | The current transaction is supported. If there is no current transaction, it will be executed in a non transaction manner. |
MANDATORY | Use the current transaction. If there is no current transaction, throw an exception. |
REQUIRES_NEW | Create a new transaction. If there is a current transaction, suspend the current transaction. |
NOT_SUPPORTED | Perform operations in a non transactional manner. If there is a transaction currently, suspend the current transaction. |
NEVER | Execute in a non transactional manner. If there is a transaction currently, an exception will be thrown. |
NESTED | If a transaction currently exists, it is executed within a nested transaction. If there is currently no transaction, perform an operation similar to REQUIRED. |
REQUIRED
Default options. If there is no current transaction, create a new transaction. If there is already a transaction, join it.
The configuration is as follows:
- TransactionService: REQUIRED
- AreaServiceImpl: REQUIRED
- GoodServiceImpl: REQUIRED
Run the above Demo and the results are as follows:
// Create first transaction DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // Create first connection DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- // Execute in the first transaction DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] // Execute in the first transaction DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // Commit the first transaction DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
Summary:
- When there is no transaction in the outer layer, transactionservice The addgoodandarea () method found that no transaction was available, so it created a new transaction by itself.
- goodService.addGood() and areaservice If an existing transaction is found during the execution of addarea(), the current transaction will be used for execution.
REQUIRES_NEW
Create a new transaction. If there is a current transaction, suspend the current transaction.
The configuration is as follows:
- TransactionService: REQUIRED
- GoodServiceImpl: REQUIRES
- AreaServiceImpl: REQUIRES_NEW
The operation results are as follows:
// Create first transaction DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // Create first connection DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] to manual commit ------addGoodAndArea------- // Suspend the first transaction and create a second transaction DEBUG DataSourceTransactionManager:446 - Suspending current transaction, creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea] // Create a second connection DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] to manual commit DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // Commit the second transaction DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@116fc68] after transaction // Resume first transaction DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // Execute in the first transaction DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] after transaction
Summary: areaserviceimpl When addarea() finds an existing transaction during execution, it suspends the current transaction and resumes after execution.
SUPPORTS
The current transaction is supported. If there is no current transaction, it will be executed in a non transaction manner.
The configuration is as follows:
- TransactionService: SUPPORTS
- AreaServiceImpl: REQUIRED
- GoodServiceImpl: SUPPORTS
The operation results are as follows:
// TransactionService.addGoodAndArea runs in a non transactional manner ------addGoodAndArea------- // Start the first transaction DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.AreaServiceImpl.addArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // Create first connection DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] to manual commit DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // Commit the first transaction DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1691f3d] after transaction DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // AreaServiceImpl.addArea runs in a non transactional manner DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
Summary: Currently there is no transaction, transactionservice Addgoodandarea() and areaserviceimpl Addarea() runs in a non transactional manner.
Modify the configuration as follows:
- TransactionService: REQUIRED
- AreaServiceImpl: SUPPORTS
- GoodServiceImpl: SUPPORTS
The operation results are as follows:
// Start the first transaction DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // Create first connection DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] to manual commit ------addGoodAndArea------- // Execute in the first transaction DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // Execute in the first transaction DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] // Commit the first transaction DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11158fb] after transaction
Summary: there are currently transactions, goodserviceimpl Addgood() and areaserviceimpl Addarea() runs in transactional mode.
MANDATORY
Use the current transaction. If there is no current transaction, throw an exception.
The configuration is as follows:
- TransactionService: REQUIRED
- AreaServiceImpl: REQUIRED
- GoodServiceImpl: MANDATORY
The operation results are as follows:
// Create first transaction EBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // Create first connection DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] to manual commit ------addGoodAndArea------- // Execute in the first transaction DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // Execute in the first transaction DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] // Commit the first transaction DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@11c4a3f] after transaction
Summary: there are currently transactions, goodserviceimpl Addgood () runs in transactional mode.
Modify the configuration as follows:
- TransactionService: SUPPORTS
- AreaServiceImpl: MANDATORY
- GoodServiceImpl: SUPPORTS
The operation results are as follows:
------addGoodAndArea------- DEBUG DataSourceTransactionManager:888 - Should roll back transaction but cannot - no transaction available org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory' at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372) at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:374) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:205) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219) at com.sun.proxy.$Proxy23.addArea(Unknown Source) at com.morris.spring.service.TransactionService.addGoodAndArea(TransactionService.java:20)
Summary: Currently there is no transaction, areaserviceimpl Addarea() throws an exception.
NOT_SUPPORTED
Perform operations in a non transactional manner. If there is a transaction currently, suspend the current transaction.
The configuration is as follows:
- TransactionService: REQUIRED
- AreaServiceImpl: NOT_SUPPORTED
- GoodServiceImpl: NOT_SUPPORTED
The operation results are as follows:
// Start the first transaction DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- // Suspend first transaction DEBUG DataSourceTransactionManager:436 - Suspending current transaction // Run as non transactional DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] // Resume first transaction DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // Suspend first transaction DEBUG DataSourceTransactionManager:436 - Suspending current transaction // Run as non transactional DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] // Resume first transaction DEBUG DataSourceTransactionManager:1043 - Resuming suspended transaction after completion of inner transaction // Commit the first transaction DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
Summary: execute goodserviceimpl Addgood() and areaserviceimpl If there is a current transaction when addarea(), suspend the current transaction.
NEVER
Execute in a non transactional manner. If there is a transaction currently, an exception will be thrown.
The configuration is as follows:
- TransactionService: NEVER
- AreaServiceImpl: NEVER
- GoodServiceImpl: NEVER
The operation results are as follows:
// No transactions run in a non transactional manner ------addGoodAndArea------- DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] DEBUG DataSourceUtils:115 - Fetching JDBC Connection from DataSource DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
Summary: Currently, no transactions are executed in non transaction mode.
Modify the configuration as follows:
- TransactionService: REQUIRED
- AreaServiceImpl: NEVER
- GoodServiceImpl: NEVER
The operation results are as follows:
// Start the first transaction DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- DEBUG DataSourceTransactionManager:864 - Initiating transaction rollback DEBUG DataSourceTransactionManager:345 - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:430) at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:374) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:205) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219) at com.sun.proxy.$Proxy23.addArea(Unknown Source) at com.morris.spring.service.TransactionService.addGoodAndArea(TransactionService.java:20)
Summary: areaserviceimpl An exception will be thrown if there is a transaction when addarea() is executed.
NESTED
If a transaction currently exists, it is executed within a nested transaction. If there are currently no transactions, perform an operation similar to REQUIRED.
The configuration is as follows:
- TransactionService: REQUIRED
- GoodServiceImpl: NESTED
- AreaServiceImpl: NESTED
The operation results are as follows:
// Create first transaction EBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT // Create first connection DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- // Create rollback point DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.AreaServiceImpl.addArea] DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)] // Release rollback point DEBUG DataSourceTransactionManager:754 - Releasing transaction savepoint // Create rollback point DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.GoodServiceImpl.addGood] DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] // Release rollback point DEBUG DataSourceTransactionManager:754 - Releasing transaction savepoint DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
If you don't throw an exception, both REQUIRED and needed are used. The difference is that an exception occurs. The following shows the difference between REQUIRED and needed when an exception occurs:
The configuration is as follows:
- TransactionService: REQUIRED
- AreaServiceImpl: REQUIRED
- GoodServiceImpl: REQUIRED
TransactionService.addGoodAndArea is modified as follows:
@Transactional(propagation = Propagation.REQUIRED) public void addGoodAndArea() { System.out.println("------addGoodAndArea-------"); try { areaService.addArea(0); } catch (Exception e) { e.printStackTrace(); } goodService.addGood(); }
The operation results are as follows:
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] to manual commit ------addGoodAndArea------- DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG DataSourceTransactionManager:877 - Participating transaction failed - marking existing transaction as rollback-only DEBUG DataSourceTransactionManager:360 - Setting JDBC transaction [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] rollback-only java.lang.ArithmeticException: / by zero at com.morris.spring.service.AreaServiceImpl.addArea(AreaServiceImpl.java:18) ... ... DEBUG DataSourceTransactionManager:487 - Participating in existing transaction DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] DEBUG DataSourceTransactionManager:723 - Global transaction is marked as rollback-only but transactional code requested commit DEBUG DataSourceTransactionManager:877 - Participating transaction failed - marking existing transaction as rollback-only DEBUG DataSourceTransactionManager:360 - Setting JDBC transaction [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] rollback-only DEBUG DataSourceTransactionManager:723 - Global transaction is marked as rollback-only but transactional code requested commit DEBUG DataSourceTransactionManager:864 - Initiating transaction rollback DEBUG DataSourceTransactionManager:345 - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@f9aa66] after transaction org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:905) ... ...
From the running results, it can be found that all transactions have been rolled back.
Modify the above configuration as follows:
- TransactionService: REQUIRED
- AreaServiceImpl: NESTED
- GoodServiceImpl: NESTED
The operation results are as follows:
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [com.morris.spring.service.TransactionService.addGoodAndArea]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull] DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] for JDBC transaction DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] to manual commit ------addGoodAndArea------- DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.AreaServiceImpl.addArea] DEBUG DataSourceTransactionManager:857 - Rolling back transaction to savepoint java.lang.ArithmeticException: / by zero at com.morris.spring.service.AreaServiceImpl.addArea(AreaServiceImpl.java:18) ... ... DEBUG DataSourceTransactionManager:466 - Creating nested transaction with name [com.morris.spring.service.GoodServiceImpl.addGood] DEBUG JdbcTemplate:860 - Executing prepared SQL update DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)] DEBUG DataSourceTransactionManager:754 - Releasing transaction savepoint DEBUG DataSourceTransactionManager:763 - Initiating transaction commit DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@8ef162] after transaction
From the running results, we can find areaservice Addarea () rolled back (no content was submitted), goodservice The content of addgood () is submitted.
Note that only runtime exceptions and exceptions specified by rollbakcFor will be rolled back.