Spring事务

目录

手动操作

 声明式提交

 注解的属性

 事务隔离级别

事务传播机制


事务可以将一组操作封装成为一个单元,一组操作要么全部成功,要么全部失败

Mysql中操作事务,有三个步骤;

1、start transaction ;开启事务

2、commit;提交事务

3、rollback;回滚事务

Spring操作事务,可以分为手动操作和声明式自动提交

手动操作

SpringBoot内置了两个对象,DataSourceTransactionManager用户获取事务、开启事务、,提交事务、回滚事务。TransactionDefinition是事务的属性


@Controller
@RequestMapping("/user")
public class InsertUserController {

    @Autowired
    InsertUserService insertUserService;

    //用于管理事务
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    private TransactionDefinition transactionDefinition;
    @RequestMapping("/insert")
    @ResponseBody
    public int insert(String name, String password) {

        //开启一个事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        //插入数据库
        int i = insertUserService.insert(name, password);
        //提交事务
        dataSourceTransactionManager.commit(transactionStatus);
        //回滚事务
        //  dataSourceTransactionManager.rollback(transactionStatus);
        return i;
    }
}

在启用事务提交后,插入数据成功

 如果启用事务回滚,可以看到返回的是1,但是数据库查找不到这个数据(因为事务回滚,删除了新增的数据)

 声明式提交

使用@Transactional注解声明方法,这个方法就可以自动开启事务,自动提交事务,遇到没有处理的异常,就可以自动回滚事务

@Transactional注解通过AOP实现,也就是基于动态代理实现。在执行业务代码之前,调用代理类的方法,开启事务,如果执行成功,提交事务,否则回滚事务

@Transactional可以用于修饰类或者方法

1、修饰方法时,只能用于public方法,否则不会生效

2、修饰类时,表示对注解类的所有public方法生效


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
      //  System.out.println(1 / 0);
        return i;
    }
}

在没有引入异常时,事务自动提交

 在引入异常之后,并且没有手动处理这个异常,那么会自动回滚事务

 调试发现,i=1,但是调试结束之后,发现数据没有插入,也就是发生了事务回滚

 如果异常被处理了,这时spring会认为我们会自己处理业务,执行回滚操作,所以不会进行事务回滚


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional()
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return i;
    }
}

插入成功

 这时,有两种解决办法,手动抛出异常,手动回滚事务

1、手动抛出异常


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional()
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            throw e;//手动抛出异常
        }
        return i;
    }
}

2、手动回滚事务


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody
    @Transactional()
    public int insert(String name, String password) {
        int i = insertUserService.insert(name, password);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            //手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

        }
        return i;
    }
}

 注解的属性

@Transactional 注解的关键属性大致有九个:

readOnly(事务读写性)

默认情况下是 false(即:不指定只读性),设置为 true 的含义是: 告诉程序该方法下使用的是只读操作,如果进行其他非读操作,则会抛出异常。

如果一次执行多次查询,这时为了保证数据整体的一致性,要用只读事务。

noRollbackFor 和 noRollbackForClassName(遇到时不回滚)
用来指明不回滚的条件是哪些异常类或者异常类名。

rollbackFor 和 rollbackForClassName(遇到时回滚)
用来指明回滚的条件是哪些异常类或者异常类名。

timeout(超时时间)
用于设置事务处理的时间长度,阻止可能出现的长时间的阻塞系统或者占用系统资源,单位为秒,超出设置时间,会抛出异常TransactionTimedOutException

默认值是-1,表示没有设置超时时间


value(指定使用的事务管理器)
value 主要用来指定不同的事务管理器,主要用来满足在同一个系统中,存在不同的事务管理器的场景需要。

 事务隔离级别

事务的隔离级别,可以让多个并发运行的事务更加稳定,减少并发问题

在mysql中,有四种隔离级别:读未提交,读已提交,可重复读,可串行化

而在spring中,有五种隔离级别,由isolation属性设置

Isolation.DEFAULT:使用连接数据库中默认的隔离级别【默认】
Isolation.READ_UNCOMMITTED:读取未提交数据
Isolation.READ_COMMITTED:读取已提交数据
Isolation.REPEATABLE_READ:可重复读
Isolation.SERIALIZABLE:串行化

 1、当Spring中设置的隔离级别和数据库的隔离级别有区别时,以Spring中设置的隔离级别为准

2、Spring中隔离级别的实现以连接的数据库的隔离级别的支持为基础

