Spring 事务管理 04

server/2024/9/20 3:53:18/ 标签: spring, oracle, 数据库

文章目录

    • 为什么需要事务?
    • 1. 声明式事务
    • 2. 编程式事务管理
    • 3. 事务的实现原理
      • 3.1 事务管理器(Transaction Manager):
      • 3.2 事务管理的抽象层:
      • 3.3 事务传播行为(Propagation Behavior):
      • 3.4 事务隔离级别(Isolation Level):
      • 3.5 事务的切点(Pointcut)和通知(Advice):
      • 3.6 异常处理和事务回滚:

为什么需要事务?

事务(Transaction)是数据库管理系统(DBMS)中的一个重要概念,它确保数据库操作的一致性完整性持久性。在软件开发中,特别是涉及到多个数据库操作或者复杂的数据更新过程时,使用事务能够有效地保证数据操作的正确性和可靠性。以下是几个需要事务的主要原因:

保证数据的一致性:

当多个操作必须要一起执行,要么全部成功,要么全部失败时,就需要使用事务。例如,转账操作中,从一个账户扣除金额和向另一个账户增加金额必须作为一个原子操作,否则可能导致数据不一致。

确保数据的完整性:

数据库设计中通常会定义各种约束条件(如主键、唯一性约束、外键约束等),事务能够保证在进行数据操作时,这些约束条件不会被破坏。如果事务执行失败,数据库会自动回滚到事务开始之前的状态,确保数据完整性。

支持并发控制:

当多个用户或者应用程序同时对数据库进行读写操作时,可能会引发并发问题,如脏读(dirty
read)、不可重复读(non-repeatable read)和幻读(phantom
read)。事务使用隔离级别(如读未提交、读已提交、可重复读、串行化)来解决这些并发问题,确保数据的一致性和隔离性。

提供数据持久性:

当事务成功提交后,数据库会将事务所做的所有更改永久保存到磁盘,即使发生系统崩溃或者断电等异常情况,数据也不会丢失。事务的持久性保证了数据的安全性和可靠性。

简化错误处理:

使用事务可以减少因为程序错误、系统故障或者其他异常情况而导致的数据不一致问题。如果事务执行过程中发生异常,数据库会回滚事务,放弃所有未提交的操作,保证系统处于一致的状态。

事务管理是软件开发中非常重要的概念之一,它确保了在复杂的数据操作过程中,数据的一致性和完整性得到有效地维护。在 Spring 框架中,事务管理是通过 Spring 的事务抽象层来实现的,它提供了声明式事务管理编程式事务管理两种主要方式。

1. 声明式事务

