惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Palo Alto Networks Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
CERT Recently Published Vulnerability Notes
C
Cybersecurity and Infrastructure Security Agency CISA
S
Schneier on Security
S
Securelist
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cyberwarzone
Cyberwarzone
Apple Machine Learning Research
Apple Machine Learning Research
S
SegmentFault 最新的问题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
GbyAI
GbyAI
Security Latest
Security Latest
Last Week in AI
Last Week in AI
Microsoft Security Blog
Microsoft Security Blog
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
C
Cisco Blogs
博客园 - 【当耐特】
Blog — PlanetScale
Blog — PlanetScale
Hugging Face - Blog
Hugging Face - Blog
B
Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
The Last Watchdog
The Last Watchdog
U
Unit 42
阮一峰的网络日志
阮一峰的网络日志
Project Zero
Project Zero
WordPress大学
WordPress大学
L
LINUX DO - 最新话题
F
Fortinet All Blogs
L
LINUX DO - 热门话题
PCI Perspectives
PCI Perspectives
Simon Willison's Weblog
Simon Willison's Weblog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MongoDB | Blog
MongoDB | Blog
Latest news
Latest news
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
The Hacker News
The Hacker News
爱范儿
爱范儿
O
OpenAI News
J
Java Code Geeks
T
The Exploit Database - CXSecurity.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More

博客园 - smileNicky

魔珐星云SDK实战测评:重构数字人交互的底层逻辑 Ling Studio 深度体验:当万亿参数大模型遇上AI原生IDE,编程范式正在重构 魔珐星云SDK实战测评:从0到1搭建会“思考+互动”的智能数字人客服应用 云电脑玩转 CANN 全攻略:从环境搭建到创新应用落地 SpringBoot系列之集成EasyExcel实现百万级别的数据导入导出实践 分布式ID生成方案总结整理 并发编程系列之如何正确使用线程池? Spring Cloud Alibaba系列之分布式服务组件Dubbo Spring5.0源码学习系列之Spring AOP简述 利用ADB命令强制卸载oppo自带浏览器 SpringBoot系列之从入门到精通系列教程 SpringCloud系列之API网关(Gateway)服务Zuul SpringCloud系列之服务容错保护Netflix Hystrix SpringCloud系列之客户端负载均衡Netflix Ribbon SpringCloud系列之使用Feign进行服务调用 SpringCloud系列使用Eureka进行服务治理 Spring Security系列之极速入门与实践教程 SpringBoot系列之actuator监控管理极速入门与实践 SpringBoot系列之IDEA项目中设置热部署教程 SpringBoot系列之发送邮件极速入门与实践
Spring5.0源码学习系列之事务管理概述
smileNicky · 2021-05-27 · via 博客园 - smileNicky

Spring源码学习系列博客专栏:链接

Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述

1、什么是事务?

事务就是一组原子性的SQL操作,或者说一个独立的工作单元。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)

注意:Spring的事务支持是基于数据库事务的,在MySQL数据库中目前只有InnoDB或者NDB集群引擎才支持,MySQL5.0之前的默认MyISAM存储引擎是不支持事务的

2、事务的ACID特性

ACID其实是事务特性的英文首字母缩写,具体含义是:原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability)

  • 原子性(atomicity):事务是一个原子操作,由一系列动作组成。整个事务中的所有操作要么全部提交成功,要么全部失败回滚;
  • 一致性(consistency):数据库总是从一个一致性的状态转换到另外一个一致性的状态,执行事务前后,数据保持一致;
  • 隔离性(isolation): 因为有多个事务处理同个数据的情况,因此每个事务都应该与其他事务隔离开来,防止数据脏读、不可重复读等等情况;
  • 持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢;

3、什么是脏读、不可重复读、幻读?

  • 脏读
    在A事务修改数据,提交事务之前,另外一个B事务读取了A事务未提交事务之前的数据,这种情况称之为脏读(Dirty Read)
  • 不可重复读
    一个A事务在读取某些数据,第1次读取出来的数据结果和第2次读取出来的不一致,因为在两次数据读取期间,另外的事务对数据进行了更改
  • 幻读
    幻读和不可重复读是很类似的,不同的地方在于幻读侧重于事务对数据的删除或者新增,都是因为在两次数据读取期间,因为另外事务对数据的删除还是新增,导致第2次读取的数据和第1次不一致

4、Spring事务管理核心接口

