学习笔记-Spring事务

这篇具有很好参考价值的文章主要介绍了学习笔记-Spring事务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学习的文章

小姐姐非要问我:spring编程式事务是啥? (qq.com)

一文搞懂什么是事务 - 知乎 (zhihu.com)

阿里3面:Spring声明式事务连环炮,让我措手不及。。 (qq.com)

带你读懂Spring 事务——事务的传播机制 - 知乎 (zhihu.com)

spring 事务失效的 12 种场景_事务什么时候失效_hanjq_code的博客-CSDN博客

什么是事务

  • 事务是并发操作的单位

  • 是用户定义的操作序列

  • 事务如果成功,则会提交

  • 事务如果失败,则会回滚

事务的四大特性

  • 原子性

    • 事务中操作,要么不做,要么都做
  • 持久性

    • 一个事务一旦提交,它对数据库的改变是永久的
  • 一致性

    • 事务让数据库从一个一致性状态转移到另一个一致性状态

    • 比如

      • 事务前,A有50,B有50,总共有100

      • 事务中,A送给B20

      • 事务后,A有30,B有70,总共还是有100

  • 隔离性

    • 一个事务的执行不能被其他事务所干扰

    • 分成不同的等级

事务并发访问导致的数据问题

脏读

读到了修改但还没有提交的数据

  • A事务修改了某条记录的字段c,但还没有提交

  • B事务在此时读取了字段c

  • A事务发生了回滚,字段c恢复了修改前的状态

  • 但B事务持有的还是修改后的状态

不可重复读

某个事务在执行过程中,两次读同一个条记录,但结果不一样

  • A事务读取了记录c,值为10

  • B事务修改了记录c,值为0

  • A事务再次读取记录c,值为0

幻读

某个事务在执行过程中,前后两次读取记录,数据总量不一样

  • A事务统计了表c中的记录总数,结果为10

  • B事务删除了表c中的5条记录

  • A事务再次统计了表c中的记录总数,结果为5

和不可重复读的区别在于,幻读针对的是记录条数,不可重复读针对的是记录内容

事务的隔离级别

针对事务并发访问时出现的问题,设置了四种事务的隔离级别

读未提交

可以的读取还没有提交的数据

没有限制,三种问题都有可能发生

读已提交

只能读取已经提交了的数据

不会发生脏读,但是会发生不可重复读和幻读

可重复读

一个事务前后读取的同一条记录的结果必须一致

不会发生脏读和不可重复读,但会发生幻读

mysql中默认的事务隔离级别

串行化

所有的事务必须依次执行

不会发生脏读、不可重读读和幻读

效率比较低

Spring事务的使用方法

Spring分为两种控制事务的方法

  • 编程式事务

    • 方法1:通过PlatformTransactionManager控制事务

    • 方法2:通过TransactionTemplate控制事务

  • 声明式事务

    • 常用

编程式事务的使用

使用PlatformTransactionManager

@Test
public void test1() throws Exception {
    //定义一个数据源
    org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
    dataSource.setUsername("root");
    dataSource.setPassword("root123");
    dataSource.setInitialSize(5);
    //定义一个JdbcTemplate,用来方便执行数据库增删改查
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    //1.定义事务管理器,给其指定一个数据源(可以把事务管理器想象为一个人,这个人来负责事务的控制操作)
    PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
    //2.定义事务属性:TransactionDefinition,TransactionDefinition可以用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。
    TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    //3.开启事务:调用platformTransactionManager.getTransaction开启事务操作,得到事务状态(TransactionStatus)对象
    TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
    //4.执行业务操作,下面就执行2个插入操作
    try {
        System.out.println("before:" + jdbcTemplate.queryForList("SELECT * from t_user"));
        jdbcTemplate.update("insert into t_user (name) values (?)", "test1-1");
        jdbcTemplate.update("insert into t_user (name) values (?)", "test1-2");
        //5.提交事务:platformTransactionManager.commit
        platformTransactionManager.commit(transactionStatus);
    } catch (Exception e) {
        //6.回滚事务:platformTransactionManager.rollback
        platformTransactionManager.rollback(transactionStatus);
    }
    System.out.println("after:" + jdbcTemplate.queryForList("SELECT * from t_user"));
}

