11. Mybatis 的增删查改【万字详解】

目录

1. 数据的查找 select

1.1 查询所有数据

1.2 通过 id 进行查找

2. 插入数据 insert

3. 修改数据 update

4. 删除数据 delete

5. $ 和 # 的区别

5.1 SQL 注入 用户登录

6. Spring Boot 打印 SQL 日志

7. order by 排序

8. like 查询

9. 通过页面返回数据

10. 总结


在上篇文章中我们介绍了 mybatis 的相关概念,通过 mybatis 对数据库中的数据进行查找,接下来我们将具体的介绍 Mybatis 的增删查改。

同时本篇文章还详细介绍了 $ 和 # 的区别(重点)。

1. 数据的查找 select

1.1 查询所有数据

通过 workbench,我们可以看到数据库中的现有数据:

 接下来我们在 UserMapper.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 namespace="com.example.demo.mapper.UserMapper"><select id="queryAll" resultType="com.example.demo.model.User">select * from userinfo</select>
</mapper>

接下来在 UserMapper 类中添加查找方法: 

@Mapper
public interface UserMapper {List<User> queryAll();
}

在上述类的方法中右键,选择生成,选择测试,即可生成测试类。接下来,在 UserMapperTest 类中进行自测: 

@Slf4j
@SpringBootTest
class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid queryAll() {List<User> users = userMapper.queryAll();log.info(users.toString());}
}

运行结果如下图所示,可以看到查找到了数据库中现有的所有数据: 

1.2 通过 id 进行查找

首先,我们在 UserMapper.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 namespace="com.example.demo.mapper.UserMapper"><select id="queryAll" resultType="com.example.demo.model.User">select * from userinfo</select><select id="queryById" resultType="com.example.demo.model.User">select * from userinfo where id = ?</select>
</mapper>

接下来在 UserMapper 类中添加查找方法: 

@Mapper
public interface UserMapper {// 查询所有数据List<User> queryAll();// 通过 id 查询数据User queryById(@Param("uid") Integer id);
}

 在上述类的方法中右键,选择生成,选择测试,生成测试类:

 可以看到生成的测试类中包含以下方法:

接下来,我们编写测试的代码:

@Slf4j
@SpringBootTest
class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid queryAll() {List<User> users = userMapper.queryAll();log.info(users.toString());}@BeforeEachvoid setUp() {log.info("before...");}@AfterEachvoid tearDown() {log.info("after...");}@Testvoid queryById() {log.info("queryById...");User user = userMapper.queryById(1);log.info(user.toString());}
}

运行结果如下: 

需要注意的是,当我们只有一个参数时,可以不写注解;但是写了注解时,SQL 语句需要和注解中使用的参数保持一致

当只有一个参数时,不仅可以不写参数,参数的名称也可以不同,以下两条语句均可以成功执行:

User queryById(Integer id);
User queryById(Integer aaa);

2. 插入数据 insert

我们先来看一下数据库中都有哪些数据:

可以看到 id 是自增的,createtime 和 updatetime 都是当前时间,因此我们在插入数据时不用添加。

接下来我们在 UserMapper.xml 文件中添加以下内容:

<insert id="insert">insert into userinfo (username,password,photo)values(#{username},#{password},#{photo})
</insert>

在 UserMapper 类中添加插入方法: 

@Mapper
public interface UserMapper {// 查询所有数据List<User> queryAll();// 通过 id 查询数据User queryById(@Param("uid") Integer id);// 插入数据Integer insert(User user);
}

在上述类的方法中右键,选择生成,选择测试,即可生成测试类,编写测试方法:

@Slf4j
@SpringBootTest
class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid queryAll() {List<User> users = userMapper.queryAll();log.info(users.toString());}@BeforeEachvoid setUp() {log.info("before...");}@AfterEachvoid tearDown() {log.info("after...");}@Testvoid queryById() {log.info("queryById...");User user = userMapper.queryById(1);log.info(user.toString());}@Testvoid insert() {User user = new User();user.setUsername("Danny");user.setPassword("123456");user.setPhoto("123");Integer result = userMapper.insert(user);log.info("insert result:"+ result);}
}

接下来,在 UserMapperTest 类中进行自测: 

 在 workbench 中可以看到数据插入成功:


