Learn to apply what they learn something, if only to know the theory and practice had not, then the master will not be particularly strong, it estimated a few days to forget, then we come together to learn Spring transaction propagation properties of practice.
Propagation properties
Propagation property is defined as a method of transaction processing behavior encountered when another transaction method , a total of seven behavior, defined as follows
Transmissible | value | description |
---|---|---|
PROPAGATION_REQUIRED |
0 | Support the current transaction, if not New Transaction |
PROPAGATION_SUPPORTS |
1 | Support the current transaction, if there is no way not to run affairs |
PROPAGATION_MANDATORY |
2 | Support the current transaction, if the current transaction will not throw an exception |
PROPAGATION_REQUIRES_NEW |
3 | Whether or not there is a current transaction, will be starting a new transaction |
PROPAGATION_NOT_SUPPORTED |
4 | It does not support transactions, if the current transaction exists, this pending transaction will not run in a transaction mode |
PROPAGATION_NEVER |
5 | It does not support transactions, if there is a transaction to throw an exception |
PROPAGATION_NESTED |
6 | If the current transaction exists in the current transaction and then starting a new transaction |
In fact, just look at the concept of words have been very straightforward explanation of the role of each spread, then this time we show you specific examples of behavior under each propagation attributes.
The demonstration we are using the H2 database, which is the role of memory in the inside, so for us it just demonstrates the effect of the transaction without our making other arrangements, we create a new table. The following statement on the schema.sql
document which can, SpringBoot program will automatically create such a table in memory, which is when we started.
CREATE TABLE FOO (ID INT IDENTITY, BAR VARCHAR(64));
Before the presentation we will define two classes FooService
and BarService
. We use BarService
inside the method call FooService
method.
Preparing the Environment
Before the transaction demonstrates, in fact, it can be divided into the following situations, according to the permutations and combinations, we can draw the following eight cases
Caller: whether the transaction
Caller: Is there an exception
Callee: Have ** transaction (this is controlled by the propagation properties) in the arrangement ** combination is not so
Callee: Is there an exception
The caller has affairs | The caller has abnormal | The caller has abnormal |
---|---|---|
Have | Have | Have |
Have | Have | no |
Have | no | Have |
Have | no | no |
no | Have | Have |
no | Have | no |
no | no | Have |
no | no | no |
Exception class
Which RollbackException
is an unusual class of our own definition
@Service // www.1b23.com public class BarServiceImpl implements BarService{ @Autowired private FooService fooService; // PROPAGATION_REQUIRED demo no transaction @Override public void testRequiredNoTransactional() throws RollbackException { fooService.testRequiredTransactional (); } }
The caller
In BarService
the definition of two methods, one with a transaction, a transaction is without
// There affairs @Override @Transactional(rollbackFor = Exception.class) public void hasTransactional() throws RollbackException { } // No transaction @Override public void noTransactional() throws RollbackException { }
Then we learn the propagation properties of the transaction based on eight kinds of circumstances Russia as defined above.
PROPAGATION_REQUIRED
In this propagation properties, is whether the caller is new to the transaction depends on whether the caller with the transaction.
Two examples you want to understand the characteristics of the propagation properties of this fact, we demonstrate eight kinds of the above situation is enough
The caller has affairs | The caller has abnormal | The caller has abnormal |
---|---|---|
no | no | Have |
Have | Have | no |
The first case we throw an exception in the case of the caller, if the query can not insert data, then it shows the caller at the caller without a transaction of their new business.
In the case of the second case we throw an exception in the case of the caller, if the query can not insert data, then it shows the caller in the caller has joined the transaction the current transaction.
Let's look at an example of the method of the class of the caller.
@Service // www.1b23.com public class FooServiceImpl implements FooService { @Autowired private JdbcTemplate jdbcTemplate; // REQUIRED propagation properties - callee exception is thrown @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED) public void testRequiredHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ("+Global.REQUIRED_HAS_EXCEPTION+")"); throw new RollbackException(); } // REQUIRED propagation properties - is no exception is thrown caller @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED) public void testRequiredNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ("+Global.REQUIRED_NO_EXCEPTION+")"); } }
Next we look at the example of the caller method
@Service public class BarServiceImpl implements BarService{ @Autowired private FooService fooService; // There affairs @Override @Transactional(rollbackFor = Exception.class) public void hasTransactional() throws RollbackException { // the caller there is a transaction, an exception is thrown caller without exception fooService.testRequiredNoException (); throw new RollbackException(); } // No transaction @Override public void noTransactional() throws RollbackException { // No transaction caller, the caller does not throw an exception is an exception fooService.testRequiredHasException (); } }
At this point we queried when the program calls
String noException = Global.REQUIRED_NO_EXCEPTION; String hasException = Global.REQUIRED_HAS_EXCEPTION; try { barService.noTransactional (); }catch (Exception e){ log.info ( "the first case {}", jdbcTemplate .queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='"+hasException+"'", Long.class)); } try { barService.hasTransactional(); }catch (Exception e){ log.info ( "the second case {}", jdbcTemplate .queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='"+noException+"'", Long.class)); }
View print out the log
2019-10-16 13: 02: 04.142 INFO 11869 --- [main] cettTransactionApplication: a first 0 2019-10-16 13: 02: 04.143 INFO 11869 --- [main] cettTransactionApplication: 0 second case
We see that we have not identified corresponding data to indicate that data are rolled back. At this point we should understand that sentence would support the current transaction, if not New Transaction .
PROPAGATION_SUPPORTS
The caller has affairs, totally dependent on the caller, the caller there is a transaction, there are transactions, the caller is not a transaction is not a transaction.
Next, we demonstrate with two examples above
The caller has affairs | The caller has abnormal | The caller has abnormal |
---|---|---|
no | no | Have |
Have | Have | no |
The first case: the case was thrown caller, if still query the data on the transaction is not rolled back, indicating that the caller does not have affairs
The second case: the caller throws an exception case, finding out if the data on the two methods in a transaction
The next example shows still
The caller, but the @Transactional
annotation in the propagation
property in order to replacePropagation.SUPPORTS
// SUPPORTS propagation properties - callee exception is thrown @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS) public void testSupportsHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_HAS_EXCEPTION+"')"); throw new RollbackException(); } // SUPPORTS propagation properties - is no exception is thrown caller @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS) public void testSupportsNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_NO_EXCEPTION+"')"); }
The caller and call the above example, we see the direct implementation of the results
2019-10-16 13: 50: 27.738 INFO 12174 --- [main] cettTransactionApplication: a first case 1 2019-10-16 13: 50: 27.741 INFO 12174 --- [main] cettTransactionApplication: second 0
We see the data found in the first case, explanation is called the first case who is not a transaction. At this point we should understand this sentence would support the current transaction, if there is no way not to run affairs .
PROPAGATION_MANDATORY
Still these two examples demonstrate
The caller has affairs | The caller has abnormal | The caller has abnormal |
---|---|---|
no | no | Have |
Have | Have | no |
The first case: the caller because there is no transaction, so the spread of this property should be thrown anomaly
The second case: the caller's transaction and the transaction is the same caller
Next is the code example of the callee
// MANDATORY propagation properties - callee exception is thrown @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY) public void testMandatoryHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_HAS_EXCEPTION+"')"); throw new RollbackException(); } // MANDATORY propagation properties - is no exception is thrown caller @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY) public void testMandatoryNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_NO_EXCEPTION+"')"); }
The caller and call the above example, we see the direct implementation of the results
2019-10-16 13:58:39.178 ERROR 12317 --- [ main] c.e.t.t.TransactionApplication : org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory' 2019-10-16 13: 58: 39.276 INFO 12317 --- [main] cettTransactionApplication: a first 0 2019-10-16 13: 58: 39.281 INFO 12317 --- [main] cettTransactionApplication: second 0
We find the same and we speculate, indicating that the callee is not their new affairs, then we should understand this sentence would support the current transaction, if the current transaction will not throw an exception .
PROPAGATION_REQUIRES_NEW
Under the propagation properties, regardless of whether the caller there is a transaction, the caller will create a new transaction
The caller has affairs | The caller has abnormal | The caller has abnormal |
---|---|---|
no | no | Have |
Have | Have | no |
The first case: the caller no transaction, the caller will create a new transaction, so finding data
The second case: the caller there is a transaction, the transaction creates a new caller, so the caller Throws not affect the caller, so the data can be found
Next we demo code.
Callee
// REQUIRES_NEW propagation properties - callee exception is thrown @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW) public void testRequiresNewHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.REQUIRES_NEW_HAS_EXCEPTION+"')"); throw new RollbackException(); } // REQUIRES_NEW propagation properties - is no exception is thrown caller @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW) public void testRequiresNewNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.REQUIRES_NEW_NO_EXCEPTION+"')"); }
The above examples and the same caller, we look at the implementation of the direct
2019-10-16 16: 29: 20.296 INFO 15553 --- [main] cettTransactionApplication: a first 0 2019-10-16 16: 29: 20.298 INFO 15553 --- [main] cettTransactionApplication: a second case
We find our reasoning is the same, indicating that the transaction and the caller's caller's transaction completely unrelated. At this point we should just understand this sentence regardless of whether there is a current transaction, will be starting a new transaction .
PROPAGATION_NOT_SUPPORTED
Whether the caller has the transaction, the caller is not running in a transaction method
Also these two examples
The caller has affairs | The caller has abnormal | The caller has abnormal |
---|---|---|
no | no | Have |
Have | Have | no |
The first case: the caller will not be a transaction, then the corresponding data can be found after throwing an exception
The second case:, the caller will be run in the absence of environmental affairs at the caller has transaction situation, so we can still be found in the data
Then validate our guess
// NOT_SUPPORTED propagation properties - callee exception is thrown @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED) public void testNotSupportHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NOT_SUPPORTS_HAS_EXCEPTION+"')"); throw new RollbackException(); } // NOT_SUPPORTED propagation properties - is no exception is thrown caller @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED) public void testNotSupportNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NOT_SUPPORTS_NO_EXCEPTION+"')"); }
And then view the results
2019-10-16 16:38:35.065 INFO 15739 --- [ main] c.e.t.t.TransactionApplication : 第一种情况 1 2019-10-16 16:38:35.067 INFO 15739 --- [ main] c.e.t.t.TransactionApplication : 第二种情况 1
我们可以看到在最后两种情况都查到了数据,根据演示效果应该可以理解这句话了,不支持事务,如果当前存在事务,就将此事务挂起不以事务方式运行。
PROPAGATION_NEVER
调用者有事务,被调用者就会抛出异常
调用者是否有事务 | 调用者是否有异常 | 被调用者是否有异常 |
---|---|---|
无 | 无 | 有 |
有 | 有 | 无 |
这个就不演示,相信大家看到这里应该都会明白在第一种情况下我们是能够查到数据的。在第二种情况下由于调用者带着事务,所以会抛异常。
PROPAGATION_NESTED
此传播属性下,被调用者的事务是调用者的事务的子集。
我们重点说一下NESTED
的传播属性的特性
调用者是否有事务 | 说明 |
---|---|
有 | 被调用者会新起一个事务,此事务和调用者事务是一个嵌套的关系 |
无 | 被调用者会自己新起一个事务 |
关于什么是嵌套事务的关系,我们用下面三个例子能够进行演示。
调用者是否有事务 | 调用者是否有异常 | 被调用者是否有异常 |
---|---|---|
无 | 无 | 有 |
有 | 有 | 无 |
有 | 无 | 有 |
第一种情况:如果查不到数据,则说明在调用者无事务情况下,被调用者会新起一个事务
第二种情况:如果查不到数据,说明外层事务能够影响内层事务
第三种情况:如果查到数据,说明内层事务不影响外层事务
接下来我们编写具体的代码
// NESTED传播属性-回滚事务 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED) public void testNestedHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_HAS_EXCEPTION+"')"); // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); throw new RollbackException(); } // NESTED传播属性-不回滚事务 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED) public void testNestedNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_NO_EXCEPTION+"')"); }
然后接下来的调用者也会有点区别
@Override @Transactional() public void hasTransactionalNoException() throws RollbackException { // NESTED传播属性 - 调用者有事务,不抛异常 被调用者有异常 jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_HAS_EXCEPTION_TWO+"')"); fooService.testNestedHasException(); }
然后执行效果
2019-10-16 18:01:06.387 INFO 17172 --- [ main] c.e.t.t.TransactionApplication : 第一种情况 0 2019-10-16 18:01:06.389 INFO 17172 --- [ main] c.e.t.t.TransactionApplication : 第二种情况 0 2019-10-16 18:01:06.390 INFO 17172 --- [ main] c.e.t.t.TransactionApplication : 第三种情况 1
可以看出来嵌套事务的本质就是外层会影响内层,内层不影响外层。而REQUIRES_NEW
则是互不影响。
总结
到现在我们已经全部分析完了七种传播属性,从写这篇文章开始到结束其中也碰到过一些坑,有些是不自己实践一遍是根本不知道的,所以我还是建议读者看完这篇文章以后自己进行实践,演示各种情况,只有这样才能够烂熟于心。