使用TransactionTemplate

@Test
public void test1() throws Exception {
    //定义一个数据源
    org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8");
    dataSource.setUsername("root");
    dataSource.setPassword("root123");
    dataSource.setInitialSize(5);
    //定义一个JdbcTemplate,用来方便执行数据库增删改查
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    //1.定义事务管理器,给其指定一个数据源(可以把事务管理器想象为一个人,这个人来负责事务的控制操作)
    PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
    //2.定义事务属性:TransactionDefinition,TransactionDefinition可以用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。
    DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    transactionDefinition.setTimeout(10);//如:设置超时时间10s
    //3.创建TransactionTemplate对象
    TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager, transactionDefinition);
    /**
     * 4.通过TransactionTemplate提供的方法执行业务操作
     * 主要有2个方法:
     * (1).executeWithoutResult(Consumer<TransactionStatus> action):没有返回值的,需传递一个Consumer对象,在accept方法中做业务操作
     * (2).<T> T execute(TransactionCallback<T> action):有返回值的,需要传递一个TransactionCallback对象,在doInTransaction方法中做业务操作
     * 调用execute方法或者executeWithoutResult方法执行完毕之后,事务管理器会自动提交事务或者回滚事务。
     * 那么什么时候事务会回滚,有2种方式:
     * (1)transactionStatus.setRollbackOnly();将事务状态标注为回滚状态
     * (2)execute方法或者executeWithoutResult方法内部抛出异常
     * 什么时候事务会提交?
     * 方法没有异常 && 未调用过transactionStatus.setRollbackOnly();
     */
    transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
        @Override
        public void accept(TransactionStatus transactionStatus) {
            jdbcTemplate.update("insert into t_user (name) values (?)", "transactionTemplate-1");
            jdbcTemplate.update("insert into t_user (name) values (?)", "transactionTemplate-2");

        }
    });
    System.out.println("after:" + jdbcTemplate.queryForList("SELECT * from t_user"));
}

声明式事务的使用

  • 在配置类上使用@EnableTransactionManagement

    • Springboot可以加在启动类上
  • 定义事务管理器

    • @Bean
      public PlatformTransactionManager transactionManager(DataSource dataSource) {
          return new DataSourceTransactionManager(dataSource);
      }
      
    • springboot中有默认的事务管理器

  • 在需要事务的目标上加上@Transactional注解

    • 作用位置

      • 注意:@Transactional只对public方法有效

      • @Transactional放在接口上,接口的所有实现类中所有的public方法都自动加上事务

      • @Transactional放在类上,当前类以及其下无限级子类中的public方法都被加上事务

      • @Transactional放在public方法上,方法被加上事务

    • 属性

      • "transactionManager"或"value"

        • 指定事务管理器的bean对象

        • 为空的话,默认按类型获取

      • “propagation”

        • 指定事务的传播类型

        • 默认为REQUIRED

      • “rollbackFor”

        • 自定义回滚异常
  • 什么时候回滚

    • 事务内部抛出异常时,事务会回滚
    • 标记为回滚:如果在事务内部调用了 setRollbackOnly() 方法将事务标记为回滚状态,事务会回滚
    • 配置回滚规则:通过配置事务的回滚规则,可以根据特定的异常类型来触发事务回滚。例如,可以配置当出现特定的异常类型时触发事务回滚

