For mysql, there may be several main points of knowledge about transactions at the isolation level; in the Spring system, there is also a transfer property of knowledge point transactions that is equally important when using transactions. This article will focus on the scenarios in which the transfer property is used in 7.
<!-- more -->
I. Configuration
In this case, declarative transactions are used. First, we create a SpringBoot project, version 2.2.1.RELEASE, using mysql as the target database, the storage engine chooses Innodb, and the transaction isolation level is RR
1. Project Configuration
In the project pom.xml file, plus spring-boot-starter-jdbc, a DataSourceTransactionManager bean is injected, providing transaction support
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
2. Database Configuration
Go to the spring configuration file application.properties to set up db-related information
## DataSource spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false spring.datasource.username=root spring.datasource.password=
3. Database
Create a new simple table structure for testing
CREATE TABLE `money` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '' COMMENT 'User name', `money` int(26) NOT NULL DEFAULT '0' COMMENT 'money', `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time', `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update Time', PRIMARY KEY (`id`), KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=551 DEFAULT CHARSET=utf8mb4;
II. Instructions for use
0. Preparation
Before you get started, you have to prepare some basic data
@Component public class PropagationDemo { @Autowired private JdbcTemplate jdbcTemplate; @PostConstruct public void init() { String sql = "replace into money (id, name, money) values (420, 'Initialization', 200)," + "(430, 'Initialization', 200)," + "(440, 'Initialization', 200)," + "(450, 'Initialization', 200)," + "(460, 'Initialization', 200)," + "(470, 'Initialization', 200)," + "(480, 'Initialization', 200)," + "(490, 'Initialization', 200)"; jdbcTemplate.execute(sql); } }
Next, to test the use of transactions, we need to create an additional test class, with subsequent test case s placed in the class PropagationSample; to make the output more friendly, an encapsulated call method is provided
@Component public class PropagationSample { @Autowired private PropagationDemo propagationDemo; private void call(String tag, int id, CallFunc<Integer> func) { System.out.println("============ " + tag + " start ========== "); propagationDemo.query(tag, id); try { func.apply(id); } catch (Exception e) { System.out.println(e.getMessage()); } propagationDemo.query(tag, id); System.out.println("============ " + tag + " end ========== \n"); } @FunctionalInterface public interface CallFunc<T> { void apply(T t) throws Exception; } }
1. REQUIRED
It is also the default delivery property, which is characterized by
- Run in the current transaction if one exists
- Start a new transaction if there are no transactions
Simple to use, do not set the propagation property of the @Transactional annotation, or set it to REQUIRED
/** * If a transaction exists, the current transaction is supported.Start a new transaction if there are no transactions * * @param id */ @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void required(int id) throws Exception { if (this.updateName(id)) { this.query("required: after updateMoney name", id); if (this.updateMoney(id)) { return; } } throw new Exception("rollback!!!"); }
Above is a basic usage position
private void testRequired() { int id = 420; call("Required Transaction Run", id, propagationDemo::required); }
The output is as follows
================= Required transaction run start ========== Required transaction run >> {id=420, name=initialization, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:26.0} Required: after updateMoney name >> {id=420, name=update, money=200, is_deleted=false, create_at=2020-02 15:23:26.0, update_at=2020-02 15:23:46.0} Transaction rollback!!! Required transaction run >> {id=420, name=initialization, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:26.0} ================= Required transaction run end ==========
2. SUPPORTS
It is characterized by execution within a transaction; otherwise, it is not transactional, i.e.
- Supports the current transaction if one exists
- Non-transactional execution if no transaction exists
Use the same posture as the front
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class) public void support(int id) throws Exception { if (this.updateName(id)) { this.query("support: after updateMoney name", id); if (this.updateMoney(id)) { return; } } throw new Exception("rollback!!!"); }
This pass-through property is special, so our test case requires two, one transactional call and one non-transactional call
When testing the transaction call, we create a new bean: PropagationDemo2, the following support method supports the transaction
@Component public class PropagationDemo2 { @Autowired private PropagationDemo propagationDemo; @Transactional(rollbackFor = Exception.class) public void support(int id) throws Exception { // Transaction Run propagationDemo.support(id); } }
For non-transactional calls, they are called directly in the test class (note the call method below, which calls the support method in two different bean s)
private void testSupport() { int id = 430; // Non-transactional, exception does not roll back call("support Run without transaction", id, propagationDemo::support); // Transaction Run id = 440; call("support Transaction Run", id, propagationDemo2::support); }
The output is as follows:
================ support No transaction run start ========= Supports Transactionless Run >> {id=430, name=initialization, money=200, is_deleted=false, create_at=2020-02 15:23:26.0, update_at=2020-02 15:23:26.0} Support: after updateMoney name >> {id=430, name=update, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:46.0} Transaction rollback!!! Supports Transactionless Run >> {id=430, name=update, money=210, is_deleted=false, create_at=2020-02 15:23:26.0, update_at=2020-02 15:23:46.0} ================ support No transaction run end ========= ================ support transaction run start ========== SupportTransaction Run >>> {id=440, name=initialization, money=200, is_deleted=false, create_at=2020-02 15:23:26.0, update_at=2020-02 15:23:26.0} Support: after updateMoney name >> {id=440, name=update, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:46.0} Transaction rollback!!! SupportTransaction Run >>> {id=440, name=initialization, money=200, is_deleted=false, create_at=2020-02 15:23:26.0, update_at=2020-02 15:23:26.0} ================ support transaction run end ==========
From the above output, you can also see that when a non-transaction is executed, it is not rolled back; when a transaction is executed, it is rolled back
3. MANDATORY
Need to execute within a normal transaction or throw an exception
Use position as follows
@Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class) public void mandatory(int id) throws Exception { if (this.updateName(id)) { this.query("mandatory: after updateMoney name", id); if (this.updateMoney(id)) { return; } } throw new Exception("rollback!!!"); }
The characteristic of this propagation property is that this method must run in an existing transaction, so our test case is simpler, what happens when it no longer runs in a transaction?
private void testMandatory() { int id = 450; // Non-transactional, throws an exception, which must be executed within a transaction call("mandatory Non-transactional Run", id, propagationDemo::mandatory); }
Output Results
============ mandatory Non-transactional Run start ========== mandatory Non-transactional Run >>>> {id=450, name=Initialization, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:26.0} No existing transaction found for transaction marked with propagation 'mandatory' mandatory Non-transactional Run >>>> {id=450, name=Initialization, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:26.0} ============ mandatory Non-transactional Run end ==========
From the output above, you can see that the exception is thrown directly, and the logic within the method is not executed.
4. NOT_SUPPORT
This is interesting because the method marked by it always executes non-transactionally and suspends if there is an active transaction
(It's hard to imagine what scene needs this propagating property)
A simple case to use is as follows:
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class) public void notSupport(int id) throws Exception { if (this.updateName(id)) { this.query("notSupport: after updateMoney name", id); if (this.updateMoney(id)) { return; } } throw new Exception("RollBACK!"); }
Next, we need to think about our test case. First, it needs to be invoked in a transaction. External things fail and roll back without affecting the execution result of the above method.
We added the following test case in PropagationDemo2
@Transactional(rollbackFor = Exception.class) public void notSupport(int id) throws Exception { // Suspend the current transaction and run non-transactionally try { propagationDemo.notSupport(id); } catch (Exception e) { } propagationDemo.query("notSupportCall: ", id); propagationDemo.updateName(id, "External Update"); propagationDemo.query("notSupportCall: ", id); throw new Exception("RollBACK"); }
The output is as follows
============ notSupport start ========== NotSupport >>> {id=460, name=initialization, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02 15:23:26.0} NotSupport: after updateMoney name >> {id=460, name=update, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:46.0} NotSupportCall: >>> {id=460, name=update, money=210, is_deleted=false, create_at=2020-02 15:23:26.0, update_at=2020-02 15:23:46.0} NotSupportCall: >>> {id=460, name=external update, money=210, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:46.0} RollBACK NotSupport >>> {id=460, name=update, money=210, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02 15:23:46.0} ============ notSupport end ==========
You can see from the output above
- NOT_SUPPORT tagged method, which is run non-transactionally (modifications are not rolled back because exceptions are thrown)
- External transaction rollback without affecting its modifications
5. NEVER
Always executes non-transactionally and throws an exception if there is an active transaction.
Use position as follows
/** * Always executes non-transactionally and throws an exception if there is an active transaction. * * @param id * @throws Exception */ @Transactional(propagation = Propagation.NEVER, rollbackFor = Exception.class) public void never(int id) throws Exception { if (this.updateName(id)) { this.query("notSupport: after updateMoney name", id); if (this.updateMoney(id)) { return; } } }
Our test is simpler, if it runs in a transaction, will it throw an exception
In PropagationDemo2, add a transaction call method
@Transactional(rollbackFor = Exception.class) public void never(int id) throws Exception { propagationDemo.never(id); }
Test Code
private void testNever() { int id = 470; call("never Non-transactional", id, propagationDemo2::never); }
Output Results
============ never Non-transactional start ========== never Non-transactional >>>> {id=470, name=Initialization, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:26.0} Existing transaction found for transaction marked with propagation 'never' never Non-transactional >>>> {id=470, name=Initialization, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:26.0} ============ never Non-transactional end ==========
An exception was thrown directly without executing the business logic within the method
6. NESTED
Its main features are as follows
- If no transaction exists, start a transaction run
- Run a nested transaction if one exists;
A concept of nested transactions was proposed above. What is a nested transaction?
- A simple understanding: external transactions are rolled back, internal transactions are also rolled back; internal transactions are rolled back, external problems are not rolled back, external transactions are not rolled back
Next, design two test cases, one for internal transaction rollback and one for external transaction rollback
a. case1 internal transaction rollback
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class) public void nested(int id) throws Exception { if (this.updateName(id)) { this.query("nested: after updateMoney name", id); if (this.updateMoney(id)) { return; } } throw new Exception("rollback!!!"); }
In the PropagationDemo2 bean, an external transaction is added to catch the exception of the above method, so the external execution is normal
@Transactional(rollbackFor = Exception.class) public void nested(int id) throws Exception { propagationDemo.updateName(id, "External Transaction Modification"); propagationDemo.query("nestedCall: ", id); try { propagationDemo.nested(id); } catch (Exception e) { } }
Test Code
private void testNested() { int id = 480; call("nested affair", id, propagationDemo2::nested); }
The output is as follows
============ nested affair start ========== nested affair >>>> {id=480, name=Initialization, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:26.0} nestedCall: >>>> {id=480, name=External Transaction Modification, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:46.0} nested: after updateMoney name >>>> {id=480, name=To update, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:46.0} nested affair >>>> {id=480, name=External Transaction Modification, money=200, is_deleted=false, create_at=2020-02-02 15:23:26.0, update_at=2020-02-02 15:23:46.0} ============ nested affair end ==========
Looking carefully at the above results, the results of the external transaction modifications are saved, and the internal transaction modifications are rolled back without affecting the final results
b. case2 external transaction rollback
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class) public void nested2(int id) throws Exception { if (this.updateName(id)) { this.query("nested: after updateMoney name", id); if (this.updateMoney(id)) { return; } } }
In PropagationDemo2, an external transaction is added, the internal transaction is normal, but the external transaction throws an exception and rolls back actively
@Transactional(rollbackFor = Exception.class) public void nested2(int id) throws Exception { // Nested transactions, external rollback, synchronous rollback of internal transactions propagationDemo.updateName(id, "External Transaction Modification"); propagationDemo.query("nestedCall: ", id); propagationDemo.nested2(id); throw new Exception("rollback"); }
Test Code
private void testNested() { int id = 490; call("nested Transaction 2", id, propagationDemo2::nested2); }
The output is as follows
================ nested transaction 2 start=========== nested transaction 2 >>> {id=490, name=initialization, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:26.0} NestedCall: >>> {id=490, name=external transaction modification, money=200, is_deleted=false, create_at=2020-02 15:23:26.0, update_at=2020-02 15:23:46.0} Nested: after updateMoney name >> {id=490, name=update, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:46.0} rollback nested transaction 2 >>> {id=490, name=initialization, money=200, is_deleted=false, create_at=2020-02-15:23:26.0, update_at=2020-02 15:23:26.0} ================ nested transaction 2 end===========
Looking closely at the output above, case1, in particular, rolled back all the changes to the internal transaction
7. REQUIRES_NEW
This is a bit like NESTED above, but it's different
- Create a new transaction to execute when an active transaction exists
- Create a transaction execution when no active transaction exists, consistent with the REQUIRES effect
Be careful
REQUIRES_NEW has no relationship with NESTED, either rollback has no effect on the other
Test case is similar to the previous one, not much detail...
8. Summary
The propagation properties in 7 are described earlier, but here's a simple comparison and summary
affair | Characteristic |
---|---|
REQUIRED | By default, if a transaction exists, it supports the current transaction; if it does not exist, it opens a new transaction |
SUPPORTS | Supports the current transaction if one exists.Non-transactional execution if no transaction exists |
MANDATORY | Need to execute within a normal transaction or throw an exception |
REQUIRES_NEW | Start a new transaction whether it exists or not |
NOT_SUPPORTED | Executes non-transactionally regardless of whether the store does not exist, suspends the transaction when it exists |
NEVER | Executed non-transactionally, throws an exception if a transaction exists |
NESTED | If no transaction exists, a transaction is started; if a transaction exists, a nested transaction is run |
II. Other
0.Series Blog & Source
Series Blog
- Basic Use of DB in 180926-SpringBoot Advanced Text
- 190407-SpringBoot Advanced JdbcTemplate Data Insertion Use Posture Details
- 190412-SpringBoot Advanced JdbcTemplate Data Query Previous
- 190417-SpringBoot Advanced JdbcTemplate Data Query Next
- Data Update and Delete of 190418-SpringBoot Advanced JdbcTemplate
- Declarative Transactional Transactional for the SpringBoot series of tutorials
- Summary of Transaction Isolation Level Knowledge Points for the 200120-SpringBoot Series Tutorials
Source code
- Project: https://github.com/liuyueyi/spring-boot-demo
- Instance source: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/101-jdbctemplate-transaction
1.A grey Blog
Unlike letters, the above are purely family statements. Due to limited personal abilities, there are unavoidable omissions and errors. If bug s are found or there are better suggestions, you are welcome to criticize and correct them with gratitude.
Below is a grey personal blog, which records all the blogs in study and work. Welcome to visit it
- A Grey Blog Personal Blog https://blog.hhui.top
- A Grey Blog-Spring Thematic Blog http://spring.hhui.top