事务及在SpringBoot项目中使用的两种方式

1.事务简介

事务(transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

事物的四大特性:

  1. 原子性(Atomicity)
    原子性指事务是一个不可分割的工作单位,事务中包括的操作要么全部完成,要么全部不完成,不可能结束在中间某个环节。事务中的所有操作要么全部提交成功,要么在发生错误时全部回滚,撤销对数据库的所有更改。这意味着事务内的操作如果失败了,那么会回滚到事务开始前的状态,就像这些操作从来没有发生过一样。
  2. 一致性(Consistency)
    一致性确保事务将数据库从一个一致的状态转变为另一个一致的状态。在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的任何数据都必须满足所有设置的规则,包括约束、触发器、级联更新等。例如,在转账事务中,无论进行多少次转账操作,所有用户的总金额应保持不变。
  3. 隔离性(Isolation)
    多个事务并发执行时,一个事务的操作不应影响其他事务。隔离性通过提供“单独”的环境来保证事务不受其他并发事务的影响。这意味着一个事务内部的操作及使用的数据对并发的其他事务是隔离的,反之亦然。这有助于防止数据在并发操作中产生不一致的问题。
  4. 持久性(Durability)
    一旦事务提交,则其结果永久保存在数据库中。即使系统崩溃、重启或发生故障,数据库还能恢复到事务成功结束时的状态。这意味着一旦事务被提交,它对数据库中数据的改变就应该是永久性的。

2.数据库中使用事务

需求:账号A向账号B转账,账户A更新余额后账户B也同时更新余额

初始数据:

操作语句:

update account set balance=1000 where account_number='A';

update account set balance=3000 where account_number='B';

操作结果:

问题:当在执行B失败时,数据库数据异常,如下:

问题:A更新数据,B数据却未更新。

解决方案:事务引入,将这两个操作作为一个事务,两个事务必须同时成功才能写入数据库。

#开启事物
start transaction ;
update account set balance=1000 where account_number='A';

update ;#抛出异常

update account set balance=3000 where account_number='B';
#提交事物,如果执行成功则同时写入数据库
commit ;
#回滚事物,如果执行失败则返回到数据库执行开始状态
ROLLBACK ;

执行结果:

3.事物并发问题

在事务处理、数据库操作或任何需要确保数据一致性和完整性的系统中,并发问题特别重要。当多个事务(或用户)同时尝试访问和修改相同的数据时,可能会出现以下问题:

  1. 脏读(Dirty Read):一个事务读取了另一个尚未提交的事务的数据。如果未提交的事务在之后被回滚,那么之前读取的数据将是无效的。
  2. 不可重复读(Non-repeatable Read):在同一个事务内,由于其他事务的修改,导致多次读取同一数据返回的结果有所不同。
  3. 幻读(Phantom Read):一个事务在读取某些行的集合时,另一个事务插入了新的行,导致前一个事务在再次读取相同的范围时看到额外的“幻影”行。
  4. 丢失更新(Lost Update):两个事务都读取了同一数据,然后都对其进行了修改,但第二个事务的修改覆盖了第一个事务的修改,导致第一个事务的修改丢失。

为了解决这些问题,数据库管理系统(DBMS)通常提供事务隔离级别来控制并发事务之间的交互。这些隔离级别包括:

  • 读未提交(Read Uncommitted):允许读取尚未提交的事务的数据。这是隔离级别最低的情况,可能导致上述所有并发问题。
  • 读已提交(Read Committed):只允许读取已提交的事务的数据。这可以防止脏读,但可能仍然会出现不可重复读和幻读。
  • 可重复读(Repeatable Read):确保在同一个事务内多次读取同一数据返回的结果是一致的。这可以防止脏读和不可重复读,但可能仍然会出现幻读。
  • 串行化(Serializable):这是最高的隔离级别,它强制事务序列化执行,从而防止上述所有并发问题。但这也可能导致性能下降,因为事务需要等待其他事务完成。

4.springboot项目中使用声明式事务

在Spring框架中,声明式事务管理是一种更简洁和声明性的方法来管理事务,通常是通过注解来实现的。声明式事务将事务管理的逻辑与业务逻辑分开,使开发者无需关心事务的底层细节。在Spring Boot中,声明式事务管理主要通过@Transactional注解来实现。

下面是如何在Spring Boot中使用声明式事务的步骤:

1.启用事务管理

确保在启动类或配置类上添加了@EnableTransactionManagement注解,以启用Spring的事务管理功能。


import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.transaction.annotation.EnableTransactionManagement;  
  
@SpringBootApplication  
@EnableTransactionManagement  
public class MyApplication {  
  
    public static void main(String[] args) {  
        SpringApplication.run(MyApplication.class, args);  
    }  
  
}
2.使用@Transactional注解

在需要事务管理的方法或类上使用@Transactional注解。这个注解告诉Spring该方法需要在事务的上下文中执行。


import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Service;  
import org.springframework.transaction.annotation.Transactional;  
  
@Service  
public class MyService {  
  
    // ... 依赖注入和数据访问层代码  
  
    @Transactional  
    public void performTransactionalTask() {  
        // 在这里执行你的业务逻辑  
        // 如果抛出运行时异常,事务会自动回滚  
    }  
}

@Transactional注解可以应用于方法或类级别。当应用于类级别时,它会影响类中的所有公共方法。

3.配置事务属性

@Transactional注解支持多种属性,用于定义事务的传播行为、隔离级别、超时和只读属性。


@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 30, readOnly = false)  
public void performTransactionalTask() {  
    // ...  
}
  • propagation:定义事务的传播行为,例如Propagation.REQUIRED表示当前方法必须在一个事务中运行,如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • isolation:定义事务的隔离级别,如Isolation.DEFAULT使用数据库默认的事务隔离级别。
  • timeout:定义事务的超时时间,单位是秒。
  • readOnly:指定事务是否只读。如果设置为true,则事务只读取数据而不修改数据。