事务的传播类型

  • REQUIRED

    • 如果当前有事务,则加入当前事务

    • 如果当前没有事务,则自己新建一个事务

  • SUPPORTS

    • 如果当前有事务,则加入当前事务

    • 如果当前没有事务,则以非事务方式执行

  • MANDATORY

    • 如果当前有事务,则加入当前事务

    • 如果当前没有事务,则抛出异常

  • REQUIERES_NEW

    • 如果当前有事务,则将该事务挂起,另外新创建一个事务

    • 如果当前没有事务,则新创建一个事务

  • NOT_SUPPORTED

    • 如果当前有事务,则将该事务挂起,以非事务方式执行

    • 如果当前没有事务,则以非事务方式执行

  • NEVER

    • 如果当前有事务,则抛出异常

    • 如果当前没有事务,以非事务方式执行

  • NESTED

    • 如果当前有事务,则在当前事务中嵌套一个事务执行

    • 如果当前没有事务,则新建一个事务执行

事务失效或回滚异常的12种情况

事务失效

  • 访问权限问题

    • 事务只能对public的方法方法生效
  • 方法用final或static修饰

    • spring事务是使用动态代理的方式实现的

    • 如果加了final或static方法,则方法无法被代理

  • 方法内部调用

    • @Service
      public class UserService {
       
          @Autowired
          private UserMapper userMapper;
       
        
          public void add(UserModel userModel) {
              userMapper.insertUser(userModel);
              updateStatus(userModel);
          }
       
          @Transactional
          public void updateStatus(UserModel userModel) {
              doSameThing();
          }
      }
      
    • 在add方法中是通过this来调用updateStatus方法的,没有通过代理

    • 解决方法:

      • 1.注入自己

        • @Servcie
          public class ServiceA {
             @Autowired
             prvate ServiceA serviceA;
           
             public void save(User user) {
                   queryData1();
                   queryData2();
                   serviceA.doSave(user);
             }
           
             @Transactional(rollbackFor=Exception.class)
             public void doSave(User user) {
                 addData1();
                 updateData2();
              }
           }
          
      • 2.通过AopContext.currentProxy()获取代理对象

        • @Servcie
          public class ServiceA {
           
             public void save(User user) {
                   queryData1();
                   queryData2();
                   ((ServiceA)AopContext.currentProxy()).doSave(user);
             }
           
             @Transactional(rollbackFor=Exception.class)
             public void doSave(User user) {
                 addData1();
                 updateData2();
              }
           }
          
  • 未被spring管理

    • 忘了给类添加注释了

    • 没有被放到IOC容器中

  • 多线程调用

    • spring事务是通过数据库连接来实现的

    • 在多线程中,每一个线程用的都是不同的数据库连接

  • 表不支持事务

    • MyISAM引擎的表不支持事务
  • 未开启事务

    • springboot需要在启动类上加上@EnableTransactionManagement的注解

    • 传统spring项目需要在applicationContext.xml中进行相关的配置