![在这里插入图片描述]( https://img-blog.csdnimg.cn/20210402140118592.png?x-oss-process=image/watermark ,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70)
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5、事务隔离级别

定义:事务的隔离级别定义了一个事务可能受其他并发事务影响的程度。隔离级别可以不同程度的解决脏读、不可重复读、幻读。

隔离级别 描述 脏读 不可重复读 幻读
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别,默认的为Repeatable read (可重复读)
ISOLATION_READ_UNCOMMITTED 不可提交读,允许读取尚未提交事务的数据,可能会导致脏读、不可重复读、幻读
ISOLATION_READ_COMMITTED 提交读,读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ 可重复读,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 串行化,这种级别是最高级别,服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读

6、事务的传播行为

事务传播行为 描述
PROPAGATION_REQUIRED 必须,默认值。如果A有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务
PROPAGATION_SUPPORTS 支持。如果A有事务,B将使用该事务;如果A没有事务,B将以非事务执行
PROPAGATION_MANDATORY 强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常
PROPAGATION_REQUIRES_NEW 必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。
PROPAGATION_NOT_SUPPORTED 不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。
PROPAGATION_NEVER 从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行
PROPAGATION_NESTED 嵌套。A和B底层采用保存点机制,形成嵌套事务。

7、事务管理其它属性

前面介绍了事务管理的隔离级别和传播行为这两个重要的属性,接着介绍一下事务的其它属性

  • 事务超时属性
    事务超时,属性值是timeout,指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。以 int 的值来表示超时时间,其单位是秒,默认值为-1。
  • 事务只读属性
    属性值readOnly,对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。只读事务不涉及数据的修改,数据库会提供一些优化手段,所以对于业务很明确的接口,可以适当加上只读属性
  • 事务回滚规则
    属性值rollbackFor,默认情况下,事务只有遇到运行期异常(RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚
属性名 说明
propagation 事务的传播行为,默认值为 REQUIRED
isolation 事务的隔离级别,默认值采用 DEFAULT
timeout 事务的超时时间,默认值-1,表示不会超时,如果设置其它值,超过该时间限制但事务还没有完成,则自动回滚事务
readOnly 指定事务为只读事务,默认值false
rollbackFor 指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。

8、Spring事务实现方式

Spring事务代码实现方式有两种,一种是编程式事务,一种是声明式事务。所谓编程式事务,是指通过Spring框架提供的TransactionTemplate或者直接使用底层的PlatformTransactionManager。声明式事务,依赖Spring AOP,配置文件中做相关的事务规则声明或者直接使用@Transactional注解

下面给出一个典型的转账汇款例子,先不用事务的方式实现,接着使用编程式事务和声明式事务进行事务管理

package com.example.springframework.dao.impl;

import com.example.springframework.dao.AccountDao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
 * <pre>
 *      AccountDaoImpl
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2021/03/25 15:51  修改内容:
 * </pre>
 */
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Override
    public void out(String outer, int money) {
        super.getJdbcTemplate().update("update account set money = money - ? where usercode=?",money,outer);
    }

    @Override
    public void in(String inner, int money) {
        super.getJdbcTemplate().update("update account set money = money + ? where usercode = ?",money , inner);
    }
}

package com.example.springframework.service.impl;

import com.example.springframework.dao.AccountDao;
import com.example.springframework.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Service;


/**
 * <pre>
 *      AccountServiceImpl
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2021/03/25 15:55  修改内容:
 * </pre>
 */
@Service
public class AccountServiceImpl extends JdbcDaoSupport implements AccountService {

    AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void transfer(final String outer,final String inner,final int money){
        accountDao.out(outer , money);
        // exception
        // int i = 1 / 0;
        accountDao.in(inner , money);
    }
    
}

代码例子看起来是挺正常的,不过假如在accountDao.out(outer , money);accountDao.in(inner , money);两个事务执行期间,发生异常,这时会怎么样?效果如图:Jack的账号已经转账成功,转了1000,不过Tom并没有收到汇款,这种情况在实际生活中肯定是不允许的,所以需要使用事务进行管理
![在这里插入图片描述]( https://img-blog.csdnimg.cn/20210401095033465.png?x-oss-process=image/watermark ,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70)

9、Spring编程式事务

Spring编程式事务实现,通过Spring框架提供的TransactionTemplate或者直接使用底层的PlatformTransactionManager

  • 使用TransactionTemplate的方式
private AccountDao accountDao;
private TransactionTemplate transactionTemplate;

public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
    this.transactionTemplate = transactionTemplate;
}

@Override
public void transfer(final String outer,final String inner,final int money){
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
            accountDao.out(outer , money);
            // exception
            int i = 1 / 0;
            accountDao.in(inner , money);
        }
    });
}
  • 使用PlatformTransactionManager的方式
private AccountDao accountDao;

public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
}

@Override
public void transferTrans(String outer, String inner, int money) {
    DataSourceTransactionManager dataSourceTransactionManager =
            new DataSourceTransactionManager();
    // 设置数据源
    dataSourceTransactionManager.setDataSource(super.getJdbcTemplate().getDataSource());
    DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
    // 设置传播行为属性
    transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef);
    try {
        accountDao.out(outer , money);
        // exception
        int i = 1 / 0;
        accountDao.in(inner , money);
        //commit
        dataSourceTransactionManager.commit(status);
    } catch (Exception e) {
        // rollback
        dataSourceTransactionManager.rollback(status);
    }
}

10、Spring声明式事务

Spring声明式事务依赖于Spring AOP,通过配置文件中做相关的事务规则声明或者直接使用@Transactional注解

  • AOP规则声明方式
    这种方式在 applicationContext.xml 文件中配置 aop 自动生成代理,进行事务管理
<?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: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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"></property>
        <property name="username" value="root"></property>
        <property name="password" value="minstone"></property>
    </bean>

    <bean id="accountDao" class="com.example.springframework.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="accountService" class="com.example.springframework.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="dataSource" ref="dataSource"></property>
    </bean>
	<!-- 事务管理器配置 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- xml配置事务 propagation 传播行为isolation 隔离级别-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>
	
	<!-- 配置所有的Service方法都支持事务-->
    <aop:config>
     <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.example.springframework.service..*.*(..))"/>
 </aop:config>

  
</beans>
  • 使用AOP注解方式
    在applicationContext.xml 配置事务管理器,将并事务管理器交予spring,在目标类或目标方法添加注解即可 @Transactional, proxy-target-class设置为 true : 底层强制使用cglib 代理

注意点:@Transactional只能用于public方法,不管是加上类上还是方法上

<!-- 事务管理器配置 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!-- 配置 Annotation 驱动,扫描@Transactional注解的类定义事务  -->
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>

通过上述管理之后,一旦发生异常,两边都会进行事务回滚,没有异常,正常提交事务
![在这里插入图片描述]( https://img-blog.csdnimg.cn/20210401095015295.png?x-oss-process=image/watermark ,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70)

本文例子代码可以在github找到下载链接