4.异常处理

默认情况下,如果在事务方法内部抛出了运行时异常(RuntimeException),Spring会触发事务回滚。对于已检查的异常(checked exceptions),Spring不会触发回滚,除非明确指定了@Transactional注解的rollbackFor属性。

@Transactional(rollbackFor = Exception.class)  
public void performTransactionalTask() throws Exception {  
    // 如果抛出Exception或其子类,事务会回滚  
}
5.事务的传播行为(Propagation Behavior)

定义了当事务方法被另一个事务方法调用时,应如何使用事务。这主要涉及到如何处理嵌套事务的情况。Spring框架提供了多种传播行为选项,这些选项可以通过@Transactional注解的propagation属性来设置。以下是Spring框架中定义的事务传播行为:

  1. PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常见的选择,通常作为默认的事务传播行为。

  1. PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。这意味着方法可以在事务中运行,也可以不在事务中运行,取决于调用它的代码是否处于事务中。
  2. PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。这要求调用方法必须在一个事务中运行。
  3. PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果当前存在事务,则将其挂起。这意味着无论调用方法是否处于事务中,被调用方法都将在一个新的事务中运行。
  4. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则将其挂起。这意味着方法将不会在一个事务中运行,即使调用它的代码处于事务中。
  5. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。这要求调用方法不能在一个事务中运行。
  6. PROPAGATION_NESTED:如果当前存在事务,则嵌套事务作为当前事务的一个子事务运行;如果当前没有事务,则执行PROPAGATION_REQUIRED行为。这意味着如果在一个事务中调用该方法,那么该方法将在嵌套事务中运行,嵌套事务可以独立回滚而不影响外部事务。

6.事务的回滚

在Java中,使用JDBC或ORM框架(如Hibernate、MyBatis等)时,开发者可以通过调用相应的API来触发事务回滚。例如,在使用JDBC时,可以通过调用Connection对象的rollback()方法来回滚事务。而在使用Spring框架时,可以通过在方法上添加@Transactional注解,并指定rollbackFor属性来指定需要触发回滚的异常类型。

7.实例:

描述: 将用户预约id为1的车位预约信息存入数据库并更新该车位状态,在预约信息存入数据库后抛出错误,观察事务是否成功。

