Spring数据库事务处理

这篇具有很好参考价值的文章主要介绍了Spring数据库事务处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据库事务的基本知识

ACID

Spring数据库事务处理

两类丢失更新

事务回滚丢失更新:

Spring数据库事务处理
目前大部分数据库已经通过锁的机制来避免了事务回滚丢失更新。
数据库锁的机制:
锁可以分为乐观锁和悲观锁,而悲观锁又分为:读锁(共享锁)和写锁(排它锁),而数据库实现了悲观锁中的读锁和写锁,而乐观锁则需要开发人员自己实现。

数据库在设计这两种锁的时候,这两种锁间的关系如下:读锁与读锁可以共存,读锁与写锁互斥,写锁与写锁互斥。

比如说,当a操作某条数据时,数据库就会给这条数据加锁,其他人只能查看这条数据,但是却不能操作,只有当a事务提交结束以后,锁被取消了,其他人才可以修改这条数据。

事务提交丢失更新

Spring数据库事务处理
这是高并发编程中需要重点关注的问题,数据库为了压制此类丢失更新,提出了事务之间的隔离级别的概念。

数据库事务的隔离级别

未提交读

允许一个事务读取到另一个事务没有提交的数据。
这种隔离级别可以拥有很好的并发能力,但是对于数据的一致性无法保证,所以适用于追求高并发性,但是对数据一致性要求低的场景。
另外,未提交读的隔离级别会造成脏读的现象:
Spring数据库事务处理

读写提交

一个事务只能读取另一个事务已经提交的数据,而不能读取未提交的数据。
这种隔离级别可以避免脏读的发生。
Spring数据库事务处理
虽然读写提交的隔离级别克服了脏读的发生,但是又会出现不可重复读的现象:
Spring数据库事务处理

可重复读

可重复读的隔离级别是为了克服不可重复读的问题:
Spring数据库事务处理
可重复读的隔离级别虽然克服了不可重复读的问题,但是会引入幻读的问题:
Spring数据库事务处理
幻读和可重复读的区别:
幻读是针对于统计的场景,而可重复读是针对于一条数据而言的。

串行化

为了解决上述的各种问题,数据库提出了串行化的隔离级别。
这种级别下,所有的sql都会按照顺序执行,可以完全保证数据的一致性。

合理使用数据库隔离级别

Spring数据库事务处理
虽然串行化可以解决脏读、不可重复读、幻读等问题,但是它也有一个很显著的特点,就是并发能力低下,这四种隔离级别的并发能力排名如下:
未提交读 > 读写提交 > 可重复读 > 串行化
所以使用时需要根据具体的业务场景来权衡使用。

另外,不同数据对于隔离级别的支持也是不一样的,比如:
Oracle:读写提交、串行化
MySQL:未提交读、读写提交、可重复读、串行化
PG:读写提交、可重复读、串行化

Spring数据库事务简介:

在 Spring 中,事务管理器的顶层接口为PlatformTransactionManager,Spring 还为此定义了一系列的接口和类,它们之间的关系如图所示:
Spring数据库事务处理
当我们引入其它框架时,还会有其它的事务管理器的类,比方说我们引入 Hibernate ,那么 Spring还会提供HibernateTransactionManager 与之对应并给我们使用 。这里我们以 MyBatis 框架为例,去讨论Spring 数据库事务方面的问题,最常用到的事务管理器是 DataSourceTransactionManager 。从上图中可以看到它也是一个实现了接口 PlatfonnTransactionManager 的类。

PlatfonnTransactionManager接口的源码:

下面我们看一下PlatfonnTransactionManager接口的源码:

public interface PlatformTransactionManager {
	// 获取事务
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
	// 事务提交
	void commit(TransactionStatus status) throws TransactionException;
	// 事务回滚
	void rollback(TransactionStatus status) throws TransactionException;
}