还可以通过第二种方法插入数据:

Integer insert2(@Param(("userinfo")) User user);
 <insert id="insert2">insert into userinfo (username,password,photo)values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})</insert>

 添加测试方法:

 @Testvoid insert2() {User user = new User();user.setUsername("Danny");user.setPassword("123456");user.setPhoto("123");Integer result = userMapper.insert2(user);log.info("insert result:"+ result);}

可以看到,同样能够运行成功:

需要注意的是,如果使用了 @Param 注解,就必须使用 @Param 注解的命名

获取自增 id

在 UserMapper.xml 文件中添加以下内容:

<insert id="insert2" useGeneratedKeys="true" keyProperty="id">insert into userinfo (username,password,photo)values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})</insert>

在 UserMapperTest 类中添加以下内容:

@Testvoid insert2() {User user = new User();user.setUsername("Danny");user.setPassword("123456");user.setPhoto("123");Integer result = userMapper.insert2(user);log.info("insert result:"+ result + ", 自增id:" + user.getId());}

此时可以看到自增 id 为:5. 

3. 修改数据 update

在修改数据库的数据之前,我们先来看一下此时数据库中的数据:

接下来我们修改 id = 3 的这行数据:

在 UserMapper.xml 文件中添加以下内容:

<update id="update">update userinfo set username = #{username},password = #{password} where id = #{id}</update>

在 UserMapper 类中添加修改方法:

@Mapper
public interface UserMapper {// 查询所有数据List<User> queryAll();// 通过 id 查询数据User queryById(@Param("uid") Integer id);// 插入数据Integer insert(User user);Integer insert2(@Param(("userinfo")) User user);// 修改数据void update(User user);
}

在上述类的方法中右键,选择生成,选择测试,即可生成测试类,编写测试方法:

 @Testvoid update() {User user = new User();user.setId(3);user.setUsername("Jenny");user.setPassword("root123456");userMapper.update(user);}

接下来,在 UserMapperTest 类中进行自测: 

运行成功后,可以看到数据库中 id =3 这一行的数据已经被修改了:

4. 删除数据 delete

我们先来看一下此时数据库中的数据:

接下来我们删除 id = 4 的这行数据:

在 UserMapper.xml 文件中添加以下内容:

<delete id="delete">delete from userinfo where id = #{id}</delete>

在 UserMapper 类中添加修改方法: 

@Mapper
public interface UserMapper {// 查询所有数据List<User> queryAll();// 通过 id 查询数据User queryById(@Param("uid") Integer id);// 插入数据Integer insert(User user);Integer insert2(@Param(("userinfo")) User user);// 修改数据void update(User user);// 通过 id 删除数据void delete(Integer id);
}

在上述类的方法中右键,选择生成,选择测试,即可生成测试类,编写测试方法:

 @Testvoid delete() {userMapper.delete(4);}

接下来,在 UserMapperTest 类中进行自测: 

运行成功后,可以看到数据库中 id =4 这一行的数据已经被删除了:

5. $ 和 # 的区别

我们将通过 id 查询的 xml 文件中的 # 改成 $:

 <select id="queryById" resultType="com.example.demo.model.User">select * from userinfo where id = ${uid}</select>

可以看到依然可以查询到数据: 

接下来我们使用 # 通过名称来查询:

User queryByName(String name);
 <select id="queryByName" resultType="com.example.demo.model.User">select * from userinfo where username = #{username}</select>
@Testvoid queryByName() {log.info("queryByName...");User user = userMapper.queryByName("Jenny");log.info(user.toString());}

可以看到成功查询到数据: 

接下来我们使用 $ 通过名称来查询:

修改 xml 文件:

<select id="queryByName" resultType="com.example.demo.model.User">select * from userinfo where username = ${username}</select>

运行代码后出现以下报错:

是因为 $ 符号传递的参数是没有加引号,直接放在参数中的

那么,如果我们自行添加引号呢?

<select id="queryByName" resultType="com.example.demo.model.User">select * from userinfo where username = '${username}'</select>

此时我们可以看到成功运行了: 

