资讯专栏INFORMATION COLUMN

SpringBoot 实战 (十) | 声明式事务

ygyooo / 1870人阅读

摘要:前言如题,今天介绍的声明式事务。提供一个注解在配置类上来开启声明式事务的支持。而在配置里还开启了对声明式事务的支持,代码如下所以在中,无须显式开启使用注解。源码下载后语以上为声明式事务的教程。

微信公众号:一个优秀的废人
如有问题或建议,请后台留言,我会尽力解决你的问题。
前言

如题,今天介绍 SpringBoot 的 声明式事务。

Spring 的事务机制

所有的数据访问技术都有事务处理机制,这些技术提供了 API 用于开启事务、提交事务来完成数据操作,或者在发生错误时回滚数据。

而 Spring 的事务机制是用统一的机制来处理不同数据访问技术的事务处理,Spring 的事务机制提供了一个 PlatformTransactionManager 接口,不同的数据访问技术的事务使用不同的接口实现,如下表:

数据访问技术 实现
JDBC DataSourceTransactionManager
JPA JPATransactionManager
Hibernate HibernateTransactionManager
JDO JdoTransactionManager
分布式事务 JtaTransactionManager
声明式事务

Spring 支持声明式事务,即使用注解来选择需要使用事务的方法,他使用 @Transactional 注解在方法上表明该方法需要事务支持。被注解的方法在被调用时,Spring 开启一个新的事务,当方法无异常运行结束后,Spring 会提交这个事务。如:

@Transactional
public void saveStudent(Student student){
        // 数据库操作
}

注意,@Transactional 注解来自于 org.springframework.transcation.annotation 包,而不是 javax.transaction。

Spring 提供一个 @EnableTranscationManagement 注解在配置类上来开启声明式事务的支持。使用了 @EnableTranscationManagement 后,Spring 容器会自动扫描注解 @Transactional 的方法与类。@EnableTranscationManagement 的使用方式如下:

@Configuration
@EnableTranscationManagement 
public class AppConfig{

}
注解事务行为

@Transactional 有如下表所示的属性来定制事务行为。

属性 含义
propagation 事务传播行为
isolation 事务隔离级别
readOnly 事务的读写性,boolean型
timeout 超时时间,int型,以秒为单位。
rollbackFor 一组异常类,遇到时回滚。(rollbackFor={SQLException.class})
rollbackForCalssName 一组异常类名,遇到回滚,类型为 string[]
noRollbackFor 一组异常类,遇到不回滚
norollbackForCalssName 一组异常类名,遇到时不回滚。
类级别使用 @Transactional

@Transactional 不仅可以注解在方法上,还可以注解在类上。注解在类上时意味着此类的所有 public 方法都是开启事务的。如果类级别和方法级别同时使用了 @Transactional 注解,则使用在类级别的注解会重载方法级别的注解。

SpringBoot 的事务支持

自动配置的事务管理器

在使用 JDBC 作为数据访问技术时,配置定义如下:

@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(DataSource.class)
public PlatformTransactionManager transactionManager(){
    return new DataSourceTransactionManager(this.dataSource)
}

在使用 JPA 作为数据访问技术时,配置定义如下:

@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager(){
    return new JpaTransactionManager()
}

自动开启注解事务的支持

SpringBoot 专门用于配置事务的类为 org.springframework.boot.autoconfigure.transcation.TransactionAutoConfiguration,此配置类依赖于 JpaBaseConfiguration 和 DataSourceTransactionManagerAutoConfiguration。
而在 DataSourceTransactionManagerAutoConfiguration 配置里还开启了对声明式事务的支持,代码如下:

@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
@Configuration
@EnableTransactionManagement
protected static class TransactionManagementConfiguration{

}

所以在 SpringBoot 中,无须显式开启使用 @EnableTransactionManagement 注解。

实战

演示如何使用 Transactional 使用异常导致数据回滚与使用异常导致数据不回滚。

准备工作:

SpringBoot 2.1.3
JDK 1.8
IDEA

pom.xml 依赖:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.3.RELEASE
         
    
    com.nasus
    transaction
    0.0.1-SNAPSHOT
    transaction
    transaction Demo project for Spring Boot

    
        1.8
    

    
        
        
            org.springframework.boot
            spring-boot-starter-data-jpa
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            mysql
            mysql-connector-java
            runtime
        
        
        
            org.projectlombok
            lombok
            1.16.20
        
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

代码注释很清楚,没啥好说的。

application.yaml 配置:

