Spring3事务

news2024/10/2 12:17:17

简介

数据库事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成;在企业级开发应用中,事务管理是必不可少的技术,它被用来保证数据的完整性和一致性

事务的四大特性(ACID)

  1. 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用

  2. 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败,在现实中的数据不应该被破坏

  3. 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

  4. 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来,通常情况下,事务的结果被写到持久化存储器中

核心接口

Spring 事务管理涉及的接口的联系如下
在这里插入图片描述

事务管理器

Spring 并不直接管理事务,而是提供了多种事务管理器,将事务管理的职责委托给 Hibernate 或者 JTA 等持久化机制所提供的相关平台框架的事务实现

Spring 事务管理器的接口是 org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring 为各个平台如 JDBC、Hibernate 等提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了;此接口内容如下

Public interface PlatformTransactionManager()...{  
    // 由TransactionDefinition得到TransactionStatus对象
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
    // 提交
    Void commit(TransactionStatus status) throws TransactionException;  
    // 回滚
    Void rollback(TransactionStatus status) throws TransactionException;  
}

Spring 支持 4 大事务:JDBC transaction,Hibernate transaction,JPA transaction,JTA transaction

  1. JDBC transaction

如果应用程序中直接使用 JDBC 来进行持久化,DataSourceTransactionManager 会为你处理事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

实际上,DataSourceTransactionManager 是通过调用 java.sql.Connection 来管理事务,而后者是通过 DataSource 获取到的; 通过调用连接的 commit() 方法来提交事务,同样,事务失败则通过调用 rollback() 方法进行回滚

  1. Hibernate transaction

如果应用程序的持久化是通过 Hibernate 实现的,那么你需要使用 HibernateTransactionManager

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
  1. JPA transaction(Java 持久化 API 事务)

Hibernate 一直是事实上的 Java 持久化标准,但是现在 Java 持久化 API 作为真正的 Java 持久化标准,如果使用 JPA 的话,那需要使用 Spring 的 JpaTransactionManager 处理事务

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

JpaTransactionManager 只需装配一个 JPA 实体管理工厂(javax.persistence.EntityManagerFactory 接口的任意实现) JpaTransactionManager 将与由工厂所产生的 JPA EntityManager 合作构建事务

  1. JTA transaction(Java 原生 API 事务)

如果没有使用以上所述的事务管理,或是跨越了多个事务管理源(比如两个或者是多个不同的数据源),需要使用 JtaTransactionManager

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManagerName" value="java:/TransactionManager" />
</bean>

JtaTransactionManager 将事务管理的责任委托给 javax.transaction.UserTransaction和javax.transaction.TransactionManager 对象,其中事务成功完成通过 UserTransaction.commit() 方法提交,事务失败通过 UserTransaction.rollback() 方法回滚

基本事务属性定义

事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 方法得到事务,这个方法里面的参数是 TransactionDefinition 类,这个类定义了一些基本的事务属性;事务属性可以理解为事务的一些基本配置,描述了事务策略如何应用到方法上,事务属性包含了 5 个方面,如图所示
在这里插入图片描述
TransactionDefinition 接口内容如下

public interface TransactionDefinition {
    int getPropagationBehavior(); // 返回事务的传播行为
    int getIsolationLevel(); // 返回事务的隔离级别, 事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
    int getTimeout();  // 返回事务必须在多少秒内完成
    boolean isReadOnly(); // 事务是否只读, 事务管理器能够根据这个返回值进行优化, 确保事务是只读的
}

事务的传播行为

事务的第一个方面是传播行为(propagation behavior)当事务方法被另一个事务方法调用时,必须指定事务应该如何传播,例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行,Spring 定义了七种传播行为
在这里插入图片描述
事务隔离级别
隔离级别(isolation level) 定义了一个事务可能受其他并发事务影响的程度
在这里插入图片描述

并发事务引起的问题

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务;并发虽然是必须的,但可能会导致以下问题

  1. 脏读(Dirty reads),发生在一个事务读取了另一个事务改写但尚未提交的数据时;如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的

  2. 不可重复读(Nonrepeatable read),发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新;不可重复读的重点是修改: 同样的条件, 你读取过的数据, 再次读取出来发现值不一样了

  3. 幻读(Phantom read),幻读与不可重复读类似,发生在一个事务(T1) 读取了几行数据,接着另一个并发事务(T2) 插入了一些数据时;在随后的查询中,第一个事务(T1)就会发现多了原本不存在的记录;幻读的重点在于新增或者删除:同样的条件, 第1次和第2次读出来的记录数不一样