综上,我们来看一下其中的本质原因:

  • #{} :预编译处理(占位)
  • ${}:字符直接替换(字符的内容直接当作 SQL 的一部分来运行,SQL 注入影响程序的安全

 当我们执行以下 SQL 语句时,会发现数据库中的所有数据都被查出来了:

SELECT * FROM userinfo where username = '' or 1 = '1'

5.1 SQL 注入 用户登录

我们先在数据库中保留以下信息:

我们先来测试 # 正确查询:

<select id="queryByNameAndPassword" resultType="com.example.demo.model.User">select * from userinfo where username = #{username} and password = #{password}</select>
User queryByNameAndPassword(@Param("username") String username,@Param("password") String password);
 @Testvoid queryByNameAndPassword() {String username = "admin";String password = "admin";User user = userMapper.queryByNameAndPassword(username,password);log.info(user.toString());}

可以看到查询成功: 

再来测试 # 错误查询,直接修改测试方法:

@Testvoid queryByNameAndPassword() {String username = "admin";String password = "admin123";User user = userMapper.queryByNameAndPassword(username,password);log.info(user==null?null:user.toString());}

可以看到查询为空:

接下来测试 SQL 注入的情况:

@Testvoid queryByNameAndPassword() {String username = "admin";String password = "'or 1 ='1";User user = userMapper.queryByNameAndPassword(username,password);log.info(user==null?null:user.toString());}

可以看到使用错误的 password 显示为 null ,数据没有被查询出来:


接下来测试 $ 正确查询:

<select id="queryByNameAndPassword" resultType="com.example.demo.model.User">select * from userinfo where username = '${username}' and password = '${password}'</select>
@Testvoid queryByNameAndPassword() {String username = "admin";String password = "admin";User user = userMapper.queryByNameAndPassword(username,password);log.info(user==null?null:user.toString());}

可以看到成功查询到: 

接下来我们输入错误的 password :

@Testvoid queryByNameAndPassword() {String username = "admin";String password = "admin123";User user = userMapper.queryByNameAndPassword(username,password);log.info(user==null?null:user.toString());}

可以看到没有查询到数据,显示为 null: 

接下来我们来看 SQL 注入的情况:

@Testvoid queryByNameAndPassword() {String username = "admin";String password = "'or 1 ='1";User user = userMapper.queryByNameAndPassword(username,password);log.info(user==null?null:user.toString());}

 可以看到数据仍然被查询出来了:

那么 SQL 注入简单来说就是将本来不该被查询出来的数据,查询出来了。

6. Spring Boot 打印 SQL 日志

添加配置文件:

mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

可以看到直接将对应的 SQL 语句打印在了控制台:

以上就是 $ 打印的结果,是直接进行替换了。

接下来我们看一下 # 打印的结果:

所以,# 就是进行预编译,采用占位的方式。

7. order by 排序

我们先来看一下此时数据库中的数据:

接下来我们根据 id 进行降序排列:

List<User> queryByOrder(String order);
<select id="queryByOrder" resultType="com.example.demo.model.User">select * from userinfo order by id #{order}</select>
@Testvoid queryByOrder() {List<User> users = userMapper.queryByOrder("desc");log.info(users.toString());}

我们可以看到运行之后的结果并没有像我们想象之中的那样降序排列,而是直接报错:

那么上述这种情况下,就无法使用 # 必须使用 $ 符号才可以。

所以排序时只能使用 $。

修改 xml 文件:

 <select id="queryByOrder" resultType="com.example.demo.model.User">select * from userinfo order by id ${order}</select>

此时可以看到成功进行了降序排列:

但是只要使用了 $ 就存在 SQL 注入的问题,该如何解决呢?

要从根源上解决以上问题就是不让用户输入 SQL 语句,用户只能点击,参数由后端进行拼接,后端在查询之前,对参数进行校验,只能传两个值:desc(降序) 和 asc(升序)。

8. like 查询

我们先来看一下 SQL 语句的 like 查询:

SELECT * FROM userinfo where username like "%m%";

接下来我们使用 # 来查询: 

List<User> queryByLike(String name);
<select id="queryByLike" resultType="com.example.demo.model.User">SELECT * FROM userinfo where username like '%#{name}%';</select>
@Testvoid queryByLike() {List<User> users = userMapper.queryByLike("m");log.info(users.toString());}

可以看到无法正确查询: 

接下来我们改成 $ 进行查询:

 <select id="queryByLike" resultType="com.example.demo.model.User">SELECT * FROM userinfo where username like '%${name}%';</select>

此时,我们可以看到查询到了数据: 

但是使用了 $ 就存在 SQL 注入的问题,因此我们需要使用 MySQL 的一个内置函数。

SELECT * FROM userinfo where username like concat('%','m','%');

可以看到使用以上 SQL 语句也可以进行查询: 

因此,我们修改 xml 文件如下:

<select id="queryByLike" resultType="com.example.demo.model.User">SELECT * FROM userinfo where username like concat('%',#{name},'%');</select>

可以看到使用 concat 函数后可以查询成功:

9. 通过页面返回数据

新建 UserController 类: 

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/selectAll")public List<User> selectAllUser(){return userService.selectAllUser();}
}

 新建 UserService 类:

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public List<User> selectAllUser() {return userMapper.queryAll();}
}