spring:
  # u6570u636Eu5E93u76F8u5173
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true
    username: root
    password: 123456
  # jpa u76F8u5173
  jpa:
    hibernate:
      ddl-auto: update   # ddl-auto:u8BBEu4E3A create u8868u793Au6BCFu6B21u90FDu91CDu65B0u5EFAu8868
    show-sql: true

实体类:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    @Id
    @GeneratedValue
    private Integer id;

    private String name;

    private Integer age;
}

dao 层

import com.nasus.transaction.domain.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository {
}

service 层

import com.nasus.transaction.domain.Student;

public interface StudentService {

    Student saveStudentWithRollBack(Student student);

    Student saveStudentWithoutRollBack(Student student);

}

实现类:

import com.nasus.transaction.domain.Student;
import com.nasus.transaction.repository.StudentRepository;
import com.nasus.transaction.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    // 直接注入 StudentRepository 的 bean
    private StudentRepository studentRepository;

    // 使用 @Transactional 注解的 rollbackFor 属性,指定特定异常时,触发回滚
    @Transactional(rollbackFor = {IllegalArgumentException.class})
    @Override
    public Student saveStudentWithRollBack(Student student) {
        Student s = studentRepository.save(student);
        if ("高斯林".equals(s.getName())){
            //硬编码,手动触发异常
            throw new IllegalArgumentException("高斯林已存在,数据将回滚");
        }
        return s;
    }

    // 使用 @Transactional 注解的 noRollbackFor 属性,指定特定异常时,不触发回滚
    @Transactional(noRollbackFor = {IllegalArgumentException.class})
    @Override
    public Student saveStudentWithoutRollBack(Student student) {
        Student s = studentRepository.save(student);
        if ("高斯林".equals(s.getName())){
            throw new IllegalArgumentException("高斯林已存在,数据将不会回滚");
        }
        return s;
    }

}

代码注释同样很清楚,没啥好说的。

controller 层

import com.nasus.transaction.domain.Student;
import com.nasus.transaction.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/student")
public class StudentController {

    // 注入 studentservice 的 bean
    @Autowired
    private StudentService studentService;

    // 测试回滚情况
    @PostMapping("/withRollBack")
    public Student saveStudentWithRollBack(@RequestBody Student student){
        return studentService.saveStudentWithRollBack(student);
    }

    // 测试不回滚情况
    @PostMapping("/withOutRollBack")
    public Student saveStudentWithoutRollBack(@RequestBody Student student){
        return studentService.saveStudentWithoutRollBack(student);
    }
}
Postman 测试结果

为了更清楚地理解回滚,以 debug (调试模式) 启动程序。并在 StudentServiceImpl 的 saveStudentWithRollBack 方法上打上断点。

测试前数据库结果:

Postman 测试回滚


debug 模式下可见数据已保存,且获得 id 为 1。:

继续执行抛出异常 IllegalArgumentException,将导致数据回滚:

测试后数据库结果:并无新增数据,回滚成功。

Postman 测试不回滚

测试前数据库结果:

遇到 IllegalArgumentException 异常数据不会回滚:

测试后数据库结果:新增数据,数据不回滚。

源码下载

https://github.com/turoDog/Demo/tree/master/springboot_transaction_demo

后语

以上为 SpringBoot 声明式事务的教程。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。

另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73359.html

相关文章

  • SpringBoot非官方教程 | 第七篇:SpringBoot开启声明事务

    摘要:准备阶段以上一篇文章的代码为例子,即整合,上一篇文章是基于注解来实现的数据访问层,这篇文章基于的来实现,并开启声明式事务。创建实体类数据访问层接口层用户减块用户加块,声明事务,并设计一个转账方法,用户减块,用户加块。 springboot开启事务很简单,只需要一个注解@Transactional 就可以了。因为在springboot中已经默认对jpa、jdbc、mybatis开启了事事...

    tyheist 评论0 收藏0
  • 深刻理解Spring声明事务

    摘要:支持声明式事务,通过注解控制方法是否支持事务。声明式事务,基于实现,将具体业务和业务逻辑解耦。该级别下事务顺序执行,阻止上面的缺陷,开销很大。 问题引入 Spring中事务传播有哪几种,分别是怎样的? 理解注解事务的自动配置? SpringBoot启动类为什么不需要加@EnableTransactionManagement注解? 声明式事务的实现原理?(待补充) ...

    Cheng_Gang 评论0 收藏0

发表评论

0条评论

ygyooo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<