Hibernate多事务同时调用update(T t) ,字段被覆盖问题

前言

今天现网有个订单卡单了,经过排查发现没有任何异常日志,根据日志定位发现本应该更新的一个状态,也sql肯定执行了(使用了Hibernate的ORM框架),但是数据库里面的状态没有更新。大概逻辑如下

String hql= from orderInfo where id="PTTest";
OrderInfo orderInfo=commomDao.findSingle(hql);
orderInfo.setStatus("completed");
commonDao.update(orderInfo);

根据日志能确认,此段代码肯定是被执行了,但是数据库表的Status依旧是processing。

问题排查

这种情况,怀疑是在Status更新成completed之后,其他的事务把Status更新了成processing了。想佐证这种猜测,直接看数据库的bin log日志是最直观的。找大数据组同事找了日志时间点周围的bin log日志

确实在更新completed之后,又被其他事务更新成processing。后来从ELK中查看到对应requestLog日志,发现确实有请求更新了Status。

问题解决

直接用update 语句更新需要更新的字段

update orderInfo set Status="completed" where id="PTTest";

这种写法,更偏向Mybatis了,违背了Hibernate的思想了。如果我们不想写sql,还是希望以对象形式更新数据。我们也可以写一个方法

    public void update(Class<T> clazz, K id, Map<String, Object> columns) {
        if (CollectionUtils.isEmpty(columns.keySet())) {
            throw new ParameterException(RetCode.HIBERNATE_COLUMNS_ARGS_LENGTH_ERROR);
        }
        StringBuilder hqlBuilder = new StringBuilder(" update " + clazz.getSimpleName() + " set ");
        columns.keySet().forEach(k -> hqlBuilder.append(k).append(" = :").append(k).append(", "));
        hqlBuilder.append(" updateTime = :updateTime ");
        hqlBuilder.append(" where id = :id ");
        commonDao.execute((ExecuteCallback<Object>) session -> {
            Query query = session.createQuery(hqlBuilder.toString());
            columns.keySet().forEach(k -> query.setParameter(k, columns.get(k)));
            query.setParameter("updateTime", new Date());
            query.setParameter("id", id);
            return query.executeUpdate();
        });
    }

那么我们在调用的时候

Map<String, Object> params = new HashMap<>();
params.put("status", "completed");
update(OrderInfo.class, orderInfo.getId(), params);

其实update方法内部还是在拼凑sql。

使用@version注解

使用@Version注解是Hibernate提供的一种乐观锁机制,用于解决并发更新时的数据覆盖问题。乐观锁是一种锁策略,它假设多个事务在同一时间对同一条记录进行更新的可能性较低,因此不采用在更新前加锁的方式(即悲观锁),而是在更新时检查版本号,如果发现版本冲突,则认为有其他事务已经更新了该记录,此时可以选择重新获取最新数据并再次尝试更新,或者抛出异常告知用户。当使用@Version注解标注在实体类的某个属性上时,Hibernate会在每次读取该记录时自动将版本号加1,并在更新时检查当前版本号与数据库中的版本号是否一致。如果版本号不一致,说明有其他事务已经更新了该记录,此时Hibernate会抛出OptimisticLockException异常,从而避免了数据覆盖的问题。

使用@Version注解的步骤如下:

  1. 选择版本号字段:选择一个合适的字段作为版本号字段,通常是一个数值类型或时间戳类型的字段。
  2. 添加注解:在实体类中的版本号字段上添加@Version注解。
  3. 处理异常:在业务层捕获OptimisticLockException异常,根据实际需求进行处理,例如提示用户重新操作或重试更新。

通过使用@Version注解,可以在不使用显式锁的情况下,依靠版本号机制来控制并发更新,从而避免数据覆盖的问题。但需要注意的是,乐观锁并不能保证数据的绝对一致性,因为在检查版本号和实际更新之间仍然存在短暂的时间窗口,极端情况下仍然可能发生并发更新的问题。因此,在使用乐观锁时需要仔细评估并发风险,并结合实际业务需求进行选择。

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

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

相关文章

Could not resolve all files for configuration

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 未经允许不得转载 目录 一、导读二、概览三、 推荐阅读 一、导…

《前端防坑》- JS基础 - 你觉得typeof nullValue === null 么?

问题 JS原始类型有6种Undefined, Null, Number, String, Boolean, Symbol共6种。 在对原始类型使用typeof进行判断时, typeof stringValue string typeof numberValue number 如果一个变量(nullValue)的值为null&#xff0c;那么typeof nullValue "?" const u …

C++的并发世界(十一)——线程池

0.线程池的概念 1.线程池使用步骤 ①初始化线程池&#xff1a;确定线程数量&#xff0c;并做好互斥访问&#xff1b; ②启动所有线程 ③准备好任务处理基类&#xff1b; ④获取任务接口&#xff1a;通过条件变量阻塞等待任务 2.atomic原子操作 std:atomic是C11标准库中的一个…

QT学习day4

widget.h #define WIDGET_H #include <QWidget> #include <QTime>//时间类 #include <QTimerEvent>//定时器类 #include <QPushButton>//按钮类 #include <QTextToSpeech>//语音播报 QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_E…

After Effects 2024 中文激活版 高效工作流程与创新的视觉特效 mac/win

After Effects 2024是Adobe公司推出的一款专业视频特效制作软件&#xff0c;广泛应用于电影、电视、动画等媒体制作领域。它凭借强大的功能和灵活的操作&#xff0c;帮助用户轻松创建电影级影片字幕、片头和过渡效果&#xff0c;以及实现立体效果和动态场景的切换。 同时&#…

机器学习中的激活函数

