Transactional Transactional in SpringBoot series

When we want a set of operations that will either all succeed or all fail, we tend to consider transactions to do this; Before the introduction of the db operation, mainly lies in the single table CURD, this article will mainly introduce the use of declarative transaction @ Transactional position

I. the configuration

Mybatis uses JPA as a Transactional annotation. Mybatis uses jdbcTemplate as a Transactional annotation. Mybatis uses JPA as a Transactional annotation

Create a SpringBoot project with version 2.2.1.RELEASE, use mysql as the target database, select Innodb as the storage engine, and set transaction isolation level to RR

1. Project configuration

In pom project. The XML file, plus the spring – the boot – starter – JDBC, will inject a DataSourceTransactionManager bean, provides 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>
Copy the code

2. Configure the database

Go to the Spring configuration file application.properties and set up db related information

# # the DataSource spring. The DataSource. Url = JDBC: mysql: / / 127.0.0.1:3306 / story? useUnicode=true&characterEncoding=UTF-8&useSSL=false spring.datasource.username=root spring.datasource.password=Copy the code

3. The database

Create a simple table structure for testing

CREATE TABLE `money` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT ' ' COMMENT 'Username'.`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;
Copy the code

II. Instructions

1. Initialization

In order to reflect the characteristics of transactions, without considering DDL, add, delete or modify statements in DML are indispensable, so we need to initialize several data for testing

@Service
public class SimpleDemo {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void init(a) {
        String sql = "Replace into money (id, name, money) values (120, 'initialize ', 200)," +
                "(130, 'initialize ', 200) +
                "(140, 'initialize ', 200)," +
                "(150, 'initialize ', 200)"; jdbcTemplate.execute(sql); }}Copy the code

We use the Replace into statement to initialize the data, which is executed each time the bean is created, ensuring that the initial data is the same each time you perform subsequent operations

2. transactional

This annotation can be placed on a class or a method; If it’s annotated on a class, then all the public methods of that class support transactions;

Annotations on the method are configured to override annotations on the class if they exist on both classes and methods

Here is a simple transaction test case

private boolean updateName(int id) {
    String sql = "Update money set 'name' =' update 'where id=" + id;
    jdbcTemplate.execute(sql);
    return true;
}

public void query(String tag, int id) {
    String sql = "select * from money where id=" + id;
    Map map = jdbcTemplate.queryForMap(sql);
    System.out.println(tag + "> > > >" + map);
}

private boolean updateMoney(int id) {
    String sql = "update money set `money`= `money` + 10 where id=" + id;
    jdbcTemplate.execute(sql);
    return false;
}

/** * Rollback occurred due to an exception@return* /
@Transactional
public boolean testRuntimeExceptionTrans(int id) {
    if (this.updateName(id)) {
        this.query("after updateMoney name", id);
        if (this.updateMoney(id)) {
            return true; }}throw new RuntimeException("Update failed, rollback!");
}
Copy the code

Add the @Transactional annotation to the public method where we need to open the transaction to indicate that the method is properly called and that a transaction rollback occurs if a run exception is thrown from within the method

Note the above statement, the correct call posture, the transaction will take effect; In other words, in some cases, it doesn’t work

3. The test

Next, to test whether the method transaction above works, we create a new Bean

@Component
public class TransactionalSample {
    @Autowired
    private SimpleDemo simpleService;

    public void testSimpleCase(a) {
        System.out.println("============ transaction working fine start ==========");
        simpleService.query("transaction before".130);
        try {
            // The transaction will work
            simpleService.testRuntimeExceptionTrans(130);
        } catch (Exception e) {
        }
        simpleService.query("transaction end".130);
        System.out.println("============ transaction working end ========== \n"); }}Copy the code

In the above call, the data before the modification is printed and the data after the modification is printed, and if the transaction is working properly, the two outputs should be consistent

The actual output is as follows, verifying that the transaction is in effect and that the intermediate operation to change the name is rolled back

= = = = = = = = = = = = affairs work normally start = = = = = = = = = = transaction before > > > > {id = 130, name = initialization, money = 200, is_deleted =false, create_at=2020-01-19 16:15:21.0, update_at=2020-01-19 16:15:21.0} after updateMoney name >>>> {id=130, name= updateMoney, money=200, is_deleted=false, create_at=2020-01-19 16:15:21.0, update_at=2020-01-19 16:15:22.0} transaction end >>>> {id=130, name= initialization, money=200, is_deleted=false, create_at = 2020-01-19 16:15:21. 0, update_at = 2020-01-19 16:15:21. 0} = = = = = = = = = = = = affairs work normally end = = = = = = = = = =Copy the code

4. Precautions

A. Application scenario

When using the @Transactional Transactional annotation, AOP is used to encapsulate the Transactional logic through proxies, so scenarios where AOP does not work are also applicable to scenarios where the Transactional annotation does not work

Briefly, in the following cases, annotations do not take effect

  • Private method decorates@TransactionalAnd don’t take effect
  • Internal call, not valid
    • For example, an external call to the normal method M of service A calls the method M2 declared with transaction annotations in this class. Under normal circumstances, the transaction does not take effect

B. Exception type

In addition, the @Transactional annotation defaults only to runtime exceptions, such as the case below, which throws an exception but does not take effect

@Transactional
public boolean testNormalException(int id) throws Exception {
    if (this.updateName(id)) {
        this.query("after updateMoney name", id);
        if (this.updateMoney(id)) {
            return true; }}throw new Exception("Declaration exception");
}
Copy the code

If you want it to work, you can specify the type of exception that triggers the rollback using the rollbackFor property


@Transactional(rollbackFor = Exception.class)
public boolean testSpecialException(int id) throws Exception {
    if (this.updateName(id)) {
        this.query("after updateMoney name", id);
        if (this.updateMoney(id)) {
            return true; }}throw new IllegalArgumentException("Abnormal parameter");
}
Copy the code

Test the two cases above

public void testSimpleCase(a) {
    System.out.println("============ transaction not effective start ==========");
    simpleService.query("transaction before".140);
    try {
        // Because it is a non-running exception, it will not be rolled back
        simpleService.testNormalException(140);
    } catch (Exception e) {
    }
    simpleService.query("transaction end".140);
    System.out.println("============ transaction invalid end ========== \n");


    System.out.println("============ transaction effective start ==========");
    simpleService.query("transaction before".150);
    try {
        // Specify that all exceptions are rolled back in the annotation
        simpleService.testSpecialException(150);
    } catch (Exception e) {
    }
    simpleService.query("transaction end".150);
    System.out.println("============ transaction effective end ========== \n");
}
Copy the code

The output is as follows, which validates what was proposed above

= = = = = = = = = = = = will not commence transaction start = = = = = = = = = = transaction before > > > > {id = 140, name = initialization, money = 200, is_deleted =false, create_at=2020-01-19 16:15:21.0, update_at=2020-01-19 16:15:21.0} after updateMoney name >>>> {id=140, name= updateMoney, money=200, is_deleted=false, create_at=2020-01-19 16:15:22.0, update_at=2020-01-19 16:15:22.0} transaction end >>>> {id=140, name= update, money=210, is_deleted=false, create_at = 2020-01-19 16:15:21. 0, Update_at = 2020-01-19 16:15:22. 0} = = = = = = = = = = = = will not commence transaction end = = = = = = = = = = = = = = = = = = = = = = transaction effective start = = = = = = = = = = transaction before >>>> {id=150, name= initialization, money=200, is_deleted=false, create_at=2020-01-19 16:15:21.0, update_at=2020-01-19 16:15:21.0} after updateMoney name >>>> {id=150, name= updateMoney, money=200, is_deleted=false, create_at=2020-01-19 16:15:21.0, update_at=2020-01-19 16:15:22.0} transaction end >>>> {id=150, name= initialization, money=200, is_deleted=false, create_at = 2020-01-19 16:15:21. 0, update_at = 2020-01-19 16:15:21. 0} = = = = = = = = = = = = transaction effective end = = = = = = = = = =Copy the code

Attribute information for the C.@Transactional annotation

The above contents are relatively basic knowledge points, enough to meet our general business needs. If you need to advance, it is necessary to understand the attribute information

Get a thorough understanding of the use of @Transactional in Spring

The property name instructions
name When you have multiple transactionmanagers in a configuration file, you can use this property to specify which TransactionManager to select.
propagation The propagation behavior of the transaction, which defaults to REQUIRED.
isolation The isolation degree of a transaction is DEFAULT.
timeout Timeout period for a transaction. Default value is -1. If the time limit is exceeded but the transaction has not completed, the transaction is automatically rolled back.
read-only Specifies whether the transaction is read-only. The default value is false. To ignore methods that do not require transactions, such as reading data, you can set read-only to true.
rollback-for Specifies the type of exception that can trigger transaction rollback. If more than one exception type needs to be specified, separate the exception types by commas.
no-rollback- for Throw the exception type specified by no-rollback-for without rolling back the transaction.

For more information on the use of the above attributes and the circumstances under which declarative transactions will not take effect, stay tuned.

II. The other

Series of blog posts & source code

Series of blog posts

  • 180926- Basic use of SpringBoot advanced DB
  • 190407-SpringBoot Advanced JdbcTemplate data insert position details
  • 190412-SpringBoot Advanced JdbcTemplate data query first
  • 190417-SpringBoot Advanced JdbcTemplate data query next
  • 190418-SpringBoot Advanced JdbcTemplate data update and delete

The source code

  • Project: github.com/liuyueyi/sp…
  • Example source: github.com/liuyueyi/sp…

1. An ashy Blog

As far as the letter is not as good, the above content is purely one’s opinion, due to the limited personal ability, it is inevitable that there are omissions and mistakes, if you find bugs or have better suggestions, welcome criticism and correction, don’t hesitate to appreciate

Below a gray personal blog, record all the study and work of the blog, welcome everyone to go to stroll

  • A grey Blog Personal Blog blog.hhui.top
  • A Grey Blog-Spring feature Blog Spring.hhui.top