数据库初始状态:

关键代码

/**
     * 预约处理
     * @param model
     * @param request
     * @return
     */
    @PostMapping("/car/orderDetail")
    @Transactional(propagation = Propagation.REQUIRED)
    public String orderDetail(Model model,HttpServletRequest request){

        String username = request.getParameter("username");
        String tel = request.getParameter("tel");
        String parkId = request.getParameter("id");//车位编号
        String location = request.getParameter("location");
        String date = request.getParameter("date");
        String time = request.getParameter("time");
        String totalprice = request.getParameter("totalprice");
        String type = request.getParameter("type");
        String duration = request.getParameter("duration");
        OrderParking orderParking=new OrderParking();
        // 拼接日期和时间字符串
        String datetime_str = date + " " + time;
        // 定义日期时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        // 将拼接后的字符串转换为LocalDateTime对象
        LocalDateTime datetime_obj = LocalDateTime.parse(datetime_str, formatter);
        orderParking.setDate(datetime_obj);
        orderParking.setLocation(location);
        orderParking.setTel(tel);
        orderParking.setParkId(Integer.valueOf(parkId));
        orderParking.setTotalprice(Double.valueOf(totalprice));
        orderParking.setType(type);
        orderParking.setUsername(username);
        // 计算截止时间
        LocalDateTime endTime = datetime_obj.plusSeconds(Long.parseLong(duration)*3600L);
        orderParking.setDeadtime(endTime);
        orderParking.setDuration(Double.valueOf(duration));
        orderParking.setType(type);
        //判断是否可以预约
        int status1 = orderService.findStatus(Integer.parseInt(parkId));
        if(status1==1){
            model.addAttribute("msg", "该车位已预约");
            model.addAttribute("target", "http://"+address+":"+port+"/order/list");
            return "/operate-result";
        }

        //将预约表单存入数据库
        int i = orderService.addOrderParking(orderParking);
        //这里模拟异常
        String s=null;
        System.out.println(s.toString());

        //更新车位表状态
        int status = orderService.updateStatus(Integer.parseInt(parkId));
        if(status==1&&i==1){
            model.addAttribute("msg", "预约成功");
            model.addAttribute("target", "http://"+address+":"+port+"/order/list");
            return "/operate-result";
        }
        model.addAttribute("msg", "预约失败");
        model.addAttribute("target", "http://"+address+":"+port+"/order/list");
        return "/operate-result";
    }

执行结果

预约表中记录并为增添且车位状态并未更新(第二列为预约车位编号)

事务执行成功

5.编程式事务

编程式事务管理指的是在代码中显式地管理事务的边界和逻辑,而不是依赖于声明式的方式(如注解)。在编程式事务管理中,开发者需要手动开始、提交或回滚事务。Spring框架提供了TransactionTemplate和PlatformTransactionManager等API来支持编程式事务管理。

以下是使用编程式事务管理的基本步骤:

1.配置事务管理器

首先,你需要在Spring配置中定义一个PlatformTransactionManager的bean。这通常是基于你使用的数据源类型(如JDBC、Hibernate、JPA等)来决定的。

例如,对于JDBC,你可以这样配置一个DataSourceTransactionManager:

2.使用TransactionTemplate

TransactionTemplate是Spring提供的一个帮助类,它简化了编程式事务的使用。你可以创建一个TransactionTemplate的bean,并注入你的事务管理器。然后,你可以在你的服务代码中使用这个TransactionTemplate来执行需要事务管理的操作。

这里是转账的逻辑

@Autowired
OrderMapper orderMapper;
public void update(String A,int aBalance,String B,int bBalance){
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            try {
                // 在这里执行你的业务逻辑
                orderMapper.updateAccount(A,aBalance);
                String s=null;
                System.out.println(s.toString());
                orderMapper.updateAccount(B, bBalance);
                // 如果抛出运行时异常,事务会自动回滚
            } catch (Exception e) {
                // 可以选择手动回滚
                status.setRollbackOnly();
                throw e;
            }
        }
    });

}
3.手动管理事务

