详解Spring事务

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 1.声明式事务1.1.概述spring中事务分为两种:1.编程式事务,通过写代码来实现,每一步。2.声明式事务,直接通过配置来实现。常用的是声明式事务,接下来介绍的也是声明式事务。1.2.使用1.2.1.建表一个用户表,有id和账户余额两个字段。

1.声明式事务

1.1.概述

spring中事务分为两种:

1.编程式事务,通过写代码来实现,每一步。

2.声明式事务,直接通过配置来实现。

常用的是声明式事务,接下来介绍的也是声明式事务。

1.2.使用

1.2.1.建表

一个用户表,有id和账户余额两个字段。

CREATE TABLE user (
    id INT(255) AUTO_INCREMENT PRIMARY KEY,
    balance INT(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user VALUES(NULL,100),(NULL,100);

1.2.2.maven依赖

<properties>

   <spring.version>5.2.0.RELEASE</spring.version>

</properties>


<dependencies>

   <dependency>

       <groupId>org.springframework</groupId>

       <artifactId>spring-core</artifactId>

       <version>${spring.version}</version>

   </dependency>

   <dependency>

       <groupId>org.springframework</groupId>

       <artifactId>spring-context</artifactId>

       <version>${spring.version}</version>

   </dependency>

   <dependency>

       <groupId>org.springframework</groupId>

       <artifactId>spring-tx</artifactId>

       <version>${spring.version}</version>

   </dependency>

   <dependency>

       <groupId>org.springframework</groupId>

       <artifactId>spring-jdbc</artifactId>

       <version>${spring.version}</version>

   </dependency>

   <!-- 数据库连接池 -->

   <dependency>

       <groupId>com.mchange</groupId>

       <artifactId>c3p0</artifactId>

       <version>0.9.5.5</version>

   </dependency>


   <!-- 数据库驱动 -->

   <dependency>

       <groupId>mysql</groupId>

       <artifactId>mysql-connector-java</artifactId>

       <version>8.0.18</version>

   </dependency>

</dependencies>

1.2.3.配置

尤其要注意命名空间不要少引入!!!

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 数据源配置 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springtest?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC" />
        <property name="user" value="root" />
        <property name="password" value="admin" />
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg ref="dataSource" />
    </bean>
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!--开启注解扫描-->
    <context:component-scan base-package="com.eryi.service"></context:component-scan>
    <!-- 启用事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

1.2.4.业务

这里模拟id为1的用户向id为2的用户转账100块,转账有两步组成:

  • id为1的用户账户扣100块
  • id为2的用户账户增加100块

在两步中模拟一个异常,导致只有只完成了id为1的用户扣100块的SQL执行了,但是id为2的用户增加100块的SQL没有执行。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void transfer(){
        try {
            // 从 fromUserId 账户中扣除金额
            jdbcTemplate.update("update user set balance = balance - ? where id = ?", 100, 1);
            // 抛出异常,测试事务是否回滚
            int i = 1 / 0;
            // 将金额转入 toUserId 账户中
            jdbcTemplate.update("update user set balance = balance + ? where id = ?", 100, 2);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

1.2.5.测试

public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.transfer();
    }

如果没有事务的话,会是这种情况:

f1163cc7eb434b0088c9fa13d94939b2.png

有了事务的话,事务中所有SQL会具有原子性,要么全部成功,要么全部失败,会是这个结果:

efd7a401b23349adbf7c6a41018ecb8a.png

2.事务的传播行为

Spring中存在着一个概念——事务的传播行为。事务的传播行为指的是一个事务方法被另一个事务方法调用,即嵌套事务,出现异常该怎么回滚。

Spring规定有7种事务的传播行为:

required 如果出现异常的方法有事务就用这个事务,没有的话再去用其他的事务。例如:方法A调用方法B,如果B出现异常,B有事务就用B的事务,B没有事务,才会去用A的事务(只要有一个回滚,整体就会回滚)
requires_new 如果当前有事务,就用当前事务,否则就重新开一个事务。例如:方法A调用方法B,如果B出现异常,B有事务就用B的事务,B没有事务,就会去新建一个事务,最后效果是B会回滚,但是A不会回滚。
supports 当前没有事务,就以非事务运行。当前有事务呢?就以当前事务运行。
mandatory 其他没有事务就会抛异常。当前没有事务抛出异常,当前有事务则支持当前事务。
not_supported 以非事务执行。
never 以非事务执行,如果存在事务,抛出异常
nested 如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。

这里附上一篇其它博主对于七种事务的传播属性的介绍,其中有详细的代码示例和讲解,很清晰:

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
SQL Java 关系型数据库
【SpringFramework】Spring事务
本文简述Spring中数据库及事务相关衍伸知识点。
51 9
|
3月前
|
Java 开发者 Spring
理解和解决Spring框架中的事务自调用问题
事务自调用问题是由于 Spring AOP 代理机制引起的,当方法在同一个类内部自调用时,事务注解将失效。通过使用代理对象调用、将事务逻辑分离到不同类中或使用 AspectJ 模式,可以有效解决这一问题。理解和解决这一问题,对于保证 Spring 应用中的事务管理正确性至关重要。掌握这些技巧,可以提高开发效率和代码的健壮性。
155 13
|
7月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
3月前
|
缓存 安全 Java
Spring高手之路26——全方位掌握事务监听器
本文深入探讨了Spring事务监听器的设计与实现,包括通过TransactionSynchronization接口和@TransactionalEventListener注解实现事务监听器的方法,并通过实例详细展示了如何在事务生命周期的不同阶段执行自定义逻辑,提供了实际应用场景中的最佳实践。
93 2
Spring高手之路26——全方位掌握事务监听器
|
8月前
|
Java 关系型数据库 MySQL
Spring 事务失效场景总结
Spring 事务失效场景总结
86 4
|
3月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
4月前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
86 1
Spring高手之路24——事务类型及传播行为实战指南
|
4月前
|
JavaScript Java 关系型数据库
Spring事务失效的8种场景
本文总结了使用 @Transactional 注解时事务可能失效的几种情况,包括数据库引擎不支持事务、类未被 Spring 管理、方法非 public、自身调用、未配置事务管理器、设置为不支持事务、异常未抛出及异常类型不匹配等。针对这些情况,文章提供了相应的解决建议,帮助开发者排查和解决事务不生效的问题。
141 1
|
4月前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
135 3
|
6月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务