从总的结果来看, 似乎 不可重复读 和 幻读 都表现为两次读取的结果不一致;但如果从控制的角度来看, 两者的区别就比较大:

对于前者, 只需要锁住满足条件的记录

对于后者, 要锁住满足条件及其相近的记录

只读

事务的第三个特性是它是否为只读事务,如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化,通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施

事务超时

为了使应用程序很好地运行,事务不能运行太长的时间;因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源;事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束

回滚规则

回滚规则定义了哪些异常会导致事务回滚而哪些不会;默认情况下,事务只有遇到运行期异常(RuntimeException)时才会回滚,而在遇到检查型异常(CheckedException) 时不会回滚(这一行为与EJB的回滚行为是一致的) 但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚;同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常

事务状态

PlatformTransactionManager 接口的 getTransaction() 的方法得到的是 TransactionStatus 接口的一个实现,这个接口的内容如下

public interface TransactionStatus {
    boolean isNewTransaction(); // 是否是新的事务
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
}

这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态

编程式事务

Spring 提供两种方式的编程式事务管理,分别是:使用 TransactionTemplate 和直接使用 PlatformTransactionManager

使用 TransactionTemplate

采用 TransactionTemplate 和采用其他 Spring 模板,如 JdbcTempalte 和 HibernateTemplate 是一样的方法;它使用回调方法,把应用程序从处理取得和释放资源中解脱出来;如同其他模板,TransactionTemplate 是线程安全的;如下代码片段

TransactionTemplate tt = new TransactionTemplate(); // 新建一个TransactionTemplate
Object result = tt.execute(
    new TransactionCallback() {  
        public Object doTransaction(TransactionStatus status) {  
            updateOperation();  
            return resultOfUpdateOperation();  
        }  
}); // 执行execute方法进行事务管理

使用 TransactionCallback() 可以返回一个值;使用 TransactionCallbackWithoutResult 则没有返回值

使用 PlatformTransactionManager

示例代码如下

//定义一个某个框架平台的TransactionManager,如JDBC、Hibernate
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); 
dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 设置数据源
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态
try {
    // 数据库操作
    dataSourceTransactionManager.commit(status); // 提交
 } catch (Exception e) {
    dataSourceTransactionManager.rollback(status); // 回滚
 }

声明式事务

根据代理机制的不同,总结了五种 Spring 事务的配置方式,配置文件如下

  1. 每个 Bean 都有一个代理
<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDao"  class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
    <!-- 配置事务管理器 --> 
    <property name="transactionManager" ref="transactionManager" />    
    <property name="target" ref="userDaoTarget" /> 
    <!-- 配置事务属性 --> 
    <property name="transactionAttributes"> 
        <props> 
            <prop key="*">PROPAGATION_REQUIRED</prop>
            <prop key="select*">PROPAGATION_REQUIRED, readOnly</prop>
	    </props> 
    </property> 
</bean>
  1. 所有Bean共享一个代理基类
<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transactionBase" 
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"> 
    <!-- 配置事务管理器 --> 
    <property name="transactionManager" ref="transactionManager" /> 
    <!-- 配置事务属性 --> 
    <property name="transactionAttributes"> 
        <props> 
            <prop key="*">PROPAGATION_REQUIRED</prop> 
        </props> 
    </property> 
</bean>   
<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDao" parent="transactionBase" > 
    <property name="target" ref="userDaoTarget" />  
</bean>
  1. 使用拦截器
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean> 
<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean> 
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> 
    <property name="transactionManager" ref="transactionManager" /> 
    <!-- 配置事务属性 --> 
    <property name="transactionAttributes"> 
        <props> 
            <prop key="*">PROPAGATION_REQUIRED</prop> 
        </props> 
    </property> 
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
    <property name="beanNames"> 
        <list> 
	    <value>*Dao</value>
	</list> 
    </property> 
    <property name="interceptorNames"> 
        <list> 
	    <value>transactionInterceptor</value> 
	</list> 
    </property> 
</bean> 
<!-- 配置DAO -->
<bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
  1. 使用 tx 标签配置的拦截器
<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
	<tx:method name="*" propagation="REQUIRED" />
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />       
</aop:config>
  1. 使用注解