事务传播机制

事务传播机制表示了多个包含了事务的方法在相互调用时,事务在这些方法中如何传递

事务隔离级别用于保证多个事务并发执行的稳定性;事务传播机制用于保证一个事务在多个调用方法之间的稳定性

Spring中定义了“七个”表示隔离级别的值,由propagation属性设置

1、Propagation.REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入这个事务。是Spring默认的传播机制
2、Propagation.SUPPORTS:如果当前有事务,就以事务方式执行;如果当前没有事务,就以非事务方式执行
3、Propagation.MANDATORY:如果已经存在一个事务,加入这个事务。如果当前不存在事务,抛出异常
4、Propagation.REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
5、Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起,非事务方式的方法完毕,继续执行挂起的事务
6、Propagation.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
7、Propagation.NESTED:如果当前存在事务,则创建一个新的事务作为这个事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务

1、默认级别(Propagation.REQUIRED):如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入这个事务


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;
 
    @RequestMapping("/autoinsert")
    @ResponseBody
    
    public void insert(String name, String password) throws InterruptedException {

        //一个增加 user表的事务
       insertUserService.insert(name, password);
        

    }
}


@Service
public class InsertUserService {

    @Autowired
    InsertUserMapper insertUserMapper;
    @Autowired
    InsertLogService insertLogService;

    @Transactional()
    public int insert(@Param("user_name") String user_name, @Param("user_password") String user_password) {
        
        int i = insertUserMapper.insert(user_name, user_password);
        System.out.println("添加User表的数据的行数" + i);

        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(user_name, sdf.format(day));
        return i;
    }
}
@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional()
    public int insert(String name, String time) {

        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1 / 0);
        return i;
    }
}

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法-》调用log表的数据新增方法

因为采用默认的传播机制,InsertUserService类中创建一个事务之后,调用InsertLogService类的方法,在log表新增数据,此时不会创建新的事务,而是会进入之前InsertUserService类中创建的事务,但是InsertLogService类中方法在新增数据时报错,导致事务回滚,因为是同一个事务,所以两个表的数据都会被回滚

  查询表数据,数据被回滚

2、Propagation.SUPPORTS:如果当前有事务,就以事务方式执行;如果当前没有事务,就以非事务方式执行


@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.SUPPORTS)
    public int insert(String name, String time) {

        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1 / 0);
        return i;
    }
}


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertLogService insertLogService;

    @RequestMapping("/autoinsert")
    @ResponseBody

    public void insert(String name, String password) throws InterruptedException {

        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(name, sdf.format(day));

    }
}

程序执行的过程:调用InsertUserAutoController类-》调用log表的数据新增方法;

InsertLogService采用的传播机制是SUPPORTS,所以log表新增操作此时是以非事务的方式运行的,不会造成事务回滚

 3、Propagation.MANDATORY:如果已经存在一个事务,加入这个事务。如果当前不存在事务,抛出异常

 4、Propagation.REQUIRES_NEW:不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务


@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insert;
    @Autowired
    InsertLogService insertLogService;

    @RequestMapping("/autoinsert")
    @ResponseBody

    public void insert(String name, String password)  {

        //一个增加 user表的事务
        insert.insert(name, password);


        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(name, sdf.format(day));
    }
}

@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insert(String name, String time) {
        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1/0);
        return i;
    }
}
@Service
public class InsertUserService {

    @Autowired
    InsertUserMapper insertUserMapper;


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int insert(@Param("user_name") String user_name, @Param("user_password") String user_password) {

        int i = insertUserMapper.insert(user_name, user_password);
        System.out.println("添加User表的数据的行数" + i);
        return i;
    }
}

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法,调用log表的数据新增方法;

采用REQUIRES_NEW这个传播机制,user表新增会创建一个事务,在log表新增也会创建一个事务,此时,log表新增报错,事务回滚,但是user表不会事务回滚

 

 5、Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起

当InsertLogService采用NOT_SUPPORTED传播级别,而InsertUserService采用默认级别时

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法-》调用log表的数据新增方法;

采用NOT_SUPPORTED传播机制,InsertUserService中会创建一个事务,在InsertLogService会挂起存在的事务,InsertLogService的方法会以非事务方式运行,运行结束后恢复InsertUserService的事务

6、Propagation.NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
7、Propagation.NESTED:如果当前存在事务,则创建一个新的事务作为这个事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务


@Service
public class InsertUserService {