接下来我们运行启动类,即可在页面中看到查找的数据库信息: 

10. 总结

  1. $ 存在 SQL 注入问题,因为 # 是预编译而 $ 是字符替换;
  2. orde by 只能使用 $,通过后端控制参数的输入(只能为 desc 和 asc);
  3. # 直接用于 like 查询会报错,需要使用 MySQL 的内置函数 concat 进行字符连接

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

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

相关文章

【算法基础:动态规划】5.3 计数类DP(整数拆分、分拆数)

文章目录 例题&#xff1a;900. 整数划分解法1——完全背包解法2——分拆数⭐⭐⭐ 例题&#xff1a;900. 整数划分 https://www.acwing.com/problem/content/902/ 解法1——完全背包 容量是 n&#xff0c;物品的大小和价值是 1 ~ n 中的所有数字。 import java.util.*;pub…

SAP ABAP 基础语法超详细

1&#xff0e;表声明 Tables: 表名[,表名]. 声明多个表时可用逗号分隔当你声明了一个数据表的同时&#xff0c;系统也同时自动生成了一个和数据表同名的结构&#xff0c;结构的变量集等于数据表里面的字段。 2&#xff0e;定义变量 Data: v1[(l)] [type t] [decimals d] [v…

【JAVASE】循环结构

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 循环 1. 循环结构1.1 while 循环1.2 bre…

自动驾驶小车

开发自动驾驶小车涉及多个方面&#xff0c;包括硬件和软件。下面是一般情况下开发自动驾驶小车的主要步骤&#xff1a; 确定需求和功能&#xff1a;明确您的自动驾驶小车的目标和应用场景&#xff0c;确定需要的功能和特性&#xff0c;例如感知、决策、控制等。 硬件选择和安…

招商银行秋招攻略和考试内容详解

招商银行秋招简介 招商银行是一家股份制商业银行&#xff0c;银行的服务理念已经深入人心&#xff0c;在社会竞争愈来愈烈的今天&#xff0c;招商银行的招牌无疑是个香饽饽&#xff0c;很多人也慕名而至&#xff0c;纷纷向招商银行投出了简历。那么秋招银行的秋招开始时间是多…

支持向量机(iris)

代码&#xff1a; import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn import svm import numpy as np# 定义每一列的属性 colnames [sepal-length, sepal-width, petal-length, petal-width, class] # 读取数据 iris pd.read_csv(data\\i…

Elasticsearch入门笔记(一)

环境搭建 Elasticsearch是搜索引擎&#xff0c;是常见的搜索工具之一。 Kibana 是一个开源的分析和可视化平台&#xff0c;旨在与 Elasticsearch 合作。Kibana 提供搜索、查看和与存储在 Elasticsearch 索引中的数据进行交互的功能。开发者或运维人员可以轻松地执行高级数据分析…

消息队列总结(4)- RabbitMQ Kafka RocketMQ高性能方案

1.RabbitMQ的高性能解决方案 1.1 发布确认机制 RabbitMQ提供了3种生产者发布确认的模式&#xff1a; 简单模式&#xff08;Simple Mode&#xff09;&#xff1a;生产者发送消息后&#xff0c;等待服务器确认消息已经被接收。这种模式下&#xff0c;生产者发送消息后会阻塞&am…

