MyBatis - XML CRUD 其他查询

1. XML 配置文件

使用 MyBatis 操作数据库的方式有两种:

  1. 注解 (在注解中定义 SQL 语句)
  2. XML 配置文件 (在 XML 文件中定义 SQL 语句)

在上一篇博客中, 已经讲解了如何使用注解操作数据库, 本篇文章来讲解如何使用 XML 进行 MyBatis 开发.

使用 XML 的步骤, 和使用注解的步骤是一致的:

  1. 导入 MyBatis 依赖和 MySQL 驱动的依赖
  2. 配置数据库信息(连接数据库)
  3. 定义接口
  4. 使用 XML 定义 SQL, 操作数据库

1.1 如何配置 XML 文件

1.1.1 步骤一: 导入相关依赖

首先, 需要导入 MyBatis 依赖和 MySQL 驱动依赖, 上篇文章已经讲过, 这里就不再赘述:

 1.1.2 步骤二: 配置数据库信息

然后, 需要配置数据库信息, 进行数据库连接, 上篇博客中也讲了, 也就不再赘述.

# 数据库配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver

 1.1.3 步骤三: 定义 Mapper 接口

定义 Mapper 接口, 接口中包含操作数据库的方法:

1.1.4 步骤四: 配置 XML 文件

既然使用 XML 操作数据库, 那么首先需要新建一个 XML 文件: 
(注意: XML 文件需要建于 resources 包下!!) 

为了提高代码可读性, 可维护性, 通常一个 XML 文件, 对应一个 MyBatis 的 Mapper 接口, 并且建议 XML 文件的命名与 Mapper 接口的命名保持一致:

在 XML 文件中, 需要配置对应 Mapper 接口的路径(接口的完全限定名).

通过 namespace 属性来关联 XML 文件和对应的 Mapper 接口(告诉 MyBatis, 我这个 XML 文件对应的是哪个接口):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--配置 Mapper 接口的路径-->
<mapper namespace="com.study.mybatis.Mapper.UserInfoMapperXML"></mapper>

此外, 还需要在配置文件中, 配置 XML 文件的路径信息, 以便在项目启动时, 能够加载该 XML 文件:

mybatis:# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件mapper-locations: classpath:mapper/*Mapper.xml 

完成以上配置后, 我们就可以通过 MyBatis 以 XML 的形式来操作数据库了.

2. MyBatisX 插件

使用 XML 操作数据库前, 我们需要下载 MyBatisX 插件, 可以帮助我们简化相关操作:

3. 使用 XML 进行 CRUD

3.1 查询数据(<select>)

使用 xml 操作数据库, 不再使用注解定义 SQL, 而是在标签中定义 SQL.

在接口中声明方法, alt + 回车, MyBatisX(插件) 会根据方法的名称, 自动创建相应的标签(由于我的方法名为 selectAll, 所以生成的就是 <select> 标签), 接下来就可以在标签中定义查询相关的 SQL 语句:

其中, 标签中 id 属性的值, 就是对应的 Mapper 接口中方法的名称, 表示在调用该方法时, 执行标签中的 SQL 语句(将 SQL 和方法映射起来).

此外, select 标签中, 还有一个 resultType 属性, 表示查询结果集中, 每条记录的类型:
(只有 select 标签有这个属性, 因为只有查询操作才会返回结果集) 

 创建测试方法, 执行观察结果:

3.1.1 <resultMap> 标签

在上篇博客中讲到, 对于注解, 如果要实现 Java 属性和数据库字段的映射, 有三种方式:

  1. 数据库字段起别名
  2. @Results 注解
  3. 配置驼峰转换

对于 XML 来讲, 第一种和第三种同样适用. 由于我在上篇博客中已经进行了 驼峰转换 的配置, 因此在上文的 select 查询中, 数据库字段和 Java 属性能够映射成功.

但是, @Results 对于 XML 就不适用了, 在 XML 中, 需要使用 <resultMap> 标签进行映射的绑定:

  1. 设置 type 属性, 表示和哪个 Java 类型中的属性进行映射关系的绑定
  2. <id> 子标签, 配置主键的映射关系
  3. <result> 子标签, 配置其他非主键字段的映射关系

子标签中的映射方式, 和 @Results 一模一样: property 表示 Java 属性名, column 表示数据库字段名.

并且, 对 <resultMap> 设置 id 属性, 将其他 select 标签的 resultMap 属性设置为该 id 值, 就可直接使用对应的 <resultMap> 中的映射关系:

这样配置后, 便将数据库字段和 Java 属性成功进行映射了:

