Spring之事务管理【java全端课31】

242 阅读15分钟

JdbcTemplate基本用法

1.1 JdbcTemplate概念

  • Spring为简化特定领域代码,封装了很多 『Template』形式的模板类。例如:JdbcTemplate、RedisTemplate、RestTemplate 等等。

  • JdbcTemplate 是 Spring JDBC 的核心类之一,它简化了与关系型数据库的交互。通过使用 JdbcTemplate,开发者不需要编写样板代码来管理资源(如打开或关闭连接),处理 SQL 语句,或者处理异常转换。以下是 JdbcTemplate 的一些关键特性和使用方式:

    • 减少样板代码:自动处理数据库资源的获取和释放。
    • SQL 执行:提供了多种方法来执行查询、更新等操作。
    • 结果集映射:支持将结果集映射为 Java 对象,简化数据处理。
    • 事务管理:可以轻松地在 Spring 的事务管理框架下工作。
    • 异常处理:将 JDBC 异常转换为 Spring 的 DataAccessExceptions,便于错误处理。

1.2 JdbcTemplate基本使用

  • 导入jar包(启动器)

  • 编写配置文件:application.properties

    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/spring_tx
    spring.datasource.username=root
    spring.datasource.password=root
    
  • 将JdbcTemplate对象装配到IOC容器中

    • SpringBoot自动配置原理,默认将JdbcTemplate装配到IOC容器

1.3 JdbcTemplate常用API

jdbcTemplate.update(String sql ,Object... args):通用增删改功能

jdbcTemplate.queryForObject(String sql,RowMapper):查询单个对象

jdbcTemplate.queryForList(String sql):查询多个数据Map<String,Object>

jdbcTemplate.query(String sql,RowMapper):查询多个对象

1.4 案例代码

package com.mytest;

import com.mytest.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.util.List;
import java.util.Map;

@SpringBootTest
class Day15SpringTxApplicationTests {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    void contextLoads() {
        System.out.println("jdbcTemplate = " + jdbcTemplate);
    }

   	//添加user信息
    @Test
    public void testAddStudent(){
        String sql = "INSERT INTO t_user(ACCOUNT,PASSWORD,nickname)VALUES(?,?,?)";
        jdbcTemplate.update(sql,"zhangsan","123456","普通员工");
    }

   	//查询单个对象
    @Test
    public void testQueryStudent(){
        String sql = "SELECT id,`account`,`password`,nickname FROM t_user WHERE id = ?";
        // 创建一个映射器
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
        User user = jdbcTemplate.queryForObject(sql, rowMapper,20010);
        System.out.println("user = " + user);
    }

   	//查询所有对象
    @Test
    public void testQueryForList(){
        String sql = "SELECT id,`account`,`password`,nickname FROM t_user";
        // 创建一个映射器
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);

        List<User> userList = jdbcTemplate.query(sql, rowMapper);
        for (User user : userList) {
            System.out.println("user = " + user);
        }

        System.out.println(" ====================== ");
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);

        for (Map<String, Object> map : maps) {
            System.out.println("map = " + map);
        }
    }
}

2 事务概念回顾

2.1 事务概念

在 MySQL 中,事务(Transaction)是一组 SQL 操作的集合,这组操作要么全部执行成功,要么全部不执行,以此来确保数据的一致性和完整性。事务是数据库管理系统(DBMS)执行过程中的一个逻辑工作单元,它具有四个关键属性,通常被简称为 ACID 属性

  1. 原子性(Atomicity)
    • 事务是一个不可分割的工作单位,事务中包含的所有操作要么全部提交成功,要么全部回滚失败。任何一部分操作的失败都会导致整个事务的撤销。
  2. 一致性(Consistency)
    • 事务必须使数据库从一个一致状态转变到另一个一致状态。这意味着即使在系统崩溃或断电的情况下,事务也必须保证数据的完整性和约束条件不会被破坏。
  3. 隔离性(Isolation)
    • 多个并发事务之间的操作应该是隔离的,即一个事务的结果不应该影响其他正在运行的事务,除非它们已经提交。MySQL 提供了不同的隔离级别来实现不同程度的隔离效果。
  4. 持久性(Durability)
    • 一旦事务提交,它对数据库所做的更改就会永久保存下来,即使系统发生故障也不会丢失。

2.2 事务控制语句