<!-- 打开注解模式 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean> 
<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

然后在 Service 层上需加上 @Transactional 注解

注意

(1) Spring 使用事务的话,会默认关闭自动提交功能,提交和回滚将被托管给事务

(2) Spring 事务中的回滚定义的异常一定是运行时异常,虽然可以定义遇到异常不回滚

(3) Spring 事务最好定义在 Service 层(实现类)上,也就是业务逻辑层,不要定义在接口上

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【黑马SpringCloud(7)】分布式事务

分布式事务事务的ACID原则分布式事务理论基础CAP定理BASE理论Seataseata的部署seata的集成事务模式XA模式Seata的XA模型优缺点实现XA模式AT模式案例&#xff1a;AT模式更新数据脏写问题优缺点实现AT模式TCC模式流程分析Seata的TCC模型事务悬挂和空回滚实现TCC模式优缺点SAGA模式…

MySQL8.x group_by报错的4种解决方法

在我们使用MySQL的时候总是会遇到各种各样的报错&#xff0c;让人头痛不已。其中有一种报错&#xff0c;sql_modeonly_full_group_by&#xff0c;十分常见&#xff0c;每次都是老长的一串出现&#xff0c;然后带走你所有的好心情&#xff0c;如&#xff1a;LIMIT 0, 1000 Error…

《Qt6开发及实例》6-2 Qt6基础图形的绘制

目录 一、绘图框架设计 二、绘图区的实现 2.1 PaintArea类 2.2 PaintArea类讲解 三、主窗口的实现 3.1 MainWidget类 3.2 MainWidget类讲解 3.3 槽函数编写 3.5 其他内容 一、绘图框架设计 界面 两个类 ​ 二、绘图区的实现 2.1 PaintArea类 ​paintarea.h #ifndef…

uniApp消息推送(极光/阿里云)