声明式事务管理是通过配置的方式来实现事务的控制,主要依赖于 AOP 和事务管理器来实现。在 Spring 中,使用 @Transactional 注解来标注需要事务管理的方法或类,Spring 容器会自动为这些方法创建事务代理,以确保方法在执行时事务行为得到正确应用。

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserService {@Transactionalpublic void saveUser(User user) {// 保存用户操作userRepository.save(user);}@Transactional(readOnly = true)public User findUserById(Long userId) {// 查询用户操作return userRepository.findById(userId).orElse(null);}
}

在上述示例中,@Transactional 注解被用来标注 saveUser 和 findUserById 方法,指示这些方法应该在事务控制下执行。在 saveUser 方法中,默认的事务传播行为和隔离级别会被应用,确保在方法执行时会开启事务并在方法结束时提交或回滚事务。

@Transactional 的常用属性:

  1. readOnly:指定事务是否为只读事务,默认为 false。
  2. propagation:指定事务的传播行为,
  3. 例如 Propagation.REQUIRED 表示如果当前没有事务,就创建一个新的事务
  4. Propagation.REQUIRES_NEW 表示每次都会创建一个新的事务。
  5. isolation:指定事务的隔离级别,例如 Isolation.DEFAULT、Isolation.READ_COMMITTED 等。
  6. timeout:指定事务的超时时间,单位为秒。
  7. rollbackFor / noRollbackFor:指定哪些异常需要回滚事务或者不回滚事务。

2. 编程式事务管理

编程式事务管理是通过编写代码显式地控制事务的开始、提交和回滚,虽然它更加灵活,但通常比较繁琐并且与业务逻辑混合在一起,不利于代码的维护和扩展,因此在 Spring 中不推荐使用。

import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;import javax.annotation.Resource;@Service
public class UserService {@Resourceprivate PlatformTransactionManager transactionManager;public void saveUser(User user) {DefaultTransactionDefinition def = new DefaultTransactionDefinition();TransactionStatus status = transactionManager.getTransaction(def);try {// 执行保存用户操作userRepository.save(user);transactionManager.commit(status);} catch (Exception e) {transactionManager.rollback(status);throw e;}}
}

在上述编程式事务管理的示例中,通过 PlatformTransactionManager 和 TransactionStatus 对象来手动管理事务的开始、提交和回滚。这种方式需要开发者显式地编写事务管理的逻辑,包括异常处理和事务状态的管理,相比声明式事务管理更为复杂。

3. 事务的实现原理

在 Spring 框架中,事务的实现原理主要基于 Spring 的事务管理抽象和底层的事务管理器Transaction Manager)来实现。Spring 提供了声明式事务管理的支持,使得开发者可以通过注解或 XML 配置简单地实现事务控制,而无需显式编写繁琐的事务管理代码。

Spring 中事务的实现原理主要包括以下几个关键点:

3.1 事务管理器(Transaction Manager):

Spring 提供了 PlatformTransactionManager 接口及其多个实现类,用于与底层的数据源或持久化技术(如 JDBC、JPA、Hibernate 等)集成,管理事务的生命周期。不同的事务管理器实现支持不同的事务管理策略,如 JDBC 的 DataSourceTransactionManager、JPA 的 JpaTransactionManager 等。

3.2 事务管理的抽象层:

Spring 使用了 AOP(面向切面编程)和代理模式来实现事务管理的声明式支持。通过 AOP,Spring 在运行时动态地为带有 @Transactional 注解的方法生成代理对象,代理对象在方法调用前后添加事务管理逻辑,实现事务的开启、提交或回滚等操作。

3.3 事务传播行为(Propagation Behavior):

@Transactional 注解中的 propagation 属性用于指定事务的传播行为,定义了方法调用时事务如何传播到嵌套方法或者同一方法内的其他事务操作。Spring 提供了多种传播行为选项,如 Propagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.SUPPORTS 等,用于控制事务在不同方法间的传播和协调。

3.4 事务隔离级别(Isolation Level):

@Transactional 注解中的 isolation 属性用于指定事务的隔离级别,定义了事务对并发操作的可见性和影响范围。Spring 支持多种隔离级别,如 Isolation.DEFAULT、Isolation.READ_COMMITTED、Isolation.REPEATABLE_READ、Isolation.SERIALIZABLE 等,通过底层数据库支持来实现隔离级别的控制。

3.5 事务的切点(Pointcut)和通知(Advice):

Spring AOP 使用切点表达式(Pointcut Expression)来选择应用事务管理的方法,而事务管理本身则通过前置通知(Before Advice)、后置通知(After Returning Advice)、异常通知(After Throwing Advice)和最终通知(After Finally Advice)等方式来实现事务的各个阶段处理。

3.6 异常处理和事务回滚:

当被 @Transactional 注解标记的方法中抛出未被捕获的异常时,Spring 会自动回滚事务。这种机制确保了在方法执行失败或者异常情况下,数据库操作不会造成数据不一致或者丢失,保障了事务的一致性和可靠性。


http://www.ppmy.cn/server/88906.html

相关文章

应对爬虫过程中代理IP掉线的实用指南

当代理IP在爬虫中频繁掉线时,我们先要了解出现问题的可能原因,这不仅限于技术性因素,还涉及操作策略和环境因素。只有在找到具体原因后,才能针对问题类型从源头解决IP掉线问题。 一、问题原因: 1. 代理IP质量问题导致…

Photoshop钢笔工具

一、钢笔工具概述 Photoshop中的钢笔工具(快捷键为P)是矢量绘图工具,主要用于精确绘制直线、曲线以及开放或闭合的路径。钢笔工具通过锚点和方向线来定义路径的形状,可以绘制出平滑的曲线和直线段,非常适合用于抠图、…

身份认证(session + token)

web 开发模式 服务端渲染 前后端分离 如何选择 前后端身份验证 session 原理 什么是 cookie cookie 安全性 提高cookie 安全性 配置 session 中间件 注意 只会清除当前用户的 session JWT 认证机制 express 中使用 JWT JWT 字符串 还原为 JSON 对象 一般 约定 api 开头的都…

Redisson中RSet的使用场景及用它来做网站独立访客统计的例子

RSet 是 Redisson 提供的一个用于操作 Redis 的 Set 数据类型的高级接口。在 Redis 中,Set 是一个存储字符串元素的无序集合,不允许重复元素。RSet 在 Java 应用程序中提供了一种方便的方式来操作 Redis 中的 Set 结构。下面列举了一些可能使用 RSet 的场…

ubuntu安装mysql8.0

文章目录 ubuntu版本安装修改密码取消root跳过密码验证 ubuntu版本 22.04 安装 更新软件包列表 sudo apt update安装 MySQL 8.0 服务器 sudo apt install mysql-server在安装过程中,系统可能会提示您设置 root 用户的密码,请务必牢记您设置的密码。…

Github遇到的问题解决方法总结(持续更新...)

1.github每次push都需要输入用户名和token的解决方法 push前,执行下面命令 : git config --global credential.helper store 之后再输入一次用户名和token之后,就不用再输入了。 2.git push时遇到“fatal: unable to access https://githu…

Docker-Compose实现MySQL之主从复制

1. 主服务器(IP:192.168.186.77) 1.1 docker-compose.yml services:mysql-master:image: mysql:latest # 使用最新版本的 MySQL 镜像container_name: mysql-master # 容器的名称environment:MYSQL_ROOT_PASSWORD: 123456 # MySQL root 用户的密码MYSQL_DATABASE: masterd…

编写Dockerfile文件解释

编写Dockerfile文件的基本步骤如下: 首先,创建一个新的文本文件,并将其命名为Dockerfile。 在Dockerfile的第一行添加基础镜像的指令。例如,使用ubuntu 18.04作为基础镜像,可以添加以下指令: FROM ubunt…

数据结构第二讲:顺序表

数据结构第二讲:顺序表 1.线性表2.什么是顺序表3. 静态顺序表4.动态顺序表4.1顺序表基础4.2顺序表的初始化4.3顺序表的销毁4.4顺序表的尾插4.5顺序表的头插4.6顺序表的尾删4.7顺序表的头删4.8顺序表在指定位置之前插入数据4.9顺序表删除指定位置的数据4.10顺序表查找…

分享一个最近在进行前后端联调时改了2天的bug...

场景再现 我们这边前端端口是8080 后端端口是8121 我们在前端里在首页面写了一个任务 当进入网页三秒后 发起一个叫getLoginUser的请求 我们的getLoginUser是调用的这里 一个异步请求 这边我们前端调用后端的接口也已经写好 我们先把后端跑起来 访问前端页面 接收到了这个…

1858. 数组查找及替换

问题描述 给定某整数数组和某一整数 b 。 要求删除数组中可以被 b 整除的所有元素,同时将该数组各元素按从小到大排序。如果数组元素数值在 𝐴‘ 到 Z 的 ASCII 之间,替换为对应字母。 元素个数不超过 100,𝑏 在 1 …

linux shell(中)

结构化命令 if语句 if-then 最基本的结构化命令是 if-then 语句。if-then 语句的格式如下: if command thencommands ifif command; then # 通过把分号(;)放在待求值的命令尾部,可以将 then 语句写在同一行commands ifbash sh…

git stash 命令详解

git stash 描述 git stash 命令用于将当前工作目录中的未提交更改(包括暂存区和工作区的更改)保存到一个栈中,并恢复工作目录到干净的 HEAD 状态。这样您可以在不提交当前更改的情况下,切换到其他分支或进行其他操作。后续可以通…

Golang使用docker sdk管理docker

包括列出容器、创建容器、删除容器、进入容器、构建镜像等操作。 package dockertoolimport ("context""fmt""github.com/docker/docker/api/types""github.com/docker/docker/api/types/container""github.com/docker/docker/ap…

微服务安全——OAuth2.1详解、授权码模式、SpringAuthorizationServer实战、SSO单点登录、Gateway整合OAuth2

文章目录 Spring Authorization Server介绍OAuth2.0协议介绍角色OAuth2.0协议的运行流程应用场景授权模式详解客户端模式密码模式授权码模式简化模式token刷新模式 OAuth 2.1 协议介绍授权码模式PKCE扩展设备授权码模式拓展授权模式 OpenID Connect 1.0协议Spring Authorizatio…

java对接soap工具类-callSonyInterfaceQuery

新写的工具类,其中有对接soap的工具类 import com.shutong.common.exception.ServiceException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.Cont…

基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-抄送服务处理

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、因为仿钉钉设计器里抄送人是一个服务任务&#xff0c;所以要根据这个服务任务进行处理 2、前端就是一个抄送&#xff0c;选择人 3、这里用了jeecg的选择人组件 <el-form-item prop…

LongAlign:大模型长文本处理能力提升之道

人工智能咨询培训老师叶梓 转载标明出处 大模型&#xff08;LLMs&#xff09;在处理长文本时&#xff0c;需要在输入序列上进行指令微调&#xff08;instruction finetuning&#xff09;&#xff0c;以确保它们能够有效地处理长文本。现有的方法主要集中在上下文扩展&#xff0…

Qt第十三章 目录和文件操作

目录和文件操作 文章目录 目录和文件操作设备I/O简介I/O设备的类型基本文件读写QFileQTemporaryFile 流操作QTextStreamQDataStream QFileInfoQDirQFileSystemWatcherQStandardPathsQSettings 设备I/O 简介 I/O设备的类型 基本文件读写 QFile QFile file("C:/Users/PV…

C++ | Leetcode C++题解之第278题第一个错误的版本

题目&#xff1a; 题解&#xff1a; class Solution { public:int firstBadVersion(int n) {int left 1, right n;while (left < right) { // 循环直至区间左右端点相同int mid left (right - left) / 2; // 防止计算时溢出if (isBadVersion(mid)) {right mid; // 答案…