可以看到它里面定义了获取事务、提交事务、回滚事务的方法。而mybatis的事务管理器DataSourceTransactionManager实现了PlatformTransactionManager 接口,所以它也拥有了这些方法。

事务的传播行为

所谓传播行为,就是方法之间调用时,事务采取的策略。

在Spring的事务机制中,对于数据库而言存在7中传播行为,它们都被定义在Propagation枚举中,该枚举源码如下:

public enum Propagation {
	// 需要事务,它也是默认的传播行为
	// 如果当前存在事务,就加入到当前事务中一起运行
	// 如果当前不存在事务,就新建一个事务来运行方法
	// 使用频次高
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	// 支持事务
	// 如果当前存在事务,就加入到当前事务中一起运行
	// 如果不存在,就继续采用无事务的方式运行
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	// 必须使用事务
	// 如果当前存在事务,就加入到当前事务中一起运行
	// 如果不存在事务,就抛出异常
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	// 无论当前是否存在事务,都会创建一个新的事务来运行
	// 使用频次高
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	// 不支持事务
	// 如果当前存在事务,将挂起事务运行
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	// 不支持事务
	// 如果存在事务,就抛出异常
	// 如果不存在,就继续采用无事务的方式运行
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	// 事务嵌套
	// 当前方法调用子方法时,如果子方法出现异常,则只回滚子方法执行过的sql,不会回滚当前方法的事务
	// 使用频次高
	NESTED(TransactionDefinition.PROPAGATION_NESTED);
}

REQUIRED、REQUIRES_NEW、NESTED这三种使用频次较高,需要重点关注。

@Transactional注解

Spring对于事务的处理主要采用声明式事务的方式,也就是通过@Transactional注解来进行数据库事务的管理。
下面是它的源码:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
	// 通过bean的name来指定事务管理器
	@AliasFor("transactionManager")
	String value() default "";

	// 和value属性一样
	@AliasFor("value")
	String transactionManager() default "";

	// 设置事务传播行为
	Propagation propagation() default Propagation.REQUIRED;

	// 设置事务隔离级别
	Isolation isolation() default Isolation.DEFAULT;

	// 指定超时时间(单位:秒)
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	// 是否只读事务
	boolean readOnly() default false;

	// 方法在发生指定异常时进行回滚,默认是所有异常都会回滚
	Class<? extends Throwable>[] rollbackFor() default {};

	// 方法在发生指定异常名称时进行回滚,默认是所有异常都会回滚
	String[] rollbackForClassName() default {};

	// 方法在发生指定异常时不进行回滚,默认是所有异常都会回滚
	Class<? extends Throwable>[] noRollbackFor() default {};
	
	// 方法在发生指定异常名称时不进行回滚,默认是所有异常都会回滚
	String[] noRollbackForClassName() default {};
}

可以看到,我们在使用@Transactional注解时,是可以自己设置事务的隔离级别、传播行为、回滚机制等等。

@Transactional实战

下面让我们一起实际应用下@Transactional对于数据库事务的控制。
首先我们创建一个保存数据库的,代码如下:

REQUIRED传播行为测试

@Service
@Slf4j
public class UserServiceImpl implements IUserService {
	......
	
	@Autowired
    @Lazy
    private IUserService userService;
    
	@Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int insert(User user) {
        long oldId = user.getId();
        user.setId(null);
        userMapper.insert(user);
        if(oldId == 10){
            throw new IllegalArgumentException("事务回滚测试" + user.getId());
        }
        user.setUpdatedBy(10288931L);
        return userMapper.update(user);
    }

	@Override
    public boolean save(int times) {
        for(int timesTmp = 1; timesTmp <= times; timesTmp++){
            User user = easyRandom.nextObject(User.class);
            user.setId((long) timesTmp);
            try {
                userService.insert(user);
            }catch (Exception e){
                log.warn("出现了异常:{}", e.getMessage());
            }
        }
        return true;
    }
}

