一、什么是事务?

我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合。由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常,异常会导致后续操作无法完成,此时由于业务逻辑并未正确完成,之前成功操作的数据并不可靠,需要在这种情况下进行回退。
事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态。
事务管理是Spring框架中最为常用的功能之一,我们在使用Spring Boot开发应用时,大部分情况下也都需要使用事务。

二、快速入门

在Spring Boot中,当我们使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖的时候,框架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager。所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用。这种方式的优点是:

  • 非侵入式,业务逻辑不受事务管理代码的污染。
  • 方法级别的事务回滚,合理划分方法的粒度可以做到符合各种业务场景的事务管理。

三、工程详解

3.1、pom文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>spring-boot</artifactId>
  7. <groupId>com.wells.demo</groupId>
  8. <version>1.0.1.0</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>mybatis-transactional</artifactId>
  12. <dependencies>
  13. <dependency>
  14. <groupId>io.springfox</groupId>
  15. <artifactId>springfox-swagger-ui</artifactId>
  16. <version>2.9.2</version>
  17. </dependency>
  18. <dependency>
  19. <groupId>io.springfox</groupId>
  20. <artifactId>springfox-swagger2</artifactId>
  21. <version>2.9.2</version>
  22. </dependency>
  23. <!-- Spring Boot Mybatis 依赖 -->
  24. <dependency>
  25. <groupId>org.mybatis.spring.boot</groupId>
  26. <artifactId>mybatis-spring-boot-starter</artifactId>
  27. <version>2.0.0</version>
  28. </dependency>
  29. <!-- MySQL 连接驱动依赖 -->
  30. <dependency>
  31. <groupId>mysql</groupId>
  32. <artifactId>mysql-connector-java</artifactId>
  33. <version>8.0.11</version>
  34. </dependency>
  35. </dependencies>
  36. </project>

3.2、application.properties

server.port=8081

# datasource config
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_demo_db?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root1234!
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

3.3、启动类打开事务管理

package com.wells.demo.mybatis.transactional;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

// 开启事务
@EnableTransactionManagement
@SpringBootApplication
@Configuration
@EnableSwagger2
public class MyBatisTransactionalApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyBatisTransactionalApplication.class, args);
    }

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wells.demo.mybatis.transactional"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Spring Boot中使用MyBatis Transactional")
                .description("更多Spring Boot相关文章请关注:https://www.yuque.com/wells/micro.service")
                .contact(new Contact("wells", "", ""))
                .version("2.0")
                .build();
    }
}

3.4、Service

在设计service层的时候,应该合理的抽象出方法包含的内容。
然后将方法用@Trasactional注解注释,默认的话在抛出Exception.class异常的时候,就会触发方法中所有数据库操作回滚,当然这指的是增、删、改。
当然,@Transational方法是可以带参数的,具体的参数解释如下:

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组
package com.wells.demo.mybatis.transactional.service.impl;

import com.wells.demo.mybatis.transactional.entity.User;
import com.wells.demo.mybatis.transactional.mapper.UserMapper;
import com.wells.demo.mybatis.transactional.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Created by Wells on 2019年02月18日
 */

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    // 方法上声明事务
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertUserException(User user) {
        // 正常处理
        int insertRes = userMapper.insertUserByObj(user);
        // 手动抛出异常: 出现异常后,插入不成功
        int exp = insertRes / 0;
        return insertRes;
    }
}

上面的例子中我们使用了默认的事务配置,可以满足一些基本的事务需求,但是当我们项目较大较复杂时(比如,有多个数据源等),这时候需要在声明事务时,指定不同的事务管理器。对于不同数据源的事务管理配置可以见《Spring Boot多数据源配置与使用》中的设置。在声明事务时,只需要通过value属性指定配置的事务管理器名即可,例如:@Transactional(value=”transactionManagerPrimary”)。

四、详细代码示例

SpringBoot MyBatis Transactional