Spring事务一网打尽

news2024/10/6 20:27:08

Spring事务一网打尽

    • 什么是事务
    • 首先说一个坑
    • Spring 中的事务
      • 两种用法
      • 三大基础设施
      • 编程性事务
        • TransactionManager 实现编程性事务
        • TransactionTemplate 实现编程性事务
      • 声明式事务
        • XML配置声明式事务
        • 注解配置声明式事务
        • 注解+XML混合配置声明式事务

什么是事务

在这里插入图片描述
在这里插入图片描述

这里要额外补充一点:只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!

首先说一个坑

在单元测试方法里面不能用事务注解,不然,代码增删改永远不会生效

在这里插入图片描述

Spring 中的事务

两种用法

三大基础设施

  • PlatformTransactionManager
    PlatformTransactionManager 就像以前学的 JDBC,它就是一个接口,一个规范
    在这里插入图片描述

    public interface PlatformTransactionManager extends TransactionManager {
        TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    
        void commit(TransactionStatus status) throws TransactionException;
    
        void rollback(TransactionStatus status) throws TransactionException;
    }
    
  • TransactionDefinition
    TransactionDefinition 主要定义的一些事务的属性,看源码就清楚了
    在这里插入图片描述

  • TransactionStatus
    你可以理解为事务本身,当然也可以说是事务状态
    在这里插入图片描述

编程性事务

TransactionManager 实现编程性事务
  • 导入必须的依赖
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.3.20</version>
        </dependency>
        <!--jdbc事务相关的代码-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>5.3.30</version>
        </dependency>
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.25</version>
        </dependency>
    
  • 全部代码如下
    @Service
    public class UserServie {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        @Autowired
        private PlatformTransactionManager transactionManager;
        @Autowired
        private TransactionTemplate transactionTemplate;
        public void transfer() {
            // 定义默认的事务属性
            DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
            // 获取 transactionStatus
            TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
            try {
                jdbcTemplate.update("update user set money = ? where username = ?;", 33, "hok");
                // 提交事务
                transactionManager.commit(transactionStatus);
            } catch (DataAccessException e) {
                e.printStackTrace();
                transactionManager.rollback(transactionStatus);
            }
        }
    
    public class App {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserServie userServie = ctx.getBean(UserServie.class);
            userServie.transfer();
        }
    }
    
    <?xml version="1.0" encoding="UTF-8"?>
            <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--配置包扫描-->
        <context:component-scan base-package="com.lhg.springtx" />
    
        <!-- 配置数据源 -->
        <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" >
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" />
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </bean>
        <!--配置事务管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
        <bean  id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager" ref="transactionManager" />
        </bean>
        <!--JdbcTemplateSpringJDBC 的封装,用于操作数据库及事务的-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
            <property name="dataSource" ref="dataSource" />
        </bean>
    </beans>
    
  • 运行结果如下,可以看到数据正确修改
    在这里插入图片描述
  • 如果转账过程中出现了问题会怎么样?
    在这里插入图片描述
    如下图可以看到数据库更改并没有生效,事务回滚成功
    在这里插入图片描述
    TransactionTemplate 实现编程性事务
    把 transfer 方法改成如下,实地验证会发现有同样的效果
    @Service
    public class UserServie {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        @Autowired
        private PlatformTransactionManager transactionManager;
        @Autowired
        private TransactionTemplate transactionTemplate;
        public void transfer() {
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    try {
                        jdbcTemplate.update("update user set money = ? where username = ?;", 888, "hok");
                        int i = 1/0;
                    } catch (DataAccessException e) {
                        status.setRollbackOnly();
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }
    

从上面可以看到,编程性事务对业务方法侵入性太强了,实际项目开发一般不会去用

声明式事务

XML配置声明式事务

首先在上面的基础上再加个依赖

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.9.7</version>
</dependency>

applicationContext.xml配置

<?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置包扫描-->
    <context:component-scan base-package="com.lhg.springtx" />

    <!-- 配置数据源 -->
    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" >
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" />
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <bean  id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager" />
    </bean>
    <!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- XML 配置事务分为三个步骤:
        1、配置事务管理器
        2、配置事务通知
        3、配置AOP
    -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--配置事务的属性:
            isolation:用于指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的默认隔离级别
            propagation:用于指定事务的传播行为,默认值是REQUERD,表示一定会有事务,增删改的选择,查询方法可以使用SUPPORTS
            read-only:用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写
            rollback-for:用于指定一个异常,当该异常产生时,事务回滚,产生其它异常时事务不回滚,没有默认值,表示任何异常都回滚
            no-rollback-for:用于指定一个异常,当该异常产生时事务不回滚,产生其它异常时事务回滚,没有默认值,表示任何异常都回滚
            -->
            <tx:method name="add*"  propagation="REQUIRED" read-only="false"/><!--通用匹配-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true" /><!--匹配以find开头的方法,优先级更高-->
            <tx:method name="transfer*" />
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* com.lhg.springtx.UserServie.*(..))"/>
        <!--建立切入点表达式与事务通知的对应关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
    </aop:config>
</beans>

业务逻辑代码改成如下,实际运行发现已正确修改

@Service
public class UserServie {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void transfer() {
        jdbcTemplate.update("update user set money = ? where username = ?;", 666, "hok");
    }
}

当然如果执行过程中出异常了,事务是能够正确回滚的,这里就不再截图了

public void transfer() {
    jdbcTemplate.update("update user set money = ? where username = ?;", 999, "hok");
    int i = 1/0;
}
注解配置声明式事务

依赖和上面一样,无需更改,然后定义一个 Java配置类

@Configuration
@ComponentScan(basePackages = "com.lhg.springtx")
@EnableTransactionManagement
public class JavaConfig {
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://120.26.161.184:3306/wxpay?serverTimezone=Asia/Shanghai");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }
}

业务层代码如下,需要在哪个方法上加事务就在哪个方法上加个@Transactional注解

@Service
public class UserServie {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional
    public void transfer() {
        jdbcTemplate.update("update user set money = ? where username = ?;", 222, "hok");
        int i = 1/0;
    }
}

启动执行

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        UserServie userServie = ctx.getBean(UserServie.class);
        userServie.transfer();
    }
}

