Spring事务一网打尽

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混合配置需要根据实际项目需要,不一定是像上面这样…

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

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

相关文章

docker主备节点数据同步

主备节点挂载 在生产环境中&#xff0c;赋予一个docker操作系统的权限是一件不安全的事&#xff0c;在不具有系统操作权限的情况下&#xff0c;主备机无法通过nfs进行挂载。此时&#xff0c;可借助数据卷进行挂载 创建两个数据卷 docker volume create vol1 docker volume cr…

openstack部署后实战

分布式部署规则 1、平常都是两台Node安装OpenStack平台&#xff0c;那如果想分布式部署该怎么做&#xff1f;比如&#xff1a;部署两台Nova服务&#xff0c;一台单独的Neutron服务&#xff0c;一台单独的存储节点等。 整体思想&#xff1a; 如果想要部署两台Nova服务&#xf…

vue3 使用element plus 打包时 报错

vue3vitetselementPlus中运行正常打包出错 能正常运行&#xff0c;但是打包出错 解决打包时出现导入element plus相关的爆红&#xff0c;导致无法打包的问题 如若出现类似于&#xff1a;Module ‘“element-plus”’ has no exported member ‘ElMessage’. Did you mean to…

94.二叉树的中序遍历

描述 : 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 题目 : LeetCode 94.二叉树的中序遍历 : 94. 二叉树的中序遍历 分析 : 这个代码还是很好写的 ...... 解析 : /*** Definition for a binary tree node.* public class TreeNode {* int val;…

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

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

Gogs安装和部署教程-centos上

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

「题解」环形链表的约瑟夫问题

文章目录 &#x1f349;题目&#x1f349;解析&#x1f34c;创建环形链表&#x1f34c;释放指定节点&#x1f34c;其他思路 &#x1f349;写在最后 &#x1f349;题目 &#x1f349;解析 题目的意思就是从环形链表的第一个节点开始数&#xff0c;数到第 m 的时候释放对应的节点…

CMake教程--QT项目使用CMake

CMake教程--QT项目使用CMake Chapter1 CMake教程--QT项目使用CMake1. Basic Cmake Based Project2. Executable VS Library3. Every module has its own CMakeList.txt in its folder3.1 不推荐的做法&#xff1a;3.2 推荐的做法 4. 强制以Debug, Release, RelWithDebInfo, Min…

Mac安装与配置eclipse

目录 一、安装Java&#xff1a;Mac环境配置&#xff08;Java&#xff09;----使用bash_profile进行配置&#xff08;附下载地址&#xff09; 二、下载和安装eclipse 1、进入eclipse的官网 (1)、点击“Download Packages ”​编辑 (2)、找到macOS选择符合自己电脑的框架选项…

人工智能基础_机器学习023_理解套索回归_认识L1正则---人工智能工作笔记0063

然后上一节我们说了L1,L2正则是为了提高,模型的泛化能力, 提高泛化能力,实际上就是把模型的公式的w,权重值,变小对吧. 然后我们这里首先看第一个L1正则,是怎么做到把w权重变小的 可以看到最上面是线性回归的损失函数,然后 L1可以看到,这个正则,就是在损失函数的基础上给损失…

轻松按需缩放图片像素——批量处理图片,满足不同需求!

在图片使用过程中&#xff0c;我们经常需要按照不同的要求调整图片的像素大小。如果一张一张地手动调整&#xff0c;不仅耗时而且容易出错。这款软件可以帮助您轻松实现批量处理图片&#xff0c;按需缩放图片像素&#xff0c;让您的图片管理更加高效、便捷&#xff01; 第一步…

Vuex持久化插件

Vuex数据默认是存储在内存中的&#xff0c;当然我们也可以将它存储在Local Storage&#xff0c;也可以指定某些数据存储在Local Storage 这样我们就用到了Vuex持久化插件vuex-persistedstate 安装vuex-persistedstate插件 npm install vuex-persistedstate --save 案列&#x…

MPSO-WPA

MPSO-WPA算法 DCAP means ’ discretized Cauchy’s argument principle’ 辅助信息 作者未提供代码

数据结构线性表——带头双向循环链表

前言&#xff1a;小伙伴们好久不见啦&#xff0c;上篇文章我们一起学习了数据结构线性表其一的单链表&#xff0c;了解了单链表的不少好处&#xff0c;但是不可能有完美的数据结构&#xff0c;就算是单链表&#xff0c;也会有很多缺点。 那么今天这篇文章&#xff0c;我们就来…

node插件MongoDB(五)—— 库mongoose 的模块化(五)

文章目录 一、使用mongoose 模块化的原因二、准备工作2. 启动mongo.exe 和mongod.exe 两个程序连接数据库 三、基本模块的拆分1、基本逻辑2、代码3、代码图示说明 四、在index.js 中进一步的拆分1.拆分原因2.新建model文件夹存储文档的结构对象3.代码4.代码实际演示和注意点 一…

用volta管理不同项目node版本

1 什么是volta volta是一个node.js的版本管理工具&#xff0c;你的电脑上安装了很多个node版本&#xff0c;volta可以让你在不同的项目中使用不同版本的node.js,并且可以切换node.js版本 Volta会自动将安装的Node.js版本与该项目绑定&#xff0c;使得您在该项目中执行 node、np…

Linux centos系统中添加磁盘

为了学习与训练文件系统或磁盘的分区、格式化和挂载/卸载&#xff0c;我们需要为虚拟机添加磁盘。根据需要&#xff0c;可以添加多块不同大小的磁盘。具体操作讨论如下&#xff0c;供参考。 一、添加 1.开机前 有两个地方&#xff0c;可选择打开添加硬盘对话框 (1)双击左侧…

2024“点点点”测试员如何上岸测试开发岗?附完整学习路线

有很多人员会不断问自己&#xff0c;自己到底要不要学测试&#xff0c;或者要不要坚持做测试&#xff0c;测试的职业发展到底怎么样&#xff1f;如果你还在迷茫&#xff0c;在到处找各种大牛问类似的问题&#xff0c;我希望这篇文章&#xff0c;你看完能够结束你的这个烦恼&…

iPad系列将在2024年全面更新!

今年还会有新iPad发布吗&#xff1f;答案是否定的。因为早在前几天的季度电话会议上&#xff0c;苹果公司CEO蒂姆・库克就已经宣布&#xff0c;今年不会推出任何新的iPad产品。 这也意味着&#xff0c;今年将是苹果公司自2010年推出首款iPad设备以来&#xff0c;第一次没有发布…

模块化之CJS, AMD, UMD 和 ESM

[[toc]] 模块化优点 防止命名冲突代码复用高维护性CJS, AMD, UMD 和 ESM 历史 ES6之前,JS一直没有自己的模块体系后来社区出现了CommonJS和AMD,CommonJS 主要用于服务器(Node)AMD 主要用于浏览器ES6引入了ESM到此,JS终于有了自己的模块体系,基本上可以完全取代CJS和AMD…