除了使用TransactionTemplate,你还可以直接使用PlatformTransactionManager来手动开始、提交和回滚事务。这通常是通过TransactionStatus对象来完成的。


import org.springframework.transaction.support.TransactionCallback;  
import org.springframework.transaction.support.TransactionTemplate;  
import org.springframework.transaction.TransactionStatus;  
import org.springframework.transaction.PlatformTransactionManager;  
  
@Service  
public class MyService {  
  
    private final PlatformTransactionManager transactionManager;  
  
    public MyService(PlatformTransactionManager transactionManager) {  
        this.transactionManager = transactionManager;  
    }  
  
    public void performManualTransactionalTask() {  
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());  
        try {  
            // 在这里执行你的业务逻辑  
            // 如果抛出运行时异常,事务会自动回滚  
  
            transactionManager.commit(status);  
        } catch (Exception e) {  
            // 发生异常,手动回滚事务  
            transactionManager.rollback(status);  
            throw e;  
        }  
    }  
}

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

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

相关文章

【智能家居入门4】(FreeRTOS、MQTT服务器、MQTT协议、微信小程序)

前面已经发了智能家居入门的1、2、3了,在实际开发中一般都会使用到实时操作系统,这里就以FreeRTOS为例子,使用标准库。记录由裸机转到实时操作系统所遇到的问题以及总体流程。相较于裸机,系统实时性强了很多,小程序下发…

相机图像质量研究(17)常见问题总结:CMOS期间对成像的影响--靶面尺寸

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结:光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结:光学结构对成…

用EL操作JAVABEAN属性

用EL操作JAVABEAN属性 问题陈述 Smart SoftWare Inc.想要开发一款维护雇员数据(例如姓、名字、职位)的Web应用程序。该组织决定将雇员数据存储在一个JavaBean中。另外,它还希望该Web应用程序能让用户从此JavaBean中检索数据并用JSP页面和EL显示。 解决方案 要解决上述问题…

.net和jar包windows服务部署

一.NetCore 1.创建启动脚本run_instal.bat,例如程序文件为ApiDoc.exe set serviceName"Apidoc Web 01" set serviceFilePath%~dp0ApiDoc.exe set serviceDescription"ApiDoc 动态接口服务 web 01"sc create %serviceName% BinPath%serviceFilePath% sc c…

【JavaEE】_文件与IO

目录 1.文件概述 1.1 文件的概念 1.2 文件的存储 1.3 文件的分类 1.4 目录结构 1.5 文件操作 1.5.1 文件系统操作 1.5.2 文件内容操作 2. Java文件系统操作 2.1 File类所处的包 2.2 构造方法 2.3 方法 2.3.1 与文件路径、文件名有关的方法 2.3.2 文件是否存在与普…

新时代异步 IO 框架:IO_URING 的原理、用法、业界示例分析

文章目录 IO_URING基本介绍常见 I/O 模型IO_URING 原理核心结构工作模式高级特性 用法APIliburing基本流程Demo 业界示例SeaStar / ScyllaDBCEPHRocksDBClickHouse IO_URING 基本介绍 常见 I/O 模型 当前 Linux 的几种 I/O 模型: I/O 模型 同步 I/O 是目前应用最…

2024.2.10 HCIA - Big Data笔记

1. 大数据发展趋势与鲲鹏大数据大数据时代大数据的应用领域企业所面临的挑战和机遇华为鲲鹏解决方案2. HDFS分布式文件系统和ZooKeeperHDFS分布式文件系统HDFS概述HDFS相关概念HDFS体系架构HDFS关键特性HDFS数据读写流程ZooKeeper分布式协调服务ZooKeeper概述ZooKeeper体系结构…

【C++】C++11上

C11上 1.C11简介2.统一的列表初始化2.1 {} 初始化2.2 initializer_list 3.变量类型推导3.1auto3.2decltype3.3nullptr 4.范围for循环5.final与override6.智能指针7. STL中一些变化8.右值引用和移动语义8.1左值引用和右值引用8.2左值引用与右值引用比较8.3右值引用使用场景和意义…

GPU独显下ubuntu屏幕亮度不能调节解决方法