可以看到,我们将方法insert的事务传播行为设置成了REQUIRED,也就是:
// 需要事务,它也是默认的传播行为
// 如果当前存在事务,就加入到当前事务中一起运行
// 如果当前不存在事务,就新建一个事务来运行方法
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

注意:这里调用insert方法时需要通过代理对象调用,因为Spring对于事务的处理是基于AOP实现的,所以如果不通过代理对象调用,就无法触发事务,也就是事务会失效。

然后将日志级别设置为DEBUG,这样就可以看到事务相关的日志了:

logging.level.root=DEBUG
logging.level.org.springframework=DEBUG
logging.level.org.org.mybatis=DEBUG

按照REQUIRED传播行为的特点,此时调用它的save方法是没有事务的,所以insert方法会单独创建自己的事务来运行,然后我们创建测试用的mapper和controller(这两个就省略了,写法都很简单),调用接口,可以看到日志如下:
Spring数据库事务处理
可以看到我新增了5个user信息,日志中也是给insert方法创建了5个事务来运行,这和REQUIRED传播行为的特点是一致的。

此时我们给save方法也加上日志,如下所示:
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

这个时候,对于insert方法来说,就是有事务的环境了,按照REQUIRED传播行为的特点,insert方法就不会自己单独创建事务了,而是沿用save方法的事务,我们再次调用接口,得到日志如下:

Spring数据库事务处理
可以看到只是给save方法创建了事务,并没有给insert方法单独创建事务
Spring数据库事务处理
可以看到insert方法运行时,是加入到原有的事务之中的,这和REQUIRED传播行为的特点是一致的。

REQUIRES_NEW传播行为测试

现在我们将insert方法的事务传播行为设置为REQUIRES_NEW,其特点如下所示:
// 无论当前是否存在事务,都会创建一个新的事务来运行

接下来我们再调用一下接口,得到如下日志:
Spring数据库事务处理
可以看到这里是创建了6个事务,其中有一个是save方法的事务,其他5个都是insert方法自己的事务。这和REQUIRED传播行为的特点是一致的。

NESTED传播行为测试

// 事务嵌套
// 如果没有事务,则创建一个自己的事务
// 如果当前有事务,则创建一个嵌套的事务
// 当前方法调用子方法时,如果子方法出现异常,则只回滚子方法执行过的sql,不会回滚当前方法的事务

更改insert方法的事务如下:
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED, rollbackFor = Exception.class)

同时去掉save方法的事务,再次调用接口,得到如下日志:
Spring数据库事务处理
此时save方法没有添加事务,所以按照NESTED传播行为的特点,insert方法会创建自己的事务。

然后我们再将save方法添加上事务,再次调用接口,得到日志如下:
Spring数据库事务处理
Spring数据库事务处理
因为此时save方法添加了事务,所以insert方法会创建一个嵌套在save方法里面的事务。

有个注意点:对于嵌套事务来说,当前方法调用子方法时,如果子方法出现异常,则只回滚子方法执行过的sql,不会回滚当前方法的事务,这个效果是通过数据库的保存点技术来实现的,至于数据库的保存点技术,可以自行了解一下。

好了,今天就到这里了,感兴趣的小伙伴赶紧去试试吧,拜拜。文章来源地址https://www.toymoban.com/news/detail-469224.html