目录 一、极光推送 1.1、在极光官网创建应用 1.2、插件下载 1.3、代码填充 1.4、发送通知/消息 二、阿里云推送 2.1、在阿里云官网创建应用 2.2、插件下载 2.3、代码填充 2.4、发给后端的值(API类型的通知 一、极光推送 1.1、在极光官网创建应用 参考 极光文档 (ji…

c/c++开发,无可避免的模板编程实践(篇三)

一、模板与多态 多态就是通过单一命名标记关联不同特定行为的能力。在C中&#xff0c;主要是通过继承和虚函数来实现&#xff0c;由于继承和虚函数主要是在运行期进行处理&#xff0c;因此c把这种多态称为“动多态”。而通过函数重载方式也可以单一命名标记关联不同行为&#x…

TrueNas篇-硬盘直通

硬盘直通 在做硬盘直通之前&#xff0c;在trueNas(或者其他虚拟机)内是检测不到安装的硬盘的。 在pve节点查看硬盘信息 打开pve的shell控制台 输入下面的命令查看硬盘信息&#xff1a; ls -l /dev/disk/by-id/该命令会显示出实际所有的硬盘设备信息&#xff0c;其中ata代…

Python 给视频添加背景音乐 | Python工具

目录 前言 环境依赖 代码 总结 前言 本文提供给视频添加背景音乐的python工具&#xff0c;一如既往的实用主义。 环境依赖 ffmpeg环境安装&#xff0c;可以参考我的另一篇文章&#xff1a;windows ffmpeg安装部署_阿良的博客-CSDN博客 本文主要使用到的不是ffmpeg&#x…

绘制正余弦曲线中的sin(x),cos(x)的使用

目录一、 基础知识1.1 头文件1.2 原型1.3 参数1.4 返回值二、使用1. 坐标与弧度的对应关系一、 基础知识 1.1 头文件 #include <math.h> 1.2 原型 double sin(double x) double cos(double x) 1.3 参数 参数是弧度制&#xff08;rad&#xff09; 1.4 返回值 返…

Python 采集 筷 实现视频批量保存

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 刷到的视频怕它下架&#xff1f;我们来采集保存一下它 知识点: 动态数据抓包 requests发送请求 json数据解析 开发环境: python 3.8 运行代码 pycharm 2022.3 辅助敲代码 requests pip install requests 代码展示 需…

故障案例:MySQL唯一索引有重复值,官方却说This is not a bug

GreatSQL社区原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。GreatSQL是MySQL的国产分支版本&#xff0c;使用上与MySQL一致。作者&#xff1a;飞鱼过天文章来源&#xff1a;GreatSQL社区原创 问题原因故障解决方案复现步骤参考文献 一、问题&#xff1a;…

图片压缩怎么弄?多种图片格式压缩大小的方法

平时接触的图片格式有许多种&#xff0c;比如jpg、png、gif、tiff、webp等&#xff0c;不同的场景都需要用不同的图片&#xff0c;但是当这些图片大小都不符合我们的使用要求时&#xff0c;该怎么去压缩图片大小呢&#xff1f;小编今天给大家分享一款支持多种图片格式压缩工具&…

openEuler RISC-V 成功适配 VisionFive 2 单板计算机

近日&#xff0c;RISC-V SIG 成功在 VisionFive 2 开发板上适配欧拉操作系统&#xff0c;目前最新版本的 openEuler RISC-V 22.03 V2 镜像已在 VisionFive 2 开发板上可用&#xff0c;这是 openEuler 推动 RISC-V 生态演进的又一新进展。下载链接​​https://mirror.iscas.ac.c…

如何在Power Virtual Agents中使用Power Automate

今天我们来介绍一下如何在Power Virtual Agents中使用PowerAutomate。我们以通过在PVA聊天机器人的对话框中输入“发布通知”后会把预设好的通知信息自动发布到Teams中的某个团队中为例。首先进入PVA聊天机器人编辑界面后选择“主题”-“新建主题”。 在“新建主题”中添加“触…

服务器容器配置日志(Linux+x86_64+Ubuntu18.04+CUDA11.0+python3.7)

一、创建并进入容器 &#xff08;平台使用教学详细&#xff0c;这部分略写&#xff09; 登上服务器后&#xff0c;打开终端输入如下进入自己建的容器 ssh -p XXXXX root10.XXX.XXX.XXX //按自己的宿主机端口写二、安装Conda&#xff08;miniconda3&#xff09; &#xff08…

数据库系统:1. 绪论

更好的阅读体验\huge{\color{red}{更好的阅读体验}}更好的阅读体验 文章目录1.1 数据库系统概述1.1.1 基本概念数据&#xff08;data&#xff09;数据库&#xff08;DataBase, DB&#xff09;数据库管理系统&#xff08;DataBase Management System, DBMS&#xff09;数据库系统…

极验3代 加密分析

目标链接 aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vZGVtby9zbGlkZS1mbG9hdC5odG1s接口分析 极验参数重要信息 gt和challenge&#xff1b;gt是固定的&#xff0c;但是challenge每次请求会产生不同的&#xff0c;这里的请求的并没有什么加密参数。 下一个请求 gettype.php&#xff0c…

365天深度学习训练营-第J2周:ResNet50V2算法实战与解析

目录 一、前言 二、论文解读 1、ResNetV2结构与ResNet结构对比 2、关于残差结构的不同尝试 3、关于激活的尝试 三、模型复现 1.Residual Block 3、ResNet50V2架构复现 4.ResNet50V2模型结构大图 一、前言 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习…

自动定时播报股价变动(零代码):今天我的股票涨了吗?

你是否会关注某个上市公司每天的股价信息吗&#xff1f;比如自己公司~你是否需要一个机器人&#xff0c;每天在内部群自动播报今日收盘价&#xff1f;不用复制粘贴&#xff0c;也不用写python&#xff0c;今天教你一个零代码可以自动播报股价数据的好办法。来看看我自动化的效果…

从开始测试到年薪40W,我的自动化测试艰辛历程

我希望我的故事能够激励现在的软件测试人&#xff0c;尤其是还坚持在做“点点点”的测试人。 你可能会有疑问&#xff1a;“我也能做到这一点的可能性有多大&#xff1f;”因此&#xff0c;我会尽量把自己做决定和思考的过程讲得更具体一些&#xff0c;并尽量体现更多细节。 …

嵌入式ARM设计编程(四) ARM启动过程控制

文章和代码已归档至【Github仓库&#xff1a;hardware-tutorial】&#xff0c;需要的朋友们自取。或者公众号【AIShareLab】回复 嵌入式 也可获取。 一、实验目的 &#xff08;1&#xff09; 掌握建立基本完整的ARM 工程&#xff0c;包含启动代码&#xff0c;C语言程序等&…