激活函数存在的意义&#xff1a; 激活函数决定了某个神经元是否被激活&#xff0c;当这个神经元接收到的信息是有用或无用的时候&#xff0c;激活函数决定了对这个神经元接收到的信息是留下还是抛弃。如果不加激活函数&#xff0c;神经元仅仅做线性变换&#xff0c;那么该神经网…

【电路笔记】-逻辑或非门

逻辑或非门 文章目录 逻辑或非门1、概述2、晶体管逻辑或非门3、数字逻辑或非门类型4、通用或非门逻辑或非门是数字逻辑或门与反相器或非门串联的组合。 1、概述 或非(Not-OR)门的输出通常为逻辑电平“1”,并且仅当其任何输入处于逻辑电平“1”时才变为“低”至逻辑电平“0”…

蓝桥杯 每日2题 day4

碎碎念&#xff1a;好难好难&#xff0c;&#xff0c;发呆两小时什么也写不出来&#xff0c;&#xff0c;&#xff0c;周六大寄了 10.阶乘约数 - 蓝桥云课 (lanqiao.cn) 暴力跑了两个小时没出来结果&#xff0c;&#xff0c;去看题解要用数学&#xff1a;约数定理&#xff0c…

谈谈什么是 Redis

&#x1f525;博客主页&#xff1a;fly in the sky - CSDN博客 &#x1f680;欢迎各位&#xff1a;点赞&#x1f44d;收藏⭐️留言✍️&#x1f680; &#x1f386;慢品人间烟火色,闲观万事岁月长&#x1f386; &#x1f4d6;希望我写的博客对你有所帮助,如有不足,请指正&#…

Docker功能简单学习及使用

Docker是什么 Docker是一个快速构建&#xff0c;运行&#xff0c;管理应用的工具 传统基于linux安装程序较为复杂繁琐&#xff0c;使用docker可以快速的进行项目部署和管理 镜像与容器 Docker进行安装应用时&#xff0c;会自动搜索并下载应用镜像(image)。镜像不仅包含应用本…

GitLab教程(一):安装Git、配置SSH公钥

文章目录 序一、Git安装与基本配置&#xff08;Windows&#xff09;下载卸载安装基本配置 二、SSH密钥配置 序 为什么要使用代码版本管理工具&#xff1a; 最近笔者确实因为未使用代码版本管理工具遇到了一些愚蠢的问题&#xff0c;笔者因此认为代码版本管理工具对于提高团队…

魔法阵-蓝桥每日真题

0魔法阵 - 蓝桥云课 (lanqiao.cn) #include <iostream> #include <queue> #include <vector> #include <cstring> #include <algorithm>using namespace std;#define x first #define y second const int N 1010; const int inf 1e4; vector&…

【汇编语言实战】求两组给定数组最大值

C语言描述该程序流程&#xff1a; #include <stdio.h> int main() {int arr1[]{11,33,23,542,12233,5443,267,456,234,453};int arr2[]{21,123,432,45,234,534,6517,678,879,1};int maxarr1[0];for(int i1;i<10;i){if(arr1[i]>max){maxarr1[i];}}printf("%d\…

蓝桥杯练习系统(算法训练)ALGO-956 P0702 strcmp 函数

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 在C语言中&#xff0c;有一个strcmp函数&#xff0c;其功能是比较两个字符串s1和s2。请编写一个你自己的字符串比较函数my_strcmp&…

设计模式——装饰器模式09

装饰器模式&#xff1a;是在原有基础上进行装饰&#xff08;无修改原来代码&#xff09;&#xff0c;来添加新的功能。 例如下面对普通耳机进行装饰。 设计模式&#xff0c;一定要敲代码理解 修饰对象&#xff08;抽象&#xff09; /*** author ggbond* date 2024年04月07日…

ShardingSphere-ShardingSphere读写分离和数据脱敏

文章目录 一、读写分离1.1 读写分离1.2 读写分离应用方案1.3 分表+读写分离1.4 分库分表+读写分离二、ShardingSphere-JDBC读写分离2.1 创建SpringBoot并添加依赖2.2 创建实体类2.3 创建mapper2.4 配置读写分离2.5 测试测试插入数据测试读测试事务一致性测试负载均衡一、读写分…

appium图像识别之images-plugin插件

在进行App自动化测试的过程中&#xff0c;由于页面的复杂性&#xff0c;需要根据页面的技术实现&#xff0c;通过上下文来切换不同的定位类型&#xff0c;给定位元素的稳定性带来了不小的挑战&#xff1b;图像识别技术则不依赖于页面本身是用什么技术实现的&#xff0c;只要能识…

打卡--MySQL8.0 四 (索引及执行计划)

一、索引的创建与使用 1、索引的分类 MySQL的索引包括普通索引、唯一性索引、全文索引、单列索引、多列索引和空间索引等。 &#xff08;1&#xff09;从 功能逻辑 上说&#xff0c;索引主要有 4 种&#xff0c;分别是普通索引、唯一索引、主键索引、全文索引。 &#xff08;…

【微服务】------服务注册

在 微服务的基建工作 中提到过&#xff0c;在云原生、微服务时代&#xff0c;如果还是手动修改服务地址&#xff0c;是几乎不可完成的工作&#xff0c;需要一种机制完成自动上报和获取服务地址的支撑组件&#xff0c;可以保障服务的快速上线和下线&#xff0c;这就是服务注册/发…

Netty学习——高级篇2 Netty解码技术 备份

接上篇&#xff1a;Netty学习——高级篇1 拆包 、粘包与编解码技术&#xff0c;本章继续介绍Netty的其他解码器 1 DelimiterBasedFrameDecoder分隔符解码器 DelimiterBasedFrameDecoder 分隔符解码器是按照指定分隔符进行解码的解码器&#xff0c;通过分隔符可以将二进制流拆分…