• SSM - Springboot - MyBatis-Plus 全栈体系(十二)


    第二章 SpringFramework

    六、Spring 声明式事务

    1. 声明式事务概念

    1. 编程式事务
    • 编程式事务是指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。在 Java 中,通常使用事务管理器(如 Spring 中的 PlatformTransactionManager)来实现编程式事务。
    • 编程式事务的主要优点是灵活性高,可以按照自己的需求来控制事务的粒度、模式等等。但是,编写大量的事务控制代码容易出现问题,对代码的可读性和可维护性有一定影响。
    Connection conn = ...;
    
    try {
        // 开启事务:关闭事务的自动提交
        conn.setAutoCommit(false);
        // 核心操作
        // 业务代码
        // 提交事务
        conn.commit();
    
    }catch(Exception e){
    
        // 回滚事务
        conn.rollBack();
    
    }finally{
    
        // 释放数据库连接
        conn.close();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 编程式的实现方式存在缺陷:

      • 细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
      • 代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用。
    2. 声明式事务
    • 声明式事务是指使用注解或 XML 配置的方式来控制事务的提交和回滚。
    • 开发者只需要添加配置即可, 具体事务的实现由第三方框架实现,避免我们直接进行事务操作!
    • 使用声明式事务可以将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性。
    • 区别:
      • 编程式事务需要手动编写代码来管理事务
      • 而声明式事务可以通过配置文件或注解来控制事务
    3. Spring 事务管理器
    3.1 Spring 声明式事务对应依赖
    • spring-tx: 包含声明式事务实现的基本规范(事务管理器规范接口和事务增强等等)
    • spring-jdbc: 包含 DataSource 方式事务管理器实现类 DataSourceTransactionManager
    • spring-orm: 包含其他持久层框架的事务管理器实现类例如:Hibernate/Jpa 等
    3.2 Spring 声明式事务对应事务管理器接口

    在这里插入图片描述

    • 我们现在要使用的事务管理器是 org.springframework.jdbc.datasource.DataSourceTransactionManager,将来整合 JDBC 方式、JdbcTemplate 方式、Mybatis 方式的事务实现!

    • DataSourceTransactionManager 类中的主要方法:

      • doBegin():开启事务
      • doSuspend():挂起事务
      • doResume():恢复挂起的事务
      • doCommit():提交事务
      • doRollback():回滚事务

    2. 基于注解的声明式事务

    2.1 准备工作
    2.1.1 准备项目
    <dependencies>
      
      
      <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-contextartifactId>
          <version>6.0.6version>
      dependency>
    
      
      <dependency>
          <groupId>org.junit.jupitergroupId>
          <artifactId>junit-jupiter-apiartifactId>
          <version>5.3.1version>
      dependency>
    
    
      <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-testartifactId>
          <version>6.0.6version>
          <scope>testscope>
      dependency>
    
      <dependency>
          <groupId>jakarta.annotationgroupId>
          <artifactId>jakarta.annotation-apiartifactId>
          <version>2.1.1version>
      dependency>
    
      
      <dependency>
          <groupId>mysqlgroupId>
          <artifactId>mysql-connector-javaartifactId>
          <version>8.0.25version>
      dependency>
    
      <dependency>
          <groupId>com.alibabagroupId>
          <artifactId>druidartifactId>
          <version>1.2.8version>
      dependency>
    
      
      <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-jdbcartifactId>
          <version>6.0.6version>
      dependency>
    
      
      <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-txartifactId>
          <version>6.0.6version>
      dependency>
    
    
      <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-aopartifactId>
          <version>6.0.6version>
      dependency>
    
      <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-aspectsartifactId>
          <version>6.0.6version>
      dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    2.1.2 外部配置文件
    • jdbc.properties
    alex.url=jdbc:mysql://localhost:3306/studb
    alex.driver=com.mysql.cj.jdbc.Driver
    alex.username=root
    alex.password=root
    
    • 1
    • 2
    • 3
    • 4
    2.1.3 spring 配置文件
    @Configuration
    @ComponentScan("com.alex")
    @PropertySource("classpath:jdbc.properties")
    public class JavaConfig {
    
        @Value("${alex.driver}")
        private String driver;
        @Value("${alex.url}")
        private String url;
        @Value("${alex.username}")
        private String username;
        @Value("${alex.password}")
        private String password;
    
    
    
        //druid连接池
        @Bean
        public DataSource dataSource(){
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    
    
        @Bean
        //jdbcTemplate
        public JdbcTemplate jdbcTemplate(DataSource dataSource){
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
            return jdbcTemplate;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    2.1.4 准备 dao/service 层
    2.1.4.1 dao
    @Repository
    public class StudentDao {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        public void updateNameById(String name,Integer id){
            String sql = "update students set name = ? where id = ? ;";
            int rows = jdbcTemplate.update(sql, name, id);
        }
    
        public void updateAgeById(Integer age,Integer id){
            String sql = "update students set age = ? where id = ? ;";
            jdbcTemplate.update(sql,age,id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    2.1.4.2 service
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        public void changeInfo(){
            studentDao.updateAgeById(100,1);
            System.out.println("-----------");
            studentDao.updateNameById("test1",1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.1.5 测试环境搭建
    /**
     * projectName: com.alex.test
     *
     * description:
     */
    @SpringJUnitConfig(JavaConfig.class)
    public class TxTest {
    
        @Autowired
        private StudentService studentService;
    
        @Test
        public void  testTx(){
            studentService.changeInfo();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    2.2 基本事务控制
    2.2.1 配置事务管理器
    • 数据库相关的配置
    /**
     * projectName: com.alex.config
     *
     * description: 数据库和连接池配置类
     */
    
    @Configuration
    @ComponenScan("com.alex")
    @PropertySource(value = "classpath:jdbc.properties")
    @EnableTransactionManagement
    public class DataSourceConfig {
    
        /**
         * 实例化dataSource加入到ioc容器
         * @param url
         * @param driver
         * @param username
         * @param password
         * @return
         */
        @Bean
        public DataSource dataSource(@Value("${alex.url}")String url,
                                     @Value("${alex.driver}")String driver,
                                     @Value("${alex.username}")String username,
                                     @Value("${alex.password}")String password){
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
    
            return dataSource;
        }
    
        /**
         * 实例化JdbcTemplate对象,需要使用ioc中的DataSource
         * @param dataSource
         * @return
         */
        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource){
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
            return jdbcTemplate;
        }
    
        /**
         * 装配事务管理实现对象
         * @param dataSource
         * @return
         */
        @Bean
        public TransactionManager transactionManager(DataSource dataSource){
            return new DataSourceTransactionManager(dataSource);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    2.2.2 使用声明事务注解@Transactional
    /**
     * projectName: com.alex.service
     *
     */
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        @Transactional
        public void changeInfo(){
            studentDao.updateAgeById(100,1);
            System.out.println("-----------");
            int i = 1/0;
            studentDao.updateNameById("test1",1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    2.2.3 测试事务效果
    /**
     * projectName: com.alex.test
     *
     * description:
     */
    //@SpringJUnitConfig(locations = "classpath:application.xml")
    @SpringJUnitConfig(classes = DataSourceConfig.class)
    public class TxTest {
    
        @Autowired
        private StudentService studentService;
    
        @Test
        public void testTx(){
            studentService.changeInfo();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    2.3 事务属性:只读
    2.3.1 只读介绍
    • 对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。
    2.3.2 设置方式
    // readOnly = true把当前事务设置为只读 默认是false!
    @Transactional(readOnly = true)
    
    • 1
    • 2
    2.3.3 针对 DML 动作设置只读模式
    • 会抛出下面异常:

      • Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
    2.3.4 @Transactional 注解放在类上
    2.3.4.1 生效原则
    • 如果一个类中每一个方法上都使用了 @Transactional 注解,那么就可以将 @Transactional 注解提取到类上。反过来说:@Transactional 注解在类级别标记,会影响到类中的每一个方法。同时,类级别标记的 @Transactional 注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了 @Transactional 注解。
    • 对一个方法来说,离它最近的 @Transactional 注解中的事务属性设置生效。
    2.3.4.2 用法举例
    • 在类级别@Transactional 注解中设置只读,这样类中所有的查询方法都不需要设置@Transactional 注解了。因为对查询操作来说,其他属性通常不需要设置,所以使用公共设置即可。
    • 然后在这个基础上,对增删改方法设置@Transactional 注解 readOnly 属性为 false。
    @Service
    @Transactional(readOnly = true)
    public class EmpService {
    
        // 为了便于核对数据库操作结果,不要修改同一条记录
        @Transactional(readOnly = false)
        public void updateTwice(……) {
        ……
        }
    
        // readOnly = true把当前事务设置为只读
        // @Transactional(readOnly = true)
        public String getEmpName(Integer empId) {
        ……
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    2.4 事务属性:超时时间
    2.4.1 需求
    • 事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是 Java 程序或 MySQL 数据库或网络连接等等)。
    • 此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。
    • 概括来说就是一句话:超时回滚,释放资源。
    2.4.2 设置超时时间
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         */
        @Transactional(readOnly = false,timeout = 3)
        public void changeInfo(){
            studentDao.updateAgeById(100,1);
            //休眠4秒,等待方法超时!
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            studentDao.updateNameById("test1",1);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    2.4.3 测试超时效果
    • 执行抛出事务超时异常
    org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Wed May 24 09:10:43 IRKT 2023
    
      at org.springframework.transaction.support.ResourceHolderSupport.checkTransactionTimeout(ResourceHolderSupport.java:155)
      at org.springframework.transaction.support.ResourceHolderSupport.getTimeToLiveInMillis(ResourceHolderSupport.java:144)
      at org.springframework.transaction.support.ResourceHolderSupport.getTimeToLiveInSeconds(ResourceHolderSupport.java:128)
      at org.springframework.jdbc.datasource.DataSourceUtils.applyTimeout(DataSourceUtils.java:341)
      at org.springframework.jdbc.core.JdbcTemplate.applyStatementSettings(JdbcTemplate.java:1467)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.5 事务属性:事务异常
    2.5.1 默认情况
    • 默认只针对运行时异常回滚,编译时异常不回滚。情景模拟代码如下:
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
         * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
         */
        @Transactional(readOnly = false,timeout = 3)
        public void changeInfo() throws FileNotFoundException {
            studentDao.updateAgeById(100,1);
            //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.5.2 设置回滚异常
    • rollbackFor 属性:指定哪些异常类才会回滚,默认是 RuntimeException and Error 异常方可回滚!
    /**
     * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
     * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
     * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
     */
    @Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class)
    public void changeInfo() throws FileNotFoundException {
        studentDao.updateAgeById(100,1);
        //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
        new FileInputStream("xxxx");
        studentDao.updateNameById("test1",1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.5.3 设置不回滚的异常
    • 在默认设置和已有设置的基础上,再指定一个异常类型,碰到它不回滚。
    • noRollbackFor 属性:指定哪些异常不会回滚, 默认没有指定,如果指定,应该在 rollbackFor 的范围内!
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
         * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
         */
        @Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class,noRollbackFor = FileNotFoundException.class)
        public void changeInfo() throws FileNotFoundException {
            studentDao.updateAgeById(100,1);
            //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.6 事务属性:事务隔离级别
    2.6.1 事务隔离级别
    • 数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:
      • 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
      • 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
      • 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
      • 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
    • 不同的隔离级别适用于不同的场景,需要根据实际业务需求进行选择和调整。
    2.6.2 事务隔离级别设置
    package com.alex.service;
    
    import com.alex.dao.StudentDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    /**
     * projectName: com.alex.service
     */
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
         * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
         * isolation = 设置事务的隔离级别,mysql默认是repeatable read!
         */
        @Transactional(readOnly = false,
                       timeout = 3,
                       rollbackFor = Exception.class,
                       noRollbackFor = FileNotFoundException.class,
                       isolation = Isolation.REPEATABLE_READ)
        public void changeInfo() throws FileNotFoundException {
            studentDao.updateAgeById(100,1);
            //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    2.7 事务属性:事务传播行为
    2.7.1 事务传播行为要研究的问题

    在这里插入图片描述

    • 举例代码:
    @Transactional
    public void MethodA(){
        // ...
        MethodB();
        // ...
    }
    
    //在被调用的子方法中设置传播行为,代表如何处理调用的事务! 是加入,还是新事务等!
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void MethodB(){
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.7.2 propagation 属性
    • @Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是:
    Propagation propagation() default Propagation.REQUIRED;
    
    • 1
    • propagation 属性的可选值由 org.springframework.transaction.annotation.Propagation 枚举类提供:
    名称含义
    REQUIRED 默认值如果父方法有事务,就加入,如果没有就新建自己独立!
    REQUIRES_NEW不管父方法是否有事务,我都新建事务,都是独立的!
    2.7.3 测试
    2.7.3.1 声明两个业务方法
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
         * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
         * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
         * isolation = 设置事务的隔离级别,mysql默认是repeatable read!
         */
        @Transactional(readOnly = false,
                       timeout = 3,
                       rollbackFor = Exception.class,
                       noRollbackFor = FileNotFoundException.class,
                       isolation = Isolation.REPEATABLE_READ)
        public void changeInfo() throws FileNotFoundException {
            studentDao.updateAgeById(100,1);
            //主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    
    
        /**
         * 声明两个独立修改数据库的事务业务方法
         */
        @Transactional(propagation = Propagation.REQUIRED)
        public void changeAge(){
            studentDao.updateAgeById(99,1);
        }
    
        @Transactional(propagation = Propagation.REQUIRED)
        public void changeName(){
            studentDao.updateNameById("test2",1);
            int i = 1/0;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    2.7.3.2 声明一个整合业务方法
    @Service
    public class TopService {
    
        @Autowired
        private StudentService studentService;
    
        @Transactional
        public void topService(){
            studentService.changeAge();
            studentService.changeName();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.7.3.3 添加传播行为测试
    @SpringJUnitConfig(classes = AppConfig.class)
    public class TxTest {
    
        @Autowired
        private StudentService studentService;
    
        @Autowired
        private TopService topService;
    
        @Test
        public void testTx() throws FileNotFoundException {
            topService.topService();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注意:

    • 在同一个类中,对于@Transactional 注解的方法调用,事务传播行为不会生效。这是因为 Spring 框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional 注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。
    2.7.4 其他传播行为值(了解)
    • Propagation.REQUIRED:如果当前存在事务,则加入当前事务,否则创建一个新事务。
    • Propagation.REQUIRES_NEW:创建一个新事务,并在新事务中执行。如果当前存在事务,则挂起当前事务,即使新事务抛出异常,也不会影响当前事务。
    • Propagation.NESTED:如果当前存在事务,则在该事务中嵌套一个新事务,如果没有事务,则与 Propagation.REQUIRED 一样。
    • Propagation.SUPPORTS:如果当前存在事务,则加入该事务,否则以非事务方式执行。
    • Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,挂起该事务。
    • Propagation.MANDATORY:必须在一个已有的事务中执行,否则抛出异常。
    • Propagation.NEVER:必须在没有事务的情况下执行,否则抛出异常。

    七、Spring 核心掌握总结

    核心点掌握
    spring 框架理解spring 家族和 spring framework 框架
    spring 核心功能ioc/di , aop , tx
    spring ioc / di组件管理、ioc 容器、ioc/di , 三种配置方式
    spring aopaop 和 aop 框架和代理技术、基于注解的 aop 配置
    spring tx声明式和编程式事务、动态事务管理器、事务注解、属性
  • 相关阅读:
    【43. 字符串相乘】
    计算机毕业设计Java计算机数字逻辑在线学习系统(源码+系统+mysql数据库+lw文档)
    【Unity3D】初学加密技巧(反破解)
    内衣洗衣机便宜好用的牌子有哪些?四款公认好用洗内裤机推荐
    【iOS】Masonry的基本使用
    记录--vue3实现excel文件预览和打印
    java高级:注解
    2022牛客暑期多校训练营3 个人题解
    FFmpeg 硬件加速介绍
    PC电脑 VMware安装的linux CentOs7如何扩容磁盘?
  • 原文地址:https://blog.csdn.net/sgsgkxkx/article/details/133105845