在 MySQL 中,你可以使用以下 SQL 语句来控制事务:

  • START TRANSACTIONBEGIN
    • 开始一个新的事务。
  • COMMIT
    • 提交当前事务,使得所有更改成为永久性的。提交后不能回滚。
  • ROLLBACK
    • 回滚当前事务,撤销所有未提交的操作。这会将数据库恢复到事务开始之前的状态。

2.3 事务隔离级别

MySQL 支持四种标准的事务隔离级别,可以通过 SET TRANSACTION ISOLATION LEVEL 命令来设置:

  • 读未提交(READ UNCOMMITTED)
    • 最低的隔离级别,允许脏读、不可重复读和幻读。
  • 读已提交(READ COMMITTED)
    • 允许不可重复读和幻读,但不允许脏读。
  • 可重复读(REPEATABLE READ)
    • MySQL 的默认隔离级别,仅允许幻读,防止脏读和不可重复读。
  • 串行化(SERIALIZABLE)
    • 最高的隔离级别,完全串行化的读写操作,避免了脏读、不可重复读和幻读。

3 Spring声明式事务基本使用

3.1 事务管理概念

spring中支持两种事务管理,分别是编程式事务管理声明式事务管理两种.

  • 编程式事务管理概念

    • 编程式事务是指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。在 Java 中,通常使用事务管理器(如 Spring 中的 PlatformTransactionManager)来实现编程式事务。

    • 编程式事务的主要优点是灵活性高,可以按照自己的需求来控制事务的粒度、模式等等。但是,编写大量的事务控制代码容易出现问题,对代码的可读性和可维护性有一定影响。

    Connection conn = ...;
      
    try {
        // 开启事务:关闭事务的自动提交
        conn.setAutoCommit(false);
        // 核心操作
        // 业务代码
        // 提交事务
        conn.commit();
      
    }catch(Exception e){
      
        // 回滚事务
        conn.rollBack();
      
    }finally{
      
        // 释放数据库连接
        conn.close();
      
    }
    
    • 编程式的实现方式存在缺陷:
      • 细节没有被屏蔽:具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐。
      • 代码复用性不高:如果没有有效抽取出来,每次实现功能都需要自己编写代码,代码就没有得到复用。
      • 事务管理代码与核心业务代码相耦合
  • 声明式事务管理概念

    • 在 Spring 框架中,声明式事务管理是一种通过配置而非编程方式来管理事务的技术。它允许开发者以非侵入的方式定义方法的事务行为,而无需修改业务逻辑代码。Spring 的声明式事务管理主要依赖于 AOP(面向切面编程)来实现,并且可以通过 XML 配置或者基于注解的方式来进行配置。
    • 声明式事务管理的优势
      • 减少样板代码:减少了围绕事务控制所需的大量重复代码。
      • 提高代码可读性:将事务逻辑与业务逻辑分离,使代码更易于维护和理解。
      • 集中化管理:可以在一处集中管理事务规则,便于维护和调整。
  • 小结

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