运行后会发现事务依然能够生效,这里就不一一截图了

注解+XML混合配置声明式事务

说白了就是想把这一坨干掉,换成<tx:annotation-driven />
在这里插入图片描述
来看看 xml 配置

<?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置包扫描-->
    <context:component-scan base-package="com.lhg.springtx" />

    <!-- 配置数据源 -->
    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource" >
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://120.26.161.184:3306/wxpay" />
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <bean  id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager" />
    </bean>
    <!--JdbcTemplate是 Spring 对 JDBC 的封装,用于操作数据库及事务的-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--事务注解支持-->
    <tx:annotation-driven />
</beans>

业务代码如下

@Service
public class UserServie {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void transfer() {
        jdbcTemplate.update("update user set money = ? where username = ?;", 333, "hok");
        int i = 1/0;
    }
}

启动运行

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserServie userServie = ctx.getBean(UserServie.class);
        userServie.transfer();
    }
}

当然最终效果还是一样,事务正常

注解+XML混合配置需要根据实际项目需要,不一定是像上面这样…

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

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

相关文章

单片机第三季-第五课:GPIO控制LED

目录 1&#xff0c;GPIO数据手册 1.1&#xff0c;端口配置寄存器 ​1.2&#xff0c;端口输入数据寄存器和端口输出数据寄存器 ​1.3&#xff0c;端口位设置/清除寄存器 1.4&#xff0c;端口位清除寄存器 2&#xff0c;原理图分析和MDK工程 3&#xff0c;写代码通过GP…

快速入门:教你如何使用vue-element-admin 集成框架开发项目(保姆式教学)

设置完中文界面&#xff0c;打开是这样的 端口号更改 嘿嘿&#xff0c;要把项目改成自己的&#xff0c;是不是要更改标题啊 题目更改 src/utils/get-page-title.js src/settings.js 如何添加菜单页面 添加菜单页面&#xff08;父菜单/子菜单&#xff09; 效果图 添加自己的页…

Unity--视觉组件(Raw Image,Mask)||Unity--视觉组件(Text,Image)

1.Raw Image 2.mask “”Raw Image&#xff1a;“” Texture&#xff1a;&#xff08;纹理&#xff09; 表示要显示的图像的纹理&#xff1b; Color&#xff1a;&#xff08;颜色&#xff09; 应用于图像的颜色&#xff1b; Material&#xff1a;&#xff08;材质&#xff09…

FreeRTOS源码阅读笔记3--queue.c

消息队列可以应用于发送不定长消息的场合&#xff0c;包括任务与任务间的消息交换&#xff0c;队列是 FreeRTOS 主要的任务间通讯方式&#xff0c;可以在任务与任务间、中断和任务间传送信息&#xff0c;发送到 队列的消息是通过拷贝方式实现的&#xff0c;这意味着队列存储…

前端开发项目中使用字体库

开发中有些项目要求使用固定的字体&#xff0c;这就需要项目中使用字体库。 首先需要下载字体库 将下载的字体文件放进项目中 在项目代码样式文件中定义字体 font-face {font-family: "Tobias-SemiBold";src: url("./assets/font/Tobias-SemiBold.ttf"…

arcgis基础篇--实验

一、绘制带空洞的面要素 方法一&#xff1a;先绘制出一个面区域&#xff0c;然后在面上再绘制一个面区域代表面洞&#xff0c;两者位于同一个图层内&#xff0c;选中代表面洞的区域&#xff0c;选择【编辑器】-【裁剪】工具&#xff0c;将面裁剪出一个洞&#xff0c;随后删除代…

jupyter notebook添加markdown目录

