Spring Boot中操作数据库的几种并发事务方式

当有多个并发事务时,会发生丢失更新异常。来自一个或多个事务的更新可能会丢失,因为其他事务会用其结果覆盖它。

让我们通过一个例子来检验一下。考虑以下执行事务的方法。

public void withdraw(Long accountId, double amount) {
Account account = accountRepository.findById(accountId).orElseThrow(() -> {
throw new IllegalStateException("account does not exist: " + accountId);
});

 double newBalance = (account.getBalance() - amount);
if (newBalance < 0) {
throw new IllegalStateException("there's not enough balance");
}
account.setBalance(newBalance);
accountRepository.save(account);
}

只要在任何给定时间点只有单个事务交易,这段代码会按预期工作。

当有多个同时事务时会发生什么?

在这种情况下,上述代码将无法正常工作。线程 1 对 newBalance 所做的修改线程 2 是看不到的。因此,它可能会破坏数据。当我们用 @Transactional 对方法进行注解时,行为不会发生变化。反正它只是定义应用程序的事务边界。

如何防止损失更新异常?
请注意,Spring 默认遵循底层数据存储的隔离级别。Postgres 的默认隔离级别是 READ_COMMITTED。这意味着它只能看到查询开始前提交的数据,而看不到未提交的数据或查询执行期间并发事务提交的更改。

实际上,我们可以通过原子更新操作来解决这个问题!
怎么做?

使用本地更新查询,在数据库中执行直接更新,而不是使用普通 ORM 风格的 "选择、修改和保存"。

@Transactional
public void withdraw(Long accountId, double amount) {

 Double currentBalance = accountRepository.getBalance(accountId);
if (currentBalance < 0) {
throw new IllegalStateException("there's not enough balance");
};
accountRepository.update(accountId, amount);
}

因此,我们使用了自定义更新方法,而不是通常的保存方法。这种更新方法具体是怎样的呢?

下面是在存储库类中添加的更新方法:

@Transactional
@Modifying
@Query(nativeQuery = true,
clearAutomatically=true,
flushAutomatically=true,
value = """
update account
set balance = (balance - :amount)
where id = :accountId """
)
public int update(Long accountId, Double amount);

请注意,我们在这两个方法中都使用了 @Transactional 注解。但它们属于两种不同类型的 Bean:一种来自服务,另一种来自存储库类。因此,更新方法遵循自己的事务定义。

  • @Modifying 会触发注解为 UPDATE 查询的方法,而不是 SELECT 查询。
  • 由于在执行Update修改查询后,实体管理器(EntityManager)中可能会包含过时的实体,所以它不会自动清除它,因此,我们需要明确说明 clearAutomatically=true。
  • 在执行Update修改查询之前,我们还需要自动清除持久化上下文中的任何受管实体。因此使用 flushAutomatically=true。

实现并发安全的更多方法
1、对任何更新使用悲观锁
将下面的注解与现有的事务注解一起使用:
@Lock(LockModeType.PESSIMISTIC_WRITE)

2、使用数据存储特定的咨询锁
在 postgres 中使用 pg_try_advisory_xact_lock 咨询advisory锁,同时使用超时和键(通常是数据库主键)。

将其与retry 重试模板一起使用,这样它就会不断重试,直到获得主键锁的指定超时为止。

示例:

 @Transactional
public void tryWithLock(String key, Duration timeout, Runnable operation) {
lock(key.getKey(), timeout);
// your DB updates run here. operation.run(); 
}

 private void lock(final String key, Duration timeout) { //尝试获取锁,直到超时结束 retryTemplate.execute(retryContext -> {
boolean acquired = jdbcTemplate
.queryForObject("select pg_try_advisory_xact_lock(pg_catalog.hashtextextended(?, 0))", Boolean.class, key);

 if (!acquired) {
throw new AdvisoryLockNotAcquiredException("Advisory lock not acquired for key '" + key + "'");
}
return null;
});
}

您可以直接在 JPA 查询中使用咨询锁,这样会简单得多。

 @Transactional
@Query(value = """
select c
from Account c
where c.id = :accountId
and pg_try_advisory_xact_lock(
pg_catalog.hashtextextended('account', c.id)
) is true """
)
public Account findByIdWithPessimisticAdvisoryLocking(Long accountId);

3、在 POJO 类中使用带版本号的乐观锁