3.2 声明式事务管理基本实现

  • SpringBoot环境中默认装配事务管理器,在service层使用@Transactional注解管理事务即可

  • 当然也可以手动装配事务管理器,具体代码如下:

  • 编写配置文件:jdbc.properties

    #配置DruidDataSource
    jdbc.driverClassName=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/demo
    jdbc.username=root
    jdbc.password=root
    
  • 编写属性类:JdbcProperties

    package com.mytest.properties;
    
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @Component("jdbcProperties")
    @ConfigurationProperties(prefix = "jdbc")
    @Data
    public class JdbcProperties {
        private String driverClassName;
        private String url;
        private String username;
        private String password;
    }
    
  • 编写配置类:DruidConfig

    package com.mytest.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.mytest.properties.JdbcProperties;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import javax.sql.DataSource;
    
    @Configuration
    public class DruidConfig {
    
        @Autowired
        @Qualifier("jdbcProperties")
        private JdbcProperties jdbcProperties;
    
        @Bean
        public DataSource dataSource(/*JdbcProperties jdbcProperties*/){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName(jdbcProperties.getDriverClassName());
            ds.setUrl(jdbcProperties.getUrl());
            ds.setUsername(jdbcProperties.getUsername());
            ds.setPassword(jdbcProperties.getPassword());
            return ds;
        }
        
        /**
         * 实例化JdbcTemplate对象,需要使用ioc中的DataSource
         * @param dataSource
         * @return
         */
        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource){
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            jdbcTemplate.setDataSource(dataSource);
            return jdbcTemplate;
        }
    
    }
    
  • 编写事务管理器配置类:TxConfig

    @Configuration
    @EnableTransactionManagement	//开启声明式事务管理
    public class TxConfig {
    
        /**
         * 装配事务管理实现对象
         * @param dataSource
         * @return
         */
        @Bean
        public TransactionManager transactionManager(DataSource dataSource){
            return new DataSourceTransactionManager(dataSource);
        }
    
    }
    
  • 整合DruidConfig与TxConfig

    @Import(value = {DruidConfig.class, TxConfig.class})
    @ComponentScan(basePackages = "com.mytest")
    @Configuration
    public class SpringConfig {
    
    }
    
  • 使用@Transactional注解实现:声明式事务管理

    • service层代码

      package com.mytest.service;
      
      import com.mytest.pojo.User;
      
      public interface UserService {
      
          //修改User信息
          public void updateUser(User user);
      
          //通过用户名修改密码
          public void updatePwdByUsername(String username, String password);
      
          //通过用户名修改昵称
          public void updateNicknameByUsername(String nickname, String username);
      
      }
      
      
      package com.mytest.service.impl;
      
      import com.mytest.dao.UserDao;
      import com.mytest.pojo.User;
      import com.mytest.service.UserService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Isolation;
      import org.springframework.transaction.annotation.Propagation;
      import org.springframework.transaction.annotation.Transactional;
      
      @Service
      //@Transactional
      public class UserServiceImpl implements UserService {
      
          @Autowired
          private UserDao userDao;
      
          /**
           * 1. 通过姓名修改密码; 2. 通过姓名修改昵称
          */
          @Transactional(
                  readOnly = false,
                  timeout = 3,
                  noRollbackFor = ArithmeticException.class,
                  isolation = Isolation.REPEATABLE_READ
          )
          @Override
          public void updateUser(User user) {
              //1. 通过姓名修改密码
              userDao.updatePwdByUsername(user.getAccount(),user.getPassword());
      
              //事务超时
      //        try {
      //            Thread.sleep(4000);
      //        } catch (InterruptedException e) {
      //            throw new RuntimeException(e);
      //        }
      
      //        bug
              int i = 1/0;
              //2. 通过姓名修改昵称
              userDao.updateNicknameByUsername(user.getNickname(),user.getAccount());
          }
      
          @Transactional(propagation=Propagation.REQUIRES_NEW)
          @Override
          public void updatePwdByUsername(String username, String password) {
              userDao.updatePwdByUsername(username, password);
          }
      
          @Transactional(propagation=Propagation.REQUIRES_NEW)
          @Override
          public void updateNicknameByUsername(String nickname, String username) {
              userDao.updateNicknameByUsername(nickname, username);
          }
      
      }
      
      package com.mytest.service.impl;
      
      import com.mytest.service.UserService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Propagation;
      import org.springframework.transaction.annotation.Transactional;
      
      @Service("userServiceAll")
      public class UserServiceAll {
      
          @Autowired
          private UserService userService;
      
          //测试事务传播行为(Propagation默认值:REQUIRED)
          @Transactional
          public void updateUserAll(){
              //1. 通过用户名修改密码
              userService.updatePwdByUsername("zhangsan", "666666");
      //        int i = 1/0;
              //2. 通过用户名修改昵称(普通员工)
              userService.updateNicknameByUsername("普通员工", "zhangsan");
          }
      
          //测试事务传播行为(Propagation默认值:REQUIRES_NEW)
          @Transactional(propagation = Propagation.REQUIRED)
          public void updateUserAllNew(){
              //1. 通过用户名修改密码
              userService.updatePwdByUsername("zhangsan", "666666");
      //        int i = 1/0;
              //2. 通过用户名修改昵称(普通员工)
              userService.updateNicknameByUsername("普通员工", "zhangsan");
          }
      
      }
      
    • 测试类代码

      package com.mytest;
      
      import com.mytest.pojo.User;
      import com.mytest.service.UserService;
      import com.mytest.service.impl.UserServiceAll;
      import org.junit.jupiter.api.Test;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.boot.test.context.SpringBootTest;
      
      @SpringBootTest
      public class TestUserService {
      
          @Autowired
          private UserService userService;
      
          @Autowired
          @Qualifier("userServiceAll")
          private UserServiceAll userServiceAll;
      
          //测试事务四个属性
          @Test
          public void testUpdateUser()
          {
              User user = new User();
              user.setAccount("zhangsan");
              user.setPassword("666666");
              user.setNickname("普通员工666");
              userService.updateUser(user);
      
          }
      
          //测试事务propagation属性(传播行为)
          @Test
          public void testPropagation() {
              //测试 REQUIRED
      //        userServiceAll.updateUserAll();
              System.out.println("userService.getClass().getName() = " + userService.getClass().getName());
              System.out.println("userServiceAll.getClass().getName() = " + userServiceAll.getClass().getName());
      
              //测试 REQUIRES_NEW
              userServiceAll.updateUserAllNew();
      
          }
      
      }
      
      

