Spring transaction attributes in detail

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.sqldocument 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 FooServiceand BarService. We use BarServiceinside the method call FooServicemethod.

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 RollbackExceptionis 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 BarServicethe 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 @Transactionalannotation in the propagationproperty 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则是互不影响

总结

到现在我们已经全部分析完了七种传播属性,从写这篇文章开始到结束其中也碰到过一些坑,有些是不自己实践一遍是根本不知道的,所以我还是建议读者看完这篇文章以后自己进行实践,演示各种情况,只有这样才能够烂熟于心。


Guess you like

Origin blog.51cto.com/14558453/2447015