MyBatis 返回行数的技术指南:避免常见陷阱

前言

在使用 MyBatis 进行数据库操作时,很多开发者会依赖返回的行数来判断操作是否成功。然而,默认配置下,MySQL 数据库返回的行数可能会导致一些误判。这篇文章将详细探讨 MyBatis 返回行数的原理、常见问题及如何通过配置来避免这些陷阱,从而确保应用程序的可靠性和准确性。此外,我们还将介绍在无法配置 useAffectedRows=true 的情况下,其他可行的解决方案。

MyBatis 默认行为

匹配行数 vs. 受影响行数

MySQL 默认返回的是匹配的行数(matched rows),而不是受影响的行数(affected rows)。这两者的区别在于:

  • 匹配行数:表示查询条件匹配到的行数,无论这些行是否实际被修改。
  • 受影响行数:表示实际被修改的行数,只有在数据发生改变时才会增加。

默认行为的潜在问题

假设我们有以下更新操作:

UPDATE users SET name = 'John Doe' WHERE id = 1;

如果用户的名字已经是 ‘John Doe’,匹配的行数是 1,但受影响的行数是 0。如果我们依赖于返回的行数来判断操作是否成功,这种行为会导致误判。

配置 useAffectedRows=true

为了确保返回的行数是实际受影响的行数,我们需要在 JDBC URL 中配置 useAffectedRows=true

JDBC URL 配置示例

jdbc:mysql://localhost:3306/mydatabase?useSSL=false&useAffectedRows=true

配置 MyBatis