事务回滚异常

  • 使用了错误的传播特性

  • 手动捕获了异常

    • 如果想要spring事务能够正常回滚,必须抛出它能够处理的异常

    • 使用try/catch将异常捕获,将导致事务不会回滚

  • 抛得异常类型不正确

    • spring事务,默认情况下只会回滚RuntimeException或Error
  • 自定义回滚异常不匹配

    • 比如定义了BusinessException,但抛出的是SqlException
  • 嵌套事务回滚范围多了

    • public class UserService {
       
          @Autowired
          private UserMapper userMapper;
       
          @Autowired
          private RoleService roleService;
       
          @Transactional
          public void add(UserModel userModel) throws Exception {
              userMapper.insertUser(userModel);
              roleService.doOtherThing();
          }
      }
       
      @Service
      public class RoleService {
       
          @Transactional(propagation = Propagation.NESTED)
          public void doOtherThing() {
              System.out.println("保存role表数据");
          }
      }
      
    • roleService.doOtherThing()如果发生回滚,它抛出的异常没有被处理,会继续往上级抛

    • add()在捕获到向上抛的异常后也会发生回滚

    • 应该手动进行捕获文章来源地址https://www.toymoban.com/news/detail-455915.html

      • @Slf4j
        @Service
        public class UserService {
         
            @Autowired
            private UserMapper userMapper;
         
            @Autowired
            private RoleService roleService;
         
            @Transactional
            public void add(UserModel userModel) throws Exception {
         
                userMapper.insertUser(userModel);
                try {
                    roleService.doOtherThing();
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        

到了这里,关于学习笔记-Spring事务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot 笔记 026 文章分类列表查询

    1.1 pinia 1.1.1 安装pinia npm install pinia 1.1.2 安装persist npm install pinia-persistedstate-plugin 1.1.2 在vue应用实例中使用pinia,在pinia中使用persist 1.1.3 在src/stores/token.js中定义store,定义状态Store时指定持久化配置参数 1.1.4 在组件中使用store 1.1.4.1 在login.vue中使用store将token存储到pinia中  

    2024年02月20日
    浏览(44)
  • SSM框架学习-Spring事务

    数据库事务和Spring中的事务是两个不同的概念,但Spring中的事务是建立在数据库事务之上的。 数据库事务是指一组数据库操作,要么全部成功执行,要么全部回滚(撤销)。这么做是为了保证数据库的一致性和完整性。在数据库操作过程中,如果其中任意一步操作失败,整个

    2024年02月05日
    浏览(54)
  • Spring Boot 笔记 017 创建接口_新增文章

    1.1实体类增加校验注释 1.1.1 自定义校验 1.1.1.1 自定义注解 1.1.1.2 自定义校验类 1.1.1.3 在需要的地方使用注解 1.1 Controller 1.2 Service 1.3 ServiceImpl 1.4 Mapper 1.5 Postman测试

    2024年02月20日
    浏览(38)
  • Spring Boot 笔记 012 创建接口_添加文章分类

    1.1.1 实体类添加校验 1.1.2 Controller 1.1.3 Service 1.1.4 ServiceImpl 1.1.5 mapper 1.1.6 测试

    2024年02月19日
    浏览(37)
  • Spring Boot 笔记 015 创建接口_更新文章分类

    1.1.1 实体类id增加NotNull注释,并做分组校验 1.1.1.1 定义分组 1.1.1.2 实体类中指定校验项属于哪个分组 1.1.1.3 Controller中指定校验分组 1.1.2 Service 1.1.3 ServiceImpl 1.1.4 Mapper 1.1.5 postman测试

    2024年02月22日
    浏览(43)
  • SSM框架学习-Spring事务管理入门

    为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。 (博客的参考源码可以在我主页的资源里找到,如果在学习的

    2024年02月05日
    浏览(45)
  • Spring5学习随笔-事务属性详解(@Transactional)

    学习视频:【孙哥说Spring5:从设计模式到基本应用到应用级底层分析,一次深入浅出的Spring全探索。学不会Spring?只因你未遇见孙哥】 事务是 保证业务操作完整性的一种数据库机制 事务的4特点:ACID A 原子性 C 一致性 I 隔离性 D 持久性 JDBC: Connection.setAutoCommit(false) Connect

    2024年02月05日
    浏览(40)
  • MySql学习笔记08——事务介绍

    事务是一个完整的业务逻辑,是一个最小的工作单元,不可再分。 一个完整的业务逻辑包括一系列的操作,这些操作是整个业务逻辑中的最小单元,这些操作要么同时成功,要么同时失败。 由于只有DML语句中才会有事务的概念,因此事务只和 insert update delete 语句有关。 说到

    2024年02月09日
    浏览(40)
  • 【Java学习】 Spring的基础理解 IOC、AOP以及事务

        官网: https://spring.io/projects/spring-framework#overview     官方下载工具: https://repo.spring.io/release/org/springframework/spring/     github下载: https://github.com/spring-projects/spring-framework     maven依赖: 1.spring全家桶的结构构图:              最下边的是测试单元   其中spring封装

    2024年02月09日
    浏览(43)
  • 一篇文章带你搞懂spring6的概念、spring入门与容器IoC详解(尚硅谷笔记)

    Spring 是一款主流的 Java EE 轻量级开源框架 ,Spring 由“Spring 之父”Rod Johnson 提出并创立,其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring 框架

    2023年04月16日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包