在 POJO 类中添加注释为 @Version 的属性。
然后使用常规的 Spring JPA 查询来获取更新数据。
在将更新写入数据库之前,Spring JPA 会自动检查版本。如果有任何脏写入,事务将中止,客户端可以使用新版本重新尝试事务。这最适合大容量系统。

4、使用悲观的 NO_WAIT 锁定

 @Transactional
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select c from Account c where c.id = :accountId")
@QueryHints({
@QueryHint(name = "javax.persistence.lock.timeout", value = (LockOptions.NO_WAIT + ""))
})
public Account findByIdWithPessimisticNoWaitLocking(Long accountId);

在这种情况下,线程不会因为写操作释放锁而无限期阻塞。相反,它会在上述 javax.persistence.lock.timeout 之后立即返回锁获取失败。如果需要,我们也可以处理此异常并重试事务。

https://www.jdon.com/71719.html

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

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

相关文章

解决防爬虫机制方法(二)

最近为了完成学校的大数据的作业&#xff0c;老师要我们爬一个的网站&#xff0c;里面有还算不错的防爬机制&#xff0c;忙活了几天&#xff0c;总结出一些常见的防爬机制的应对方法&#xff0c;方法均来自个人实战总结&#xff0c;非专业爬虫角度分析 承接上一次讲的方法解决…

OpenAI 悄然更新使用政策

据 The Intercept 报道&#xff0c;OpenAI 近日悄然在其使用政策中删除了「明确禁止将其技术用于军事目的」的措辞。 截至 1 月 10 日&#xff0c;OpenAI 的「使用政策」页面还包括禁止「具有高人身伤害风险的活动」&#xff0c;特别是应用于「武器开发」和「军事和战争」。 而…

MedSegDiff: Medical Image Segmentation with Diffusion Probabilistic Model

摘要 Diffusion probabilistic model (DPM) recently becomes one of the hottest topic in computer vision. Its image generation application such as Imagen, Latent Diffusion Models and Stable Diffusion have shown impressive generation capabilities, which arouse…

Binder 机制 javanative

一&#xff1a;Binder介绍 Binder是一套ipc通信方案 Binder框架定义了四个角色&#xff1a; Server &#xff0c;Client&#xff0c;ServiceManager &#xff08;以后简称SMgr&#xff09;以及Binder驱动。其中Server &#xff0c;Client&#xff0c;SMgr运行于用户空间&#…

你知道什么是Java中的类型强转吗?

强制类型转换 强转存在与父转子的时候&#xff0c;子转父不需要进行强转&#xff0c;如 Object o "hello"; //String类是Object类的子类&#xff0c;无需进行强转类型强转分为两种情况&#xff1a; Ⅰ、向下转型&#xff1a;将父类对象引用转换为子类对象引用&am…

随心玩玩(十三)Stable Diffusion初窥门径

写在前面&#xff1a;时代在进步&#xff0c;技术在进步&#xff0c;赶紧跑来玩玩 文章目录 简介配置要求安装部署下载模型启动ui插件安装教程分区提示词插件Adetailer插件提示词的分步采样采样器选择采样器的收敛性UniPC采样器 高分辨率修复 (Hires. fix)图生图ControlNet介绍…

jetson nano VNC远程桌面配置及使用(nomachine)

文章目录 jetson nano VNC远程桌面配置及使用1.Nomachine介绍2.在电脑端安装Nomachine3.在Jetson Nano端安装Nomachine4.电脑端连接及使用步骤5.修改分辨率6.NoMachine常见问题6.1 黑屏6.2 白屏 jetson nano VNC远程桌面配置及使用 本节适用于Jetson Nano没有单独显示器可以给…

正则验证封装