到了这里,关于Spring数据库事务处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 知识图谱:Neo4j数据库的基本使用——创建张学良的关系谱

            知识图谱(Knowledge Graph)是人工智能的重要分支技术,它在2012年由谷歌提出,是结构化的语义知识库,用于以符号形式描述物理世界中的概念及其相互关系,其基本组成单位是“实体—关系—实体”三元组,以及实体及其相关属性—值对,实体间通过关系相互联

    2024年02月04日
    浏览(69)
  • spring事务和数据库事务是怎么实现

    Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行: 获取连接 Connection con = DriverManager.getConnection() 开启事务con.setAutoCommit(true/false); 执行CRUD 提交事务/回滚事务

    2024年02月13日
    浏览(47)
  • 数据库的事务处理

    在现代信息化时代,大量的数据不断地被创建、修改、删除和查询。 为了保证数据的准确性和一致性,数据库的事务处理成为了必不可少的一个重要组成部分。 本文将针对数据库的事务处理进行详细阐述,包括事务的概念、特性、ACID属性、隔离级别以及事务的并发控制等方

    2024年02月06日
    浏览(58)
  • 【Spring/MySQL数据库系列】数据库事务的特点与隔离级别

    ⭐️ 前面的话 ⭐️ 本文已经收录到《Spring框架全家桶系列》专栏,本文将介绍有关数据库事务的特点以及隔离级别。 📒博客主页:未见花闻的博客主页 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 📌本文由 未见花闻 原创, CSDN 首发! 📆首发时间:🌴2023年5月20日🌴 ✉️坚

    2024年02月05日
    浏览(55)
  • 再相逢【知识图谱】中文医学知识图谱CMeKG,中文产科医学知识图谱COKG | 附:图数据库Neo4j下载安装教学(遇到问题并解决) + Neo4j基本操作

      无论结果如何,请相信那些你努力游向岸的日子都有它的意义。   🎯 作者主页 : 追光者♂ 🔥          🌸 个人简介 : 计算机专业硕士研究生 💖、 2022年CSDN博客之星人工智能领域TOP4 🌟、 阿里云社区特邀专家博主 🏅、 CSDN-人工智能领域新星创作者 🏆、 预期20

    2024年02月14日
    浏览(82)
  • Spring事务管理 | 数据库连接池流程原理分析

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! 事务(Transaction),一般是指要做的或所做的事情。在计算机 术语 中是指访问并可能更新数据库中各种 数据项 的一个程序 执行单元 。事务通常由 高级数据库 操纵语言或编程语言(如SQL,C++或Java)书写的 用户程序

    2024年02月02日
    浏览(50)
  • Spring Boot中操作数据库的几种并发事务方式

    当有多个 并发 事务时,会发生丢失更新异常。来自一个或多个 事务 的更新可能会丢失,因为其他事务会用其结果覆盖它。 让我们通过一个例子来检验一下。考虑以下执行事务的方法。 public void withdraw(Long accountId, double amount) { Account account = accountRepository.findById(accountId).orEl

    2024年01月22日
    浏览(53)
  • 【SQL Server】数据库开发指南(八)高级数据处理技术 MS-SQL 事务、异常和游标的深入研究

    本系列博文还在更新中,收录在专栏:#MS-SQL Server 专栏中。 本系列文章列表如下: 【SQL Server】 Linux 运维下对 SQL Server 进行安装、升级、回滚、卸载操作 【SQL Server】数据库开发指南(一)数据库设计的核心概念和基本步骤 【SQL Server】数据库开发指南(二)MSSQL数据库开发对

    2024年02月07日
    浏览(92)
  • 音频数据处理基本知识学习——降噪滤波基础知识

    滤波是一种信号处理方法,它可以通过消除或减弱信号中的某些频率分量,来实现信号的去噪、去除干扰、增强某些频率成分等目的。常见的滤波方法包括低通滤波、高通滤波、带通滤波等。 降噪是一种信号处理方法,它可以通过消除或减弱信号中的噪声成分,来提高信号的

    2024年02月15日
    浏览(52)
  • Spring Boot使用jasypt处理数据库账号密码等数据加密问题

    在我们业务场景中,项目中的application.yml 配置文件比如数据库账号密码,的各种链接的username,password的值都是明文的,存在一定的安全隐患,可以使用jasypt 加密框架的方式进行明文加密,进而使得我们项目更加安全 注意这里排除了mybatis-plus的包可能是项目中有冲突依赖,

    2024年02月06日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包