SpringFramework-03_Spring声明式事务

1. 声明式事务概念

1.1 声明式事务

声明式事务是指使用注解或 XML 配置的方式来控制事务的提交和回滚。

开发者只需要添加配置即可, 具体事务的实现由第三方框架实现,避免直接进行事务操作。

使用声明式事务可以将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性。

区别:

  • 编程式事务需要手动编写代码来管理事务
  • 而声明式事务可以通过配置文件或注解来控制事务。

1.2 Spring事务管理器

  1. Spring声明式事务对应依赖

    • spring-tx: 包含声明式事务实现的基本规范(事务管理器规范接口和事务增强等等)
    • spring-jdbc: 包含DataSource方式事务管理器实现类DataSourceTransactionManager
    • spring-orm: 包含其他持久层框架的事务管理器实现类例如:Hibernate/Jpa等
  2. Spring声明式事务对应事务管理器接口
    在这里插入图片描述

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

    DataSourceTransactionManager类中的主要方法:

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

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

2.1 基本事务控制

  1. 配置事务管理器
    数据库相关的配置

    @Configuration
    @ComponenScan("com.start")
    @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("${jdbc.url}")String url,
                                     @Value("${jdbc.driver}")String driver,
                                     @Value("${jdbc.username}")String username,
                                     @Value("${jdbc.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);
        }
    
    }
    
    
  2. 使用声明事务注解@Transactional

    @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);
        }
    }
    

2.2 事务属性:只读

  1. 只读介绍
    对一个查询操作来说,如果把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。

  2. 设置方式

    // readOnly = true把当前事务设置为只读 默认是false!
    @Transactional(readOnly = true)
    
  3. 针对DML动作设置只读模式

    会抛出下面异常:

    Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

  4. @Transactional注解放在类上

    1. 生效原则

      @Transactional 注解在类级别标记,会影响到类中的每一个方法。同时,类级别标记的 @Transactional 注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了 @Transactional 注解。

      方法离最近的 @Transactional 注解中的事务属性设置生效。

    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) {
          ……
          }
          
      }
      

2.3 事务属性:超时时间

  1. 设置超时时间

    @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);
        }
    }
    

2.4 事务属性:事务异常

  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);
        }
    }
    
  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);
    }
    
  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);
        }
    }
    
    

2.5 事务属性:事务隔离级别

  1. 事务隔离级别

    数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。

    常见的隔离级别:

    1. 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
    2. 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
    3. 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
    4. 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
  2. 事务隔离级别设置

    import com.atguigu.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;
    @Service
    public class StudentService {
    
        @Autowired
        private StudentDao studentDao;
    
        /**
         * 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);
            new FileInputStream("xxxx");
            studentDao.updateNameById("test1",1);
        }
    }
    
    

2.6 事务属性:事务传播行为

  1. propagation属性

    @Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是:

    Propagation propagation() default Propagation.REQUIRED;
    

    propagation 属性的可选值由 org.springframework.transaction.annotation.Propagation 枚举类提供:

    名称含义
    REQUIRED 默认值如果父方法有事务,就加入,如果没有就新建自己独立
    REQUIRES_NEW不管父方法是否有事务,都新建事务,都是独立的
  2. 测试

    1. 声明两个业务方法

      @Service
      public class StudentService {
      
          @Autowired
          private StudentDao studentDao;
      
          @Transactional(readOnly = false,
                         timeout = 3,
                         rollbackFor = Exception.class,
                         noRollbackFor = FileNotFoundException.class,
                         isolation = Isolation.REPEATABLE_READ)
          public void changeInfo() throws FileNotFoundException {
              studentDao.updateAgeById(100,1);
              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;
          }
      
      }
      
    2. 声明一个整合业务方法

      @Service
      public class TopService {
      
          @Autowired
          private StudentService studentService;
      
          @Transactional
          public void  topService(){
              studentService.changeAge();
              studentService.changeName();
          }
      }
      
      
    3. 添加传播行为测试

      @SpringJUnitConfig(classes = AppConfig.class)
      public class TxTest {
      
          @Autowired
          private StudentService studentService;
      
          @Autowired
          private TopService topService;
      
          @Test
          public void  testTx() throws FileNotFoundException {
              topService.topService();
          }
      }
      
      

    注意:

    在同一个类中,对于@Transactional注解的方法调用,事务传播行为不会生效。因为Spring框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值