leetcode-647-回文子串

题意描述&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成…

论文笔记--Skip-Thought Vectors

论文笔记--Skip-Thought Vectors 1. 文章简介2. 文章概括3 文章重点技术3.1 Skip Thought Vectors3.2 词表拓展 4. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;Skip-Thought Vectors作者&#xff1a;Ryan Kiros, Yukun Zhu, Ruslan Salakhutdinov, Rich…

JavaSE复盘2

Collection接口的接口对象集合&#xff08;单列集合&#xff09; List接口&#xff1a;元素按照先后有序保存&#xff0c;可重复 LinkList接口实现类&#xff0c;链表&#xff0c;随机访问&#xff0c;没有同步&#xff0c;线程不安全ArrayList接口实现类&#xff0c;数组&…

element时间选择器的默认值

概览&#xff1a;vue使用element组件&#xff0c;需要给时间选择器设置默认值&#xff0c;场景一&#xff1a;默认时间选择器&#xff0c;场景二&#xff1a;时间范围选择器&#xff0c;开始时间和结束时间。 一、默认时间选择器 实现思路&#xff1a; element组件的v-model绑…

“深入探究Spring Boot:从入门到精通“

标题&#xff1a;深入探究Spring Boot&#xff1a;从入门到精通 摘要&#xff1a;本文将介绍Spring Boot框架的基本概念和特点&#xff0c;以及详细讲解如何使用Spring Boot进行快速开发。通过深入学习Spring Boot的各个方面&#xff0c;读者将能够全面掌握这个强大的开发框架…

忘记安卓图案/密码锁如何解锁?

如何解锁Android手机图案锁&#xff1f;如何删除忘记的密码&#xff1f;Android 手机锁定后如何重置&#xff1f;这是许多智能手机用户在网上提出的几个问题。为了回答这些问题&#xff0c;我们想出了一些简单有效的方法来解锁任何设备而不丢失数据。 忘记手机密码可能会令人恐…

Git基础入门

git基础入门 Git是一款免费、开源的分布式 版本控制系统 &#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 git的安装 下载地址 注意&#xff1a; 不要安装在中文目录不要使用桌…

web服务器(Tomcat)

目录 一、web服务器 1. 常见web服务器 2. web服务器简介 二、 Apache Tomcat服务器 1. Tomcat服务器简介 2. Tomcat服务器基本使用 3. 启动tomcat常见问题 &#xff08;1&#xff09;启动tomcat控制台乱码 &#xff08;2&#xff09;启动tomcat闪退问题 &#xff08;…

DevOps系列文章 之 Springboot单元测试

在没有代码生成工具或尝试一门新的 ORM框架时&#xff0c;当我们希望不去另外写 Service 和 Controller 来验证 DAO 层的代码不希望只通过接口请求的方式来验证时&#xff0c;这时候单元测试的方式就可以帮助我们满足这一需求。 在我们开发Web应用时&#xff0c;经常会直接去观…

爬虫003_pycharm的安装以及使用_以及python脚本模版设置---python工作笔记021

这里我们用ide,pycharm来编码,看一看如何下载 这里我们下载这个社区办,这个是免费的,个人版是收费的 然后勾选以后 安装以后我们来创建一个项目 这里可以选择python的解释器,选择右边的... 这里我们找到我们自己安装的python解释器

Android Hook系统 Handler 消息实现

前言 主线程的Handler 主要依赖于 ActivityThread&#xff0c;Android是消息驱动&#xff0c;比如view的刷新&#xff0c;activity的创建等&#xff0c;如果能打印系统层Handler消息日志&#xff0c;就需要对于系统层的Handler 进行Hook 原理 ActivityThread中 mH对象主要负责…

【雕爷学编程】MicroPython动手做(02)——尝试搭建K210开发板的IDE环境4

7、使用串口工具 &#xff08;1&#xff09;连接硬件 连接 Type C 线&#xff0c; 一端电脑一端开发板 查看设备是否已经正确识别&#xff1a; 在 Windows 下可以打开设备管理器来查看 如果没有发现设备&#xff0c; 需要确认有没有装驱动以及接触是否良好 &#xff08;2&a…