正则表达式常用符号说明: .是除换行以外的所有任意符号 \s空白符号 \S除空白符号以外的任意符号 \w字母、数字、下划线 \W 除字母、数字、下划线以外的其他任意符号 \d 数字(0----9) \D 除数字以外的任意其他符号 ^ 字符串开始 $ 字符串结束 * 匹配0到无数次(匹配的是符号前边的…

2023年跨国企业如何实现跨境数据传输合规化(上)

一、什么是数据跨境传输&#xff1f; 首先了解一个概念&#xff0c;什么是数据跨境传输&#xff1f; 数据跨境传输简单概括就是指信息通过互联网等网络媒介&#xff0c;在跨国企业之间进行传递和交换的过程。 有一则官方网站关于全球化数字化的数据统计&#xff1a;仅2019 年…

MyBatisPlus学习笔记二

接上&#xff1a;MyBatisPlus学习笔记一&#xff1a; MyBatisPlus学习笔记一-CSDN博客 1、条件构造器 MyBatisPlus支持各种复杂的where条件&#xff0c;可以满足日常开发的所有需求。 1.1、集成体系 1.2、实例 查询 lambda查询 更新 1.3、总结 2、自定义sql 我们可以利用MyB…

强化学习AI构建实战 - 基于“黄金点”游戏(二)

服务端接口 为了让大家的AI可以顺利地进行游戏&#xff0c;并验证我们对策略和AI的一些实现&#xff0c;我们需要一些基础设施来帮助我们完成一些工作。这些工作包括游戏回合的控制、参与者之间的数据同步、游戏数据的储存等功能。 为了简化这些基础工作&#xff0c;以便大家…

VM虚拟化——物理机迁移至虚拟化

一、安装迁移工具 VMware vCenter Converter Standalone 【安装向导】 【最终用户专利协议】 【最终用户许可协议】 【安装位置】 【安装类型】默认本地安装 【用户体验设置】 【准备安装】 二、迁移 【转换机器】 【源主机】 填ip、用户名和密码 最好是用administ…

训练营四十八天 | 198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

198.打家劫舍 不要忘记空数组和数组长度为1的情况单独考虑 和前两个状态有关 代码随想录 class Solution {public int rob(int[] nums) {if(nums null && nums.length 0) return 0;if(nums.length 1) return nums[0];int[] dp new int[nums.length];//int[] dp …

易观察|2024年金融科技新趋势揭秘,大模型发展有望落地

2023年&#xff0c;是金融科技市场持续向好的一年&#xff0c;受政策和市场的双重推动&#xff0c;金融科技企业信心大增&#xff0c;未来发展信心指数平均分提升到82.8&#xff0c;创下近三年来的新高。而随着市场、政策和经济的企稳预期&#xff0c;以及GPT大模型技术的迅猛发…

【打卡】牛客网:BM90 最小覆盖子串

题目&#xff1a; BM65 最长公共子序列(二)&#xff1a; 找二者的相同部分&#xff0c;该部分对于二者可以不连续排列的&#xff08;但是是有序的&#xff09;。 BM66 最长公共子串 找二者的相同部分&#xff0c;该部分对于二者是连续排列的。 本题&#xff1a;BM90 最小覆盖…

什么是DDOS高防ip?DDOS高防ip是怎么防护攻击的

随着互联网的快速发展&#xff0c;网络安全问题日益突出&#xff0c;DDoS攻击和CC攻击等网络威胁对企业和网站的正常运营造成了巨大的威胁。为了解决这些问题&#xff0c;高防IP作为一种网络安全服务应运而生。高防IP通过实时监测和分析流量&#xff0c;识别和拦截恶意流量&…

PattPatel-“Introduction to Computing Systems“(4)期末样卷题目解析:C语言递归

C语言的递归我觉得最主要的还是要把Patt&Patel的部分好好理解下&#xff08;因为有和硬件结合的部分&#xff09;&#xff0c;但因为今天就考试&#xff08;来不及做这样的事情&#xff09;&#xff0c;先把之前模拟卷的题目给尝试弄明白&#xff0c;然后考完试之后继续学习…

vue-ESlint代码规范及修复

1. 介绍 ESLint:是一个代码检查工具&#xff0c;用来检查你的代码是否符合指定的规则(你和你的团队可以自行约定一套规则)。 在创建项目时&#xff0c;我们使用的是 JavaScript Standard Style 代码风格的规则。 规范网址&#xff1a;https://standardjs.com/rules-zhcn.htm…

美易官方:仅差8万辆,特斯拉2023年全球销量接近奥迪

特斯拉在2023年全球销量仅差8万辆就能追平奥迪&#xff0c;这一消息引起了汽车行业的广泛关注。作为电动汽车市场的领头羊&#xff0c;特斯拉一直以创新的技术和设计引领着汽车行业的发展。而奥迪作为传统豪华汽车品牌&#xff0c;也在不断探索和尝试新的发展路径。 特斯拉在20…

力扣-刷MySQL(详细解析)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…