    @Autowired
    InsertUserMapper insertUserMapper;
    @Autowired
    InsertLogService insertLogService;

    @Transactional(propagation = Propagation.NESTED)
    public int insert(@Param("user_name") String user_name, @Param("user_password") String user_password) {

        int i = insertUserMapper.insert(user_name, user_password);
        System.out.println("添加User表的数据的行数" + i);

        java.util.Date day = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        //一个增加 日志表的事务
        insertLogService.insert(user_name, sdf.format(day));
        return i;
    }
}

@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int insert(String name, String time) {
        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        System.out.println(1/0);
        return i;
    }
}

@Controller
@RequestMapping("/user")
public class InsertUserAutoController {
    @Autowired
    InsertUserService insertUserService;

    @RequestMapping("/autoinsert")
    @ResponseBody

    public void insert(String name, String password)  {

        //一个增加 user表的事务
        insertUserService.insert(name, password);

    }
}

程序执行的过程:调用InsertUserAutoController类-》调用user表的数据新增方法-》调用log表的数据新增方法;

InsertUserService采用NESTED传播机制,创建了一个事务,在  InsertLogService类中使用 @Transactional()创建了一个事务,这个事务会作为InsertUserService开启的事务的嵌套事务来运行

当InsertLogService事务出现异常,事务回滚,这时,会继续向上查找调用InsertLogService方法的事务,对其进行回滚,所以,两个表都不会插入数据

 

REQUIRED和NESTED两种传播机制的区别:

NESTED:如果当前存在事务,则创建一个新的事务作为这个事务的嵌套事务来运行;如果当前没有事务,则创建一个新的事务

REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,加入这个事务

两个实际上都可以实现A,B两个事务的关联,只不过NESTED是A包含B,REQUIRED是AB一起

在NESTED中,在一个保存点,如果B事务手动回滚,会保留到B事务之前的状态,也就是不影响A事务。而REQUIRED没有这种机制。所以,NESTED可以实现部分事务回滚


@Service
public class InsertLogService {
    @Autowired
    InsertBlogMapper insertBlogMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int insert(String name, String time) {
        int i = insertBlogMapper.insert(name, time);
        System.out.println("添加日志表的数据的行数" + i);
        try {
            System.out.println(1 / 0);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return i;
    }
}