GPU独显下屏幕亮度不能调节(假设你已经安装了合适的nvidia显卡驱动),我试过修改 /etc/default/grub 的 GRUB_CMDLINE_LINUX_DEFAULT"quiet splash acpi_backlightvendor" ,没用。修改和xorg.conf相关的文件,…

【python】类创建、实例化和调用类方法、子类、继承、私有属性、静态方法

一、类属性:定义在类中函数外的属性;self代表类的实例。 其中 number属于类属性,name、age属于实例属性;实例属性一般在初始函数中定义。 class people:number300 #类属性def __init__(self,name,age):#初始化方法 左右两个下划线self.nam…

就是民族的气节

我们拥有一个名字叫中国 - 张明敏 一把黄土塑成千万个你我 静脉是长城 动脉是黄河五千年的文化是生生不息的脉搏(齐楚燕韩赵魏秦)提醒你 提醒我我们拥有个名字叫中国(中原地区为主体,河南,山东,安徽&…

语言与真实世界的关系(超级语言生成能力将促进世界深刻变化)

语言与真实世界之间存在着紧密且复杂的关系。在人类社会中,语言是认知、表达和交流现实世界的主要工具,它帮助我们构建并理解周围环境,并将我们的思维和经验概念化。 1. 符号与指代: 语言是一种符号系统,通过词汇、句…

【精选】Java面向对象进阶——接口

🍬 博主介绍👨‍🎓 博主介绍:大家好,我是 hacker-routing ,很高兴认识大家~ ✨主攻领域:【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 🎉点赞➕评论➕收藏 …

(11)Hive调优——explain执行计划

一、explain查询计划概述 explain将Hive SQL 语句的实现步骤、依赖关系进行解析,帮助用户理解一条HQL 语句在底层是如何实现数据的查询及处理,通过分析执行计划来达到Hive 调优,数据倾斜排查等目的。 官网指路: https://cwiki.ap…

第14讲投票帖子详情实现

投票帖子详情实现 后端,根据id查询投票帖子信息: /*** 根据id查询* param id* return*/ GetMapping("/{id}") public R findById(PathVariable(value "id")Integer id){Vote vote voteService.getById(id);WxUserInfo wxUserInfo wxUserInf…

【Go语言】第一个Go程序

第一个 Go 程序 1 安装 Go Go语言官网:Download and install - The Go Programming Language,提供了安装包以及引导流程。 以 Windows 为例,进入windows安装包下载地址:All releases - The Go Programming Language&#xff0c…

BDD - Python Behave 用户自定义配置文件

BDD - Python Behave 用户自定义配置文件 引言默认 behave.ini 配置文件自定义配置文件json 格式的配置文件ini 格式的配置文件 实例应用项目结构代码BDD/Features/user_data.feature 文件BDD/steps/user_data_steps.py 文件BDD/environment.py 文件默认配置文件 behave.ini自定…

构建智慧交通平台:架构设计与实现

随着城市交通的不断发展和智能化技术的迅速进步,智慧交通平台作为提升城市交通管理效率和水平的重要手段备受关注。本文将探讨如何设计和实现智慧交通平台的系统架构,以应对日益增长的城市交通需求,并提高交通管理的智能化水平。 ### 1. 智慧…

Spring 用法学习总结(二)之基于注解注入属性

Spring学习 5 基于注解方式创建对象6 基于注解注入属性 5 基于注解方式创建对象 注解是代码的特殊标记,可以简化xml配置,格式:注解名称(属性名称属性值),可以作用在类、方法、属性上 以下注解都可以创建bean实例 Com…

【Qt】qt常用控件之QIcon 以及 qrc机制设置图片路径(QtCreator)

文章目录 1. QIcon / windowIcon2. setIcon() 与 setwindowIcon()2.1 setIcon() 介绍与使用2.2 setWindowIcon 介绍与使用 3. 路径问题 & qrc机制的引入3.1 绝对路径 / 相对路径 的问题3.2 qrc机制3.3 在QtCreator下利用qrc机制引入图片 1. QIcon / windowIcon QIcon QIco…