注意: 

  1. 如果使用 <resultMap> 的方式进行映射绑定, 即使数据库字段和 Java 属性本来就能成功映射, 但是也建议在 <resultMap> 中将所有 Java 属性和数据库字段全部手动进行映射绑定一遍!!
  2. 使用 XML 传递参数的方式和使用注解传递参数的方式都是一样的, 在 SQL 中都是使用 #{} 接收.

3.2 插入数据(<insert>)

给方法传入一个对象, 将对象中的一些属性值作为插入值, 插入表中:

同样, 若对象使用 @Param 进行重命名, SQL 中的 #{} 依旧需要使用 对象名.属性 的形式来声明参数:

此外, insert 标签中, 同样也是设置 useGenerateKey = true 和 keyProperty = "Java 属性" 的方式来获取新增记录的主键:

3.3 更新数据(<update>)

通过 XML 更新数据, 使用 <update> 标签.

3.4 删除数据(<delete>)

通过 MyBatis 使用 XML 删除数据库表中的记录, 使用 <delete> 标签.


4. 其它查询

4.1 联合查询

4.1.1 准备工作

联合查询需要用到多个表, 我们再创建一个 article_info 表, 并插入数据:

-- 创建文章表
DROP TABLE IF EXISTS article_info;CREATE TABLE article_info (id INT PRIMARY KEY auto_increment,title VARCHAR ( 100 ) NOT NULL,content TEXT NOT NULL,uid INT NOT NULL,delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',create_time DATETIME DEFAULT now(),update_time DATETIME DEFAULT now() 
) DEFAULT charset 'utf8mb4';-- 插入测试数据
INSERT INTO article_info ( title, content, uid ) VALUES ( 'Java', 'Java正文', 1 );

 并且, 创建一个 Java 类和表中字段相映射:

4.1.2 执行查询

联合查询虽然使用了多个表, 但其本质仍然是 select 语句, 我们直接在 <select> 标签中定义 SQL 即可.

将 article_info 和 user_info 两表联合查询, 以 article_info 为基准(左外连接), 联合查询 article_info 中 id = 1, 并且 article_info.uid = user_info.id 的记录:

观察结果, 查询成功: 

注意: 由于此时是联合查询, 定义 SQL 时, 额外接收了一个 user_info 表中的 username 字段, 因此, 我们需要在 ArticleInfo 类中多定义一个 username 属性, 以便将查询结果中的该字段映射到 Java 属性中:

注意: 由于联合查询时的 SQL 为 慢 SQL, 效率极低, 因此在实际工作中要避免使用多表联合查询!!

当查询的数据需要结合多张表时, 应先查询一个表的数据, 再根据得到的数据去查第二个表:

4.2 #{} 和 ${}

#{} 和 ${} 之间, 有三大主要区别:

  1. #{} 为预编译 sql, 提前预留参数位置, 自动识别参数类型, 性能更好.
  2. ${} 为即时 sql, 直接拼接参数.
  3. 由于 ${} 是直接拼接的方式, 如果参数是 String 类型, 需要手动加单引号, 因此存在 SQL 注入的安全问题. 而 #{} 可以自动识别参数类型, 无需加引号, 不存在 SQL 注入问题.

因此, 可以用 #{}, 就用 #{}!!

4.2.1 预编译 SQL, 即时 SQL

我们先将 @Select 注解中的 #{} 修改为 ${}, 观察执行结果:

观察日志发现, #{} 没有直接将参数传入 SQL 中, 而是使用占位符(?)进行占位; 而 ${} 是直接将参数拼接到了 SQL 中.

使用 ${} 传递字符串类型的参数, 继续观察效果:

 我们发现, 程序报错了. 其实原因就是: #{} 是将参数传入 SQL 的占位符中, 而 ${} 是直接将参数拼接到了 SQL 中.

${} 直接将参数拼接到了 SQL 中, 由于没有给字符串类型的参数加引号, 从而导致了 SQL 语法错误:

#{} 可以进行自动类型转换, 自动为字符串类型的参数加引号:

因此, 如果要使用 ${} 传递字符串类型的参数, 需要对 ${} 外手动加上单引号, 以便 MySQL 识别其为一个字符串:

导致以上情况的原因, 就是因为使用 #{} 的 SQL 为预编译 SQL, 使用占位符将参数的位置提前预留好, 收到参数后放入占位符中, 可以对参数自动进行类型转换, 无需手动加引号.

使用 ${} 的 SQL 为 即时 SQL, 采取直接拼接的方式拼接进 SQL 中, 若拼接的是字符串类型, 需要手动加引号.