 insertUserService类和insertLogService类的事务传播级别都是NESTED,而insertLogService中实现了手动回滚,回到了保留点,不会影响insertUserService的事务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/4374.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot 整合Mybatis-Plus分页、自动填充功能

springboot 整合Mybatis-Plus分页、自动填充功能功能 此次分页、自动填充功能的实现是在Spring Boot整合 druid、Mybatis-plus实现的基础上完成的,包括数据源配置、各种依赖添加、mapper和service的实现。不在重复记录。 Java开发手册要求数据表必须要有三个字段&am…

【lwIP(第九章)】ICMP协议

目录一、ICMP协议简介1. ICMP协议类型与结构2. ICMP 差错报文3. ICMP 查询报文二、ICMP协议原理1. ICMP报文数据结构2. ICMP的差错报文3. 差错报文的原理4. ICMP的查询报文一、ICMP协议简介 ICMP协议是一个网络层协议。 一个新搭建好的网络,往往需要先进行一个简单的…

为什么说学人工智能一定要学Python?

学习人工智能需要掌握大量的数据处理和算法实现,而Python作为一种高级编程语言,具有简单易学、灵活多变、开源丰富的库等优点,成为了人工智能领域广泛应用的语言之一。 具体来说,Python在人工智能中的优势包括: ​​…

Matlab群体智能优化算法之巨型睡莲优化算法(VAO)

Matlab群体智能优化算法之巨型睡莲优化算法(VAO) 摘要:介绍一种新型智能优化算法,巨型睡莲优化算法。其应用于24个基准测试函数,并与其他10个著名算法进行了比较。提出的算法在10个优化问题上进行了测试:最小生成树、枢纽位置分配…

Nginx学习(9)—— 负载均衡模块

文章目录Nginx负载均衡模块负载均衡配置指令钩子初始化配置初始化请求peer.get和peer.free回调函数小结Nginx负载均衡模块 负载均衡模块用于从”upstream”指令定义的后端主机列表中选取一台主机。nginx先使用负载均衡模块找到一台主机,再使用upstream模块实现与这…

HTTP代理端口是什么意思?

HTTP代理端口是指代理服务器所使用的端口。代理服务器是一种介于客户端和服务器之间的计算机系统,它可以拦截客户端发送给服务器的请求,并将其转发到服务器。而HTTP代理端口则是代理服务器上专门用于处理HTTP请求和响应的端口号。默认情况下,…

【Java】JavaSE概要

整理:【狂神说Java】JavaSE阶段回顾总结_哔哩哔哩_bilibili JavaSE概要 简介 JDK:开发者工具包 JRE:运行环境 //Hello.java public class Hello{public static void main(String[] args){System.out.println("Hello,World!");} }…

Redis数据库

一、关系数据库与非关系型数据库概述 1、关系型数据库 关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。 SQL 语句(标准数据查询语言)就是一种基于关系型数据库的语…

Spring Boot基础学习之(六):前后端交互实现用户登录界面

本篇博客写的内容,是一个系列,内容都是关于spring boot架构的学习,实现前后端交互,极大的解放双手spring boot学习系列这是关于spring boot的专栏,后期也会不定期进行更新。内容都是有序号的,一步接着一步。…

有人物联口红DTU DR154配置与RS 485传感器数据处理

一、硬件设备 (1)有人物联口红DTU DR154(RS 485版本) 这个DTU非常给力,不用插卡自带esim卡,送8年流量,配置的话通过小程序【联博士】蓝牙配置(手机扫描DTU背后的二维码即可&#x…

界面开发框架Qt新手入门教程 - 项目视图示例介绍

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。Qt提供了许多功能&…

java基础问答

57、synchronized 各种加锁场景的作用范围 1.作用于非静态方法,锁住的是对象实例(this),每一个对象实例有一个锁。 public synchronized void method() {} 2.作用于静态方法,锁住的是类的Class对象,因为Cl…

chatgpt+安全机器人控制器+底盘一体化方案设计构想

“你有没有想过,你只需告诉你的家庭助理机器人:‘请加热我的午餐’,它就会自己找到微波炉。这是不是很神奇?” 近日,微软在其官网发表了一篇名为《机器人 ChatGPT:设计原则和模型能力(ChatGPT …

MongoDB 6.0 入门(一)

为什么研究MongDB 6.0 今天和老大聊天 聊到了一个场景的设计,我刚开始推荐了 clickhouse ,然后老大指出 前两天 测试的结果,因为clickhouse 因为 是列式存储,导致我们要查询一行数据,需要200ms(库中有2000…

MyBatis源码分析(二、续)SqlSource创建流程,SQL如何解析?如何将#{id}变成?的

文章目录实例一、SqlSource处理入口二、SqlSource处理逻辑1、XMLScriptBuilder 构造方法2、解析动态sql3、DynamicSqlSource4、RawSqlSource解析sql&#xff08;1&#xff09;parse方法解析sql写在后面实例 此处我们分析的sql&#xff1a; <select id"selectBlog&quo…

redis 十. 线程基础

目录一. redis 基础复习与了解redis6二. redis 线程问题总结一. redis 基础复习与了解redis6 redis官网, redis中文网站, redis命令参考网站此处以redis6.0.8或以上版本为例(查看自己redis版本命令"redis- server -v")按照redis6以上版本测试使用时,redis.conf下需要…

Baklib:企业知识管理帮助文档制作平台

在当今的商业环境中&#xff0c;企业面临着越来越多的挑战。其中之一是如何管理并传递企业内部的知识。企业知识管理的重要性不言而喻&#xff0c;它可以帮助企业更好地组织和利用内部的知识资源&#xff0c;提高生产力和竞争力。而Baklib作为一款企业知识管理&帮助文档制作…

新四级强化辅导

词汇题&#xff08;55道&#xff09; 1. You should carefully think over_____ the manager said at the meeting. A. that B. which C. what D. whose 1.选C,考察宾语从句连接词&#xff0c;主句谓语动词think over后面缺宾语&#xff0c;后面的宾语从句谓语动…

聚焦“专精特新” 共话高质量发展

3月23日&#xff0c;第七届杭州全球企业家论坛暨第三届中国专精特新企业&#xff08;新三板&#xff09;高峰论坛在杭州国博中心成功举办。超过500位专精特新企业代表、专家学者、政府部门代表、科研院所代表共聚一堂&#xff0c;现场座无虚席&#xff0c;气氛热烈。本届峰会以…

SQL Server 2016安装教程

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;欢迎订阅系列专栏&#xff1a;SQL Server 2016从入门到精通&#x1f947; &#x1f4…