4 @Transactional注解(事务属性)

4.1 事务只读

  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 注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了 @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) {
        ……
        }
        
    }
    

4.2 事务超时

  1. 需求

    事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。

    此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。

    概括来说就是一句话:超时回滚,释放资源。

  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);
        }
    }
    
  3. 测试超时效果

    org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Wed May 24 09:10:43 IRKT 2023
    

4.3 事务回滚|不回滚异常

  1. 默认情况

    默认只针对运行时异常回滚,编译时异常不回滚。情景模拟代码如下:

  2. 设置回滚异常

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

4.4 事务隔离级别

  1. 事务隔离级别回滚

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

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

    package com.mytest.service;
    
    import com.mytest.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.mytest.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);
        }
    }
    
    

4.5 事务传播行为

  • 事务传播行为概念

    事务传播行为(Propagation Behavior)定义了当一个方法被调用时,如何与已有的事务进行交互。在 Spring 框架中,@Transactional 注解的 propagation 属性用于指定方法的事务传播行为。不同的传播行为决定了当前方法是否应该运行在一个新事务中、加入现有事务、或者以非事务方式运行等。

    举例说明:

    • 假设MethodA()方法的事务是tx1,MethodB()方法的事务tx2,MethodA()中调用MethodB()
    • 事务传播行为决定了MethodB()按照tx1或tx2事务执行
      • REQUIRED(默认值):tx1
      • REQUIRES_NEW:tx2
  • 案例代码

    @Transactional
    public void MethodA(){
        // ...
        MethodB();
        // ...
    }
    
    //在被调用的子方法中设置传播行为,代表如何处理调用的事务! 是加入,还是新事务等!
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void MethodB(){
        // ...
    }
    
  • propagation属性

  • propagation属性值

    spring中支持七种事务传播行为,常用两种:REQUIRED和REQUIRES_NEW

    1. REQUIRED(默认值)
      • 如果当前存在事务,则加入该事务;如果不存在,则创建一个新的事务。
    2. SUPPORTS
      • 如果当前存在事务,则加入该事务;如果没有事务,则以非事务方式执行。这意味着此方法对事务的存在与否不敏感。
    3. MANDATORY
      • 必须在一个已有事务中运行,否则抛出异常(IllegalTransactionStateException)。这通常用于必须确保在事务上下文中执行的方法。
    4. REQUIRES_NEW
      • 创建一个新的事务,如果当前存在事务,则挂起当前事务。这意味着即使有现有的事务,也会为这个方法启动一个全新的事务。
    5. NOT_SUPPORTED
      • 不支持事务,总是以非事务方式执行。如果有活动的事务,它将被暂停。
    6. NEVER
      • 方法不应该在事务中运行。如果尝试在事务中调用该方法,将会抛出异常。
    7. NESTED
      • 如果当前存在事务,则在嵌套事务内执行;如果不存在,则创建新的事务。嵌套事务可以独立于外部事务回滚,但它的提交依赖于外部事务的提交。
  • 测试事务传播行为

    package com.mytest.service.impl;
    
    import com.mytest.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service("userServiceAll")
    public class UserServiceAll {
    
        @Autowired
        private UserService userService;
    
        //测试事务传播行为(Propagation默认值:REQUIRED)
        @Transactional
        public void updateUserAll(){
            //1. 通过用户名修改密码
            userService.updatePwdByUsername("zhangsan", "666666");
    //        int i = 1/0;
            //2. 通过用户名修改昵称(普通员工)
            userService.updateNicknameByUsername("普通员工", "zhangsan");
        }
    
        //测试事务传播行为(Propagation默认值:REQUIRES_NEW)
        @Transactional(propagation = Propagation.REQUIRED)
        public void updateUserAllNew(){
            //1. 通过用户名修改密码
            userService.updatePwdByUsername("zhangsan", "666666");
    //        int i = 1/0;
            //2. 通过用户名修改昵称(普通员工)
            userService.updateNicknameByUsername("普通员工", "zhangsan");
        }
    }
    

spring 中配置数据源

  1. 导入jar包

  1. 配置

  2. 属性类(可选)

  1. 装配

  1. 装配事物管理器