此外, 由于 #{} 是预编译 SQL, 为参数进行了占位, 因此, MyBatis 会对参数之外的 SQL 进行缓存, 只需关注参数的变化, 根据传入参数的不同加载参数的变化即可, 不需加载参数之外的 SQL, 性能更高.

而 ${} 是即时 SQL, 不管 SQL 发生了什么变化, 每次都会重新加载整个 SQL, 因此性能较低.

 4.2.2 ${}: 存在 SQL 注入!!

上文提到的预编译 SQL 也好, 性能高也好, 其实都不是选择 #{} 作为首选的主要原因.

选择 #{} 而不选择 ${} 主要原因是:

  1. #{} 不存在 SQL 注入问题, 安全.
  2. ${} 由于其需要手动添加引号, 导致其有 SQL 注入的安全问题.

SQL 注入: 通过操作输入的数据来修改事先定义好的SQL语句, 以达到执行代码对服务器进行攻击的方法.

由于没有对用户输入的值进行充分检查, 而 SQL 又是拼接而成, 在用户输入参数时, 在参数中添加⼀些 SQL 关键字, 达到改变 SQL 运行结果的目的, 完成恶意攻击. 

${} 被 SQL 注入的示例如下:

 如上图所示, 攻击人利用 ${} 拼接参数的漏洞, 操作参数信息, 编写 1 = '1' 恒为 true 的 SQL, 导致所有人的信息都被泄露. 

SQL 注入常用于登录界面, 在不知道用户密码的情况下, 利用 SQL 注入, 实现成功登录.

而如果我们使用的是 #{} 进行参数传递, 那么 MyBatis 就会将攻击人编写的所有内容当做参数传入 SQL 中, 不会改变原本 SQL 的结构:

注意:

并非使用 ${} 就出现 SQL 注入问题, 还要结合代码分析, 如果代码进行了处理, 也是可以避免 SQL 注入的.

比如上例, 根据用户名和密码查询用户信息时, 我们可以设置查询结果只能是一条记录, 不能是多条记录, 即: 将返回值 List<UserInfo> 改为 UserInfo.

4.3 排序功能

上文说到, 为了避免 SQL 注入, 在能使用 #{} 的情况下, 尽量使用 #{}.

但是, 在传入某些特定参数时, 必须使用 ${} 才能完成相关操作, 比如: 排序功能.

我们先来看查询排序时正确 SQL :

接下来, 通过 MyBatis 进行排序查询操作. 

当使用 #{} 来传递排序规则的参数(desc/asc)时, 由于在 Java 中, 参数只能通过 String 类型来传入, 所以 MyBatis 会自动给 #{} 中的参数添加单引号(desc => 'desc'), 而正确的 SQL 中此处不应该有引号, 因此导致 SQL 语法错误:

${} 采用直接拼接参数的方式, 不会给参数加引号, 所以, 当实现排序功能时, 必须使用 ${} 进行参数传递:

虽然, 使用 ${} 可能具有 SQL 注入的风险, 但是我们可以通过代码进行校验, 在参数进入 Dao 层之前, 将 SQL 注入的风险排除掉即可. 如上例: 由于排序规则的参数只有 desc 和 asc 两个, 所以我们可以将传入的参数设为枚举类型, 对参数的值进行限制.

4.4 like 模糊查询

先来回顾下 like 查询的语法:

除了排序查询不能使用 #{} 外, like 查询也不能使用 #{} 来传递模糊参数. 如上图, 模糊参数为 "186", 不能将 % 传入到参数中:

此时, ${} 也能够完成: 

但是, 由于此时模糊参数的值是没有限制的, 不能通过设置枚举来校验, 因此, 我们不使用 ${} 来传递模糊参数, 而是使用 MySQL 的内置函数 --- CONCAT(), 进行字符串拼接.

因此, 通过 MyBatis , 在注解中使用 concat 进行字符串拼接, 进行模糊查询:

不仅排序查询和模糊查询传参时不能使用 #{}, 如果传入的是一些非字符串类型的参数(String 类型的参数到 SQL 中, 会自动加上单引号), 也不能使用 #{} 进行传递, 例如: 列名, 表名.

5. 数据库连接池

数据库连接池 负责分配、管理和释放数据库连接, 它允许应用程序重复使用一个现有的数据库连接,
 而不是再重新建立一个.

当需要与数据库建立连接时, 直接从连接池获取 Connection 对象即可, 当执行完毕后, 再将 Connection 放回连接池. 而不需像 jdbc 中频繁的获取和释放连接, 造成资源的消耗.