MyBatis 配置文件(mybatis-config.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mydatabase?useSSL=false&useAffectedRows=true"/><property name="username" value="root"/><property name="password" value="password"/></dataSource></environment></environments><mappers><mapper resource="org/mybatis/example/BlogMapper.xml"/></mappers>
</configuration>
Spring Boot 配置
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase?useSSL=false&useAffectedRows=true
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
application.yml
spring:datasource:url: jdbc:mysql://localhost:3306/mydatabase?useSSL=false&useAffectedRows=trueusername: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driver

使用示例

假设我们有一个更新用户信息的方法,我们希望在没有行被更新时抛出异常:

Mapper 接口

public interface UserMapper {@Update("UPDATE users SET name = #{name} WHERE id = #{id}")int updateUser(User user);@Select("SELECT * FROM users WHERE id = #{id}")User findUserById(int id);
}

服务层代码

public void updateUser(User user) throws Exception {// 查询当前版本号User currentUser = userMapper.findUserById(user.getId());user.setVersion(currentUser.getVersion());// 执行更新操作int affectedRows = userMapper.updateUser(user);// 检查是否更新成功if (affectedRows == 0) {throw new Exception("User update failed, no rows affected.");}
}

在这个示例中,通过配置 useAffectedRows=true,我们确保 affectedRows 的值是实际受影响的行数。如果没有行被更新,affectedRows 将是零,从而抛出异常。

无法配置 useAffectedRows=true 的解决方案

如果无法配置 useAffectedRows=true,仍有其他方法可以判断更新操作是否成功。这些方法包括:

方法一:手动校验数据的变化

在执行更新操作前后,手动查询并比较数据是否发生变化。这种方法适合在数据变化不频繁的情况下使用。

示例代码

假设我们有一个 User 表,需要更新用户的名字。我们可以先查询当前名字,再执行更新操作,最后比较更新前后的名字。

public void updateUser(User user) throws Exception {// 查询当前数据User currentUser = userMapper.findUserById(user.getId());String currentName = currentUser.getName();// 执行更新操作int affectedRows = userMapper.updateUser(user);// 更新后再查询一次User updatedUser = userMapper.findUserById(user.getId());String updatedName = updatedUser.getName();// 比较更新前后的数据if (affectedRows == 0 || currentName.equals(updatedName)) {throw new Exception("User update failed, no rows affected.");}
}

方法二:使用触发器

可以在数据库层面创建触发器,当数据发生变化时记录日志或进行其他操作。

示例

假设我们有一个 User 表,我们可以创建一个触发器来记录每次更新操作。

CREATE TABLE UserUpdateLog (id INT AUTO_INCREMENT PRIMARY KEY,userId INT,oldName VARCHAR(255),newName VARCHAR(255),updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);DELIMITER $$
CREATE TRIGGER before_user_update
BEFORE UPDATE ON users
FOR EACH ROW
BEGININSERT INTO UserUpdateLog(userId, oldName, newName)VALUES (OLD.id, OLD.name, NEW.name);
END$$
DELIMITER ;

然后在应用程序中检查触发器日志来判断是否更新成功。

方法三:使用乐观锁

通过在表中添加一个版本号或时间戳字段,每次更新时检查并更新版本号或时间戳,以确保数据的一致性。

示例

假设我们有一个 User 表,包含 version 字段。

Mapper 接口
public interface UserMapper {@Update("UPDATE users SET name = #{name}, version = version + 1 WHERE id = #{id} AND version = #{version}")int updateUser(User user);
}
服务层代码
public void updateUser(User user) throws Exception {// 查询当前版本号User currentUser = userMapper.findUserById(user.getId());user.setVersion(currentUser.getVersion());// 执行更新操作int affectedRows = userMapper.updateUser(user);// 检查是否更新成功if (affectedRows == 0) {throw new Exception("User update failed, possibly due to concurrent modification.");}
}

总结

尽管配置 useAffectedRows=true 是最直接、最简便的方法,但在无法配置的情况下,仍有其他方法可以确保更新操作的成功:

  1. 手动校验数据的变化:适合数据变化不频繁的场景。
  2. 使用触发器:在数据库层记录变化,适合需要详细记录数据变化的场景。
  3. 乐观锁:基于版本号或时间戳来确保数据一致性,适合并发更新的场景。

这些方法可以根据实际业务需求和系统架构选择使用,以确保数据更新操作的可靠性和准确性。通过上述配置和方法,你的应用程序可以更可靠地判断更新操作的成功与否,从而开发出更加健壮和可靠的应用程序。

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

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

相关文章

Linux grep技巧 提取log中的json数据

目录 一. 前提1.1 数据准备1.2 需求1.3 分析 二. 数据提取2.1 提取所有的json数据2.2 提取子项目的全部json数据2.3 提取指定项目的json数据 一. 前提 1.1 数据准备 545-1 2024/07/20 18:20:21 [ERROR] MPX001 eventControlleraupay transactionIdA545 {"event":&q…

【云原生】Kubernetes中的DaemonSet介绍、原理、用法及实战应用案例分析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【LeetCode】填充每个节点的下一个右侧节点指针 II

目录 一、题目二、解法完整代码 一、题目 给定一个二叉树&#xff1a; struct Node { int val; Node *left; Node *right; Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NUL…

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(十)-无人机A2X服务

引言 3GPP TS 23.256 技术规范&#xff0c;主要定义了3GPP系统对无人机&#xff08;UAV&#xff09;的连接性、身份识别、跟踪及A2X&#xff08;Aircraft-to-Everything&#xff09;服务的支持。 3GPP TS 23.256 技术规范&#xff1a; 【免费】3GPPTS23.256技术报告-无人机系…

Mindspore框架循环神经网络RNN模型实现情感分类|(二)RNN模型构建

Mindspore框架循环神经网络RNN模型实现情感分类 Mindspore框架循环神经网络RNN模型实现情感分类|&#xff08;一&#xff09;IMDB影评数据集准备 Mindspore框架循环神经网络RNN模型实现情感分类|&#xff08;二&#xff09;RNN模型构建 Mindspore框架循环神经网络RNN模型实现情…

java代码判断windows还是linux系统

public String getFilePath() {String osName System.getProperty("os.name").toLowerCase();log.info("此系统名称为:{}", osName);// 判断操作系统if (osName.contains("windows")) {return "windows";} else {//linux地址return &…

『大模型笔记』LLM秘密:温度、Top-K和Top-P抽样技术解析!

『大模型笔记』LLM秘密:温度、Top-K和Top-P抽样技术解析! 文章目录 一. LLM秘密:温度、Top-K和Top-P随机采样技术解析!1. 温度(Temperature)参数2. Top-K采样3. Top-P采样4. 总结补充:TopK采样解释:步骤1: 确定Top-K词步骤2: 归一化选择的Top-K词的概率步骤3: 从重新归…

实测!高性能PCIe 5.0 SSD为AI训练贡献了啥?

如今&#xff0c;AI 的火热程度已经不需要解释。算力、算法、数据&#xff0c;构成驱动 AI 技术快速发展的三驾马车&#xff1a; 算力越强&#xff0c;越能够处理更加复杂的训练模型&#xff0c;并加速训练进程&#xff1b;算法越先进&#xff0c;越能高效的从数据中学习并优化…

element-plus的el-table自定义表头筛选查询

文章目录 一、效果二、代码1.代码可直接复制使用 三、问题1.使用el-popover完成筛选框 一、效果 二、代码 1.代码可直接复制使用 <template><div class"page-view" click"handleClickOutside"><el-button click"resetFilters"&…

云服务器2核2G配置可以干嘛?2C2G用来做什么合适?

最便宜的服务器2核2G配置可以做什么&#xff1f;可用来搭建Web网站服务器、小程序服务器、APP后端服务器、图床、开发测试环境、小型电子商务网站及文件存储等使用场景。服务器百科网fwqbk.com举例说明&#xff1a; 搭建个人博客&#xff1a;使用开源WordPress等博客程序系统&…

git使用、git与idea结合、gitee、gitlab

本文章基于黑马程序javase模块中的"git"部分 先言:git在集成idea中,不同版本的idea中页面显示不同,操作时更注重基于选项的文字;git基于命令操作参考文档实现即可,idea工具继承使用重点掌握 1.git概述 git是目前世界上最先进的分布式文件版本控制系统 分布式:将…

照片怎么改大小kb?分享5个改小图片的工具

夏日炎炎&#xff0c;正是出游拍照的大好时节&#xff0c;然而随之而来的问题也让人头疼——手机里的美照越来越多&#xff0c;存储空间却越来越紧张。 不仅如此&#xff0c;上传至社交媒体时&#xff0c;大尺寸的照片常常让加载速度慢如蜗牛&#xff0c;影响了分享的乐趣。 …

java设计模式:02-01-单例模式

单例模式&#xff08;Singleton Pattern&#xff09; 单例模式&#xff08;Singleton Pattern&#xff09;确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问该实例。单例模式通常用于需要全局唯一实例的场景&#xff0c;例如&#xff1a; 日志记录&#xff08;…

[Spring Boot]Protobuf解析MQTT消息体

简述 本文主要针对在MQTT场景下&#xff0c;使用Protobuf协议解析MQTT的消息体 Protobuf下载 官方下载 https://github.com/protocolbuffers/protobuf/releases网盘下载 链接&#xff1a;https://pan.baidu.com/s/1Uz7CZuOSwa8VCDl-6r2xzw?pwdanan 提取码&#xff1a;an…

【毕业论文】| 基于Unity3D引擎的冒险游戏的设计与实现

&#x1f4e2;博客主页&#xff1a;肩匣与橘 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由肩匣与橘编写&#xff0c;首发于CSDN&#x1f649; &#x1f4e2;生活依旧是美好而又温柔的&#xff0c;你也…

mysql + Oracle

eg627. 变更性别 Salary 表&#xff1a; ----------------------- | Column Name | Type | ----------------------- | id | int | | name | varchar | | sex | ENUM | | salary | int | ----------------------- id 是这个表…

SetPriorityClass 函数潜在得32MB工作集内存限制

MSDN&#xff1a; SetPriorityClass function (processthreadsapi.h) - Win32 apps | Microsoft Learn 当人们调用 SetPriorityClass 函数并且将 “dwPriorityClass” 参数设置为&#xff1a;PROCESS_MODE_BACKGROUND_BEGIN 时。 应用程序得工作集&#xff08;Working Set&…

小程序-4(自定义组件:数据、属性、数据监听器、生命周期函数、插槽、父子通信、behaviors)

目录 1.组件的创建和引用 局部引用组件 全局引用组件 组件和页面的区别 组件样式隔离 ​编辑 组件样式隔离的注意点 修改组件的样式隔离选项 data数据 methods方法 properties属性 data和properties属性的区别 使用setData修改properties的值 2.数据监听器 什么…

昇思25天学习打卡营第19天|MindNLP ChatGLM-6B StreamChat

文章目录 昇思MindSpore应用实践ChatGML-6B简介基于MindNLP的ChatGLM-6B StreamChat Reference 昇思MindSpore应用实践 本系列文章主要用于记录昇思25天学习打卡营的学习心得。 ChatGML-6B简介 ChatGLM-6B 是由清华大学和智谱AI联合研发的产品&#xff0c;是一个开源的、支持…

数据结构——线性表(循环链表)

一、循环链表定义 将单链表中终端结点的指针端由空指针改为指向头结点&#xff0c;就使整个单链表形成一 个环&#xff0c;这种头尾相接的单链表称为单循环链表&#xff0c;简称循环链表(circular linked list)。 循环链表解决了一个很麻烦的问题。如何从当中一 个结点出发&am…