jupyternotebook添加markdown目录 1. 安装python包2. 安装JavaScript和CSS文件3. 启用扩展4. 设置markdown选项 1. 安装python包 官方安装 使用pip pip install jupyter_contrib_nbextensions # 或者 pip install https://github.com/ipython-contrib/jupyter_contrib_nbext…

苹果M3处理器跑分曝光,单核无敌!

10月底&#xff0c;苹果发布了全新的M3、M3 Pro、M3 Max芯片以及搭载M3系列芯片的3款新硬件&#xff0c;包括&#xff1a;新款24英寸iMac、新款14/16英寸MacBook Pro。 根据苹果官方介绍&#xff0c;M3系列芯片基于台积电3纳米工艺打造&#xff0c;采用全新图形处理器架构&…

【操作系统内核】线程

【操作系统内核】线程 为什么需要线程 比如我要做一个视频播放器&#xff0c;就需要实现三个功能&#xff1a; ① 从磁盘读取视频数据 ② 对读取到的视频数据进行解码 ③ 对解码的数据进行播放 如果串行执行&#xff08;通过一个进程来执行&#xff09;&#xff1a; 那么…

视通科技新品发布:4K30分布式编解码一体机,高性价比之选!

随着信息技术的日新月异&#xff0c;各领域对于音视频传输、控制和显示等方面的需求呈现出爆发式的增长。这种需求的增长源于多种因素&#xff0c;包括但不限于高清视频的普及&#xff0c;实时音视频通信的广泛应用&#xff0c;以及各种显示设备的升级换代。 在这样的背景下&a…

动态规划(5)---Leetcode338.比特位计数

题目 给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中 1 的个数 &#xff0c;返回一个长度为 n 1 的数组 ans 作为答案。 分析 通常动态规划的做题顺序&#xff0c;先确定dp数组dp[i],然后确定确定递推公式&#xff0c;再dp数…

Gogs安装和部署教程-centos上

0、什么是 Gogs? Gogs 是一款极易搭建的自助 Git 服务。 Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发&#xff0c;并且支持 Go 语言支持的 所有平台&#xff0c;包括 Linux、Mac OS X、Windo…

华为防火墙2种局域网内远程监控其它端口的方法

防火墙必须要工作在三层&#xff0c;接口上有地址 第一种用ip-link 这种方法是如果监测的接口故障后&#xff0c;自身优先级降低2 FW3 ip-link check enable ip-link 1 destination 1.1.1.1 interface g0/0/0 mode icmp hrp track ip-link 1 active FW4 ip-link check …

Hls学习(一)

1&#xff1a;CPU、DSP、GPU都算软件可编程的硬件 2&#xff1a;dsp在递归方面有所减弱&#xff0c;在递归方面有所增强&#xff0c;比如递归啊等&#xff0c;GPU可以同时处理多个进程&#xff0c;对于大块数据&#xff0c;流处理比较适用 3&#xff1a;为了提高运算量处理更多…

7-Zip的介绍和【阿里云盘】的使用

7zip从入门到入坑 前言一、7-zip的介绍和安装1、基本介绍1&#xff09;7-Zip 主要特征2&#xff09;支持格式3&#xff09;基础功能4&#xff09;安装环境需求 2、基本操作&#xff08;1&#xff09;简便的界面&#xff08;2&#xff09;发生的问题 二、阿里云盘的使用1、“exe…

OSG练习:模仿Ventsim制作三维矿井智能通风系统

1、效果 2、计划内容 1) 三维场景的加载显示;已实现 2)矿井巷道建模及纹理;已实现 3)矿井基础数据采集及修正;已实现 4)通风网络解算算法;已实现 5)通风设备及设施模型制作;未实现 6)风流模拟效果 ;进行中 7)火灾模拟效果;未实现 8)巷道属性查看栏;未实现 9)…

k8s 裸金属集群部署metalLB软负载均衡 —— 筑梦之路

metalLB 官方网站 Repo&#xff1a;https://github.com/metallb/metallb 官网&#xff1a;https://metallb.universe.tf/installation metalLB解决什么问题&#xff1f; MetalLB 是一个用于裸机 Kubernetes 集群的负载均衡器实现&#xff0c;使用标准路由协议。 k8s 并没有为裸…

深度学习 python opencv 火焰检测识别 火灾检测 计算机竞赛

文章目录 0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 数据集准备5.1 数…

解锁 PaddleOCR 的超能力

光学字符识别&#xff08;OCR&#xff09;是一项强大的技术&#xff0c;使机器能够从图像或扫描文档中识别和提取文本。OCR 在各个领域都有应用&#xff0c;包括文件数字化、从图像中提取文本以及基于文本的数据分析。在本文中&#xff0c;我们将探讨如何使用 PaddleOCR&#x…

LeetCode【923】三数之和的多种可能性

题目&#xff1a; 思路&#xff1a; https://www.jianshu.com/p/544cbb422300 代码&#xff1a; int threeSumMulti(vector<int>& A, int target) {//Leetcode923:三数之和的多钟可能//initialize some constint kMod 1e9 7;int kMax 100;//calculate frequenc…