因此, 数据库连接池的优点如下:

  1. 减少了网络开销
  2. 资源重用
  3. 提升了系统的性能

常见的数据库连接池如下:

  1. C3P0
  2. DBCP
  3. Druid (流行)
  4. Hikari (流行)

SpringBoot 默认使用的数据库连接池是 Hikari:

Hikari是日语 "光" 的意思(ひかり), 以追求性能极致为目标.

Hikari 和 Druid 的性能不分伯仲.

如果想更换数据库连接池为 Druid, 引入 Druid 依赖即可:

// SpringBoot 3.x 版本
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version>
</dependency>
// SpringBoot 2.x 版本
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>

6. MySQL 开发规范

  1. 表名采用蛇形形式命名, 单词一律小写.(MySQL 在 Windows 下不区分大小写, 但在 Linux 下默认区分大小写, 因此一律采用小写)
  2. select 时, 不使用 * 作为查询列表字段
  3. 表必备三字段: id, create_time, update_time

END 

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

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

相关文章

DeepSeek + 飞书多维表格搭建你的高效工作流

众所周知&#xff0c;大模型DeepSeek擅长于处理大规模语言模型推理任务&#xff0c;特别是在成本降低和思维链推理方面表现出色‌&#xff0c;我们一般把大模型必做我们的大脑&#xff0c;但是一个人不能只有大脑&#xff0c;还需要其他输入输出以及操作支配的眼耳鼻嘴手足等。…

跨域-告别CORS烦恼

跨域-告别CORS烦恼 文章目录 跨域-告别CORS烦恼[toc]1-参考网址2-思路整理1-核心问题2-个人思考3-脑洞打开4-个人思考-修正版1-个人思考2-脑洞打开 3-知识整理1-什么是跨域一、同源策略简介什么是源什么是同源是否是同源的判断哪些操作不受同源策略限制跨域如何跨域 二、CORS 简…

基于Django创建一个WEB后端框架(DjangoRestFramework+MySQL)流程

一、Django项目初始化 1.创建Django项目 Django-admin startproject 项目名 2.安装 djangorestframework pip install djangorestframework 解释: Django REST Framework (DRF) 是基于 Django 框架的一个强大的 Web API 框架&#xff0c;提供了多种工具和库来构建 RESTf…

基于多目标向日葵优化算法(Multi-objective Sunflower Optimization,MOSFO)的移动机器人路径规划研究,MATLAB代码

一、机器人路径规划介绍 移动机器人路径规划是机器人研究的重要分支&#xff0c;是对其进行控制的基础。根据环境信息的已知程度不同&#xff0c;路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或局部已知的局部路径规划。随着科技的快速发展以及机器人的大量…

cursor使用经验分享(java后端服务开发向)

前言 cursor是一款基于vscode&#xff0c;并集成AI能力的代码编辑器&#xff0c;其功能包括但不限于代码生成及补全、AI对话&#xff08;能够直接将代码环境作为上下文&#xff09;、即时应用建议等等&#xff0c;是一款面向未来的代码编辑器。 对于vscode&#xff0c;最先想…

【Java学习】异常

一、异常的处理过程 异常类的似复刻变量被throw时&#xff0c;会立即中止当前所在的这层方法&#xff0c;即当层方法里throw异常类似复刻变量之后的语句就不会执行了&#xff0c;如果throw异常语句在当层方法中被try{}包裹&#xff0c;则中止就先发生被包裹在了try{}层&#xf…

双足机器狗开发:Rider - Pi

双足机器狗开发:Rider - Pi https://github.com/YahboomTechnology/Rider-Pi-Robot 项目介绍 Rider - Pi是一款为开发者、教育工作者和机器人爱好者设计的桌面双轮腿式机器人,它基于树莓派CM4核心模块构建,具备多种先进功能和特点: 硬件特性 核心模块:采用树莓派CM4核…

vscode 查看3d

目录 1. vscode-3d-preview obj查看ok 2. vscode-obj-viewer 没找到这个插件&#xff1a; 3. 3D Viewer for Vscode 查看obj失败 1. vscode-3d-preview obj查看ok 可以查看obj 显示过程&#xff1a;开始是绿屏&#xff0c;过了1到2秒&#xff0c;后来就正常看了。 2. vsc…

Nginx 本地配置ssl证书

Nginx 本地配置ssl证书 主要为了本地使用https站点访问测试 本地linux 服务器环境为Centos7 本地安装mkcert证书工具 对于 Debian 或 Ubuntu 系统&#xff0c;你可以使用以下命令安装&#xff1a; sudo apt update sudo apt install mkcert # 验证是否安装成功 mkcert --vers…

Redis相关面试题

Redis相关面试题 缓存三剑客 面试官&#xff1a;什么是缓存穿透 ? 怎么解决 ? 缓存穿透是指查询一个一定不存在的数据&#xff0c;如果从存储层查不到数据则不写入缓存&#xff0c;这将导致这个不存在的数据每次请求都要到 DB 去查询&#xff0c;可能导致 DB 挂掉。这种情况…

Android ChatOn-v1.66.536-598-[构建于ChatGPT和GPT-4o之上]

ChatOn 链接&#xff1a;https://pan.xunlei.com/s/VOKYnq-i3C83CK-HJ1gfLf4gA1?pwdwzwc# 添加了最大无限积分 删除了所有调试信息 语言&#xff1a;全语言支持

机器学习(六)

一&#xff0c;决策树&#xff1a; 简介&#xff1a; 决策树是一种通过构建类似树状的结构&#xff08;颠倒的树&#xff09;&#xff0c;从根节点开始逐步对数据进行划分&#xff0c;最终在叶子节点做出预测结果的模型。 结构组成&#xff1a; 根节点&#xff1a;初始的数据集…

求最小公倍数

求最小公倍数 编程实现&#xff1a; 求最小公倍数。 具体要求&#xff1a; 1、小猫询问“请输入第一个数”&#xff0c;并在列表中求出该数的质因数放入列表A&#xff0c;例如输入12&#xff1b; 2、小猫依次询问“请输入第二个数”&#xff0c;并在列表中求出该数的质因数…

【人工智能】GPT-4 vs DeepSeek-R1:谁主导了2025年的AI技术竞争?

前言 2025年&#xff0c;人工智能技术将迎来更加激烈的竞争。随着OpenAI的GPT-4和中国初创公司DeepSeek的DeepSeek-R1在全球范围内崭露头角&#xff0c;AI技术的竞争格局开始发生变化。这篇文章将详细对比这两款AI模型&#xff0c;从技术背景、应用领域、性能、成本效益等多个方…

C/C++蓝桥杯算法真题打卡(Day1)

一、LCR 018. 验证回文串 - 力扣&#xff08;LeetCode&#xff09; 算法代码&#xff1a; class Solution { public:bool isPalindrome(string s) {int n s.size();// 处理一下s为空字符的情况if (n 0) {return true; // 修正拼写错误}// 定义左右指针遍历字符串int left …

SpringUI高保真动态交互元件库:助力产品原型设计

SpringUI 是一个专为Web设计与开发领域打造的高质量、全面且易于使用的交互元件集合。通过提供一系列预制的、高质量的交互组件&#xff0c;帮助设计师快速构建出功能丰富、界面美观的原型。 ————基础元件&#xff1a; ——————按钮 Button&#xff1a;基础按钮、禁用…

vue+neo4j 四大名著知识图谱问答系统

编号: D039 视频 vueneo4j四大名著知识图谱问答系统 技术架构 vuedjangoneo4jmysql技术实现 功能模块图 问答&#xff1a;基于知识图谱检索、支持图多跳、显示推理路径 姜维的师傅的主公的臣是谁&#xff1a; 马谡 知识图谱&#xff1a;四大名著总共4个图谱 红楼梦图谱 …

学习使用ESP8266进行MQTT通信并在网页上可视化显示

目录 一、工具 二、 流程 三、代码实现 设置MQTT服务器地址 设置服务器和端口号 连接MQTT服务器并订阅话题 回调处理函数 发布数据到话题 四、调试软件使用 打开MQTTx 添加话题 五、网页使用 一、工具 arduino ide esp8266/32单片机 lot物联网网页 MQTTx软件或者m…

大模型应用开发学习笔记

Huggingface 下载模型&#xff1a; model_dirr"G:\python_ws_g\code\LLMProject\session_4\day02_huggingface\transformers_test\model\uer\uer\gpt2-chinese-cluecorpussmall\models--uer--gpt2-chinese-cluecorpussmall\snapshots\c2c0249d8a2731f269414cc3b22dff021…

虚拟卡 WildCard (野卡) 保姆级开卡教程

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 本篇教程为 WildCard 的介绍以及开卡教学&#xff0c;要了解不同平台&#xff08;Grok、Talkatone 等&#xff09;的订阅方式请移步《订阅教程》分类 当我们想要充值国外平台会员时&#xff0c;一般都需要使…