【 Spring Mybatis 复杂的查询操作 】

news/2024/4/18 9:38:16/文章来源:https://blog.csdn.net/qq_64317046/article/details/130322813

文章目录

  • 引言
  • 一、参数占位符 #{} 和 ${}
  • 二、SQL 注入
  • 三、like 模糊查询
  • 四、返回类型:resultType 和 resultMap
  • 五、多表查询

引言

前面我们已经学会了使用 Mybatis 进行增,删,改操作,也实现了简单的查询操作 !下面我们就来学习更加复杂的查询操作,大体步骤与前面的操作类似 !

一、参数占位符 #{} 和 ${}

  1. #{}:预编译处理
  2. ${}:字符直接替换

预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement 的set ⽅法来赋值。直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 直接替换成变量的值

xml 中相应的查询语句仅修改了占位符,却达到了不同的效果:

在这里插入图片描述

由此得出结论,#{}适用于所有类型的参数匹配,而${}在某些场合不适合String类型参数匹配,但如果参数是一个 SQL 关键字时就不需要单引号 ,此时 ${} 直接替换关键字就起到了重要作用!

如下 ${} 重要性示例:

当你在淘宝购买东西时,你需要根据价格高低来筛选商品,此时SQL关键字 desc/asc 就起到了作用,如查询语句:select * from goodsInfo order by price desc; 就达到了根据价格从低到高查询 !

在这里插入图片描述

上述你只需要将参数 desc 或 asc 传递就可以 !如果尝试使用 #{} 来获取参数,我们看效果 !以下为伪代码

实现 mapper 接口:

//${} 参数直接替换 场景演示:根据价格进行倒序或正序
public List<GoodsInfo> getGoodsList(@Param("order") String order);

xml 文件实现:

select * from goodsinfo order by price #{order} 

当传递参数为 desc 时,实际的效果:

select * from goodsinfo order by price ‘desc’;

我们尝试在数据库中去运行此代码,会发现它出错了!原因显而易见,此时的 desc 加上了单引号,它并不是一个SQL关键字。所以如果使用#{}预处理的话就会把 sql关键字如 desc 加上单引号,当成一个value值而不是一个sql关键字,就造成了查询失败 !

此时 ${} 的作用就派上用场了,如下将 #{} 替换为 ${},看效果:

select * from goodsinfo order by price desc;

以上就是一条正确的 SQL 语句,所以 ${} 直接替换成了 desc,没有加单引号 !


二、SQL 注入

上面我们讲到了 ${} 的一个重要作用,当参数为 SQL 关键字时,我们需要使用 ${} 直接替换参数!但是直接替换参数又会产生严重的弊端,如果传来的参数不合法可能会对数据库中的数据造成严重的威胁,如 SQL 注入问题 !如下:

mapper 接口实现:

//${}安全漏洞: SQL注入案例:
public UserInfo login(@Param("username") String username,@Param("password") String password);

xml 文件实现:

<!-- SQL注入案例:登录功能-->
<select id="login" resultType="com.example.demo.model.UserInfo">select *from userinfowhere username = '${username}' #手动加上单引号and password = '${password}';
</select>

生成测试代码:

    @Test//${}安全漏洞: SQL 注入问题演示void login() {//1、能够通过正确的账号密码得到用户信息
//        String username = "admin";
//        String password = "admin";
//        UserInfo userInfo = userInfoMapper.login(username,password);
//        log.info("用户信息:" + userInfo);//2、输入不正确的密码,也能够得到用户信息   SQL 注入问题String username = "admin";String password = "' or 1='1";//此时 1=’1‘ 相当于一个万能钥匙,能够获取任何一个用户信息UserInfo userInfo = userInfoMapper.login(username,password);log.info("用户信息:" + userInfo);}

上述通过正确的账号密码能够获取到用户信息 ! 但是我们尝试输入不正确的密码时,如 ’ or 1='1
我们观察测试结果,如下:

在这里插入图片描述

我们发现,虽然输入了不正确的密码,但还是得到了该用户正确的信息,这就是一个非常恐怖的事情了 !!

这就是 SQL 注入问题,此时 1=’1‘ 相当于一个万能钥匙,能够获取任何一个用户信息 !! 那如何解决避免这种问题呢?第一,⽤于查询的字段,尽量使⽤ #{} 预查询的⽅式,它的安全性能更高。第二,不得不去使用${}时,就务必在业务代码中对传递的参数进行安全效验 !!


三、like 模糊查询

当我们需要使用 like 进行模糊查询时,我们的查询语句如下:

select * from userinfo where username like '%冯%';

我们看查询结果:

在这里插入图片描述

所以 ‘%冯%’ 表示,查询表中 username包含 冯 的所有数据 !!

那我们将 冯 作为参数传递,使用#{} 占位符来观察效果,如下

mapper 接口实现:

public List<UserInfo> getUserByName(@Param("username") String username);

xml 文件实现:

<select id="getUserByName" resultType="com.example.demo.model.UserInfo">select *from userinfowhere username like '%#{username}%';
</select>

相应的测试代码:

    @Test// 特殊的 like 查询void getUserByName() {String username = "冯";List<UserInfo> userInfos = userInfoMapper.getUserByName(username);log.info("用户信息:" + userInfos);}

观察运行结果如下:

在这里插入图片描述

我们发现运行失败,所以 like模糊查询 使⽤ #{} 会报错,其具体实现的 SQL 相当于如下:

select * from userinfo where username like '%'username'%';

此时就会把 username 用单引号引起来,当成一个字符 !而使用 ${} 在业务层的值又不能穷举,所以此时单一使用参数占位符就失效了,那我们该如何解决这个问题呢?

可以考虑使⽤ mysql 的内置函数 concat() 来处理拼接,实现代码如下:

xml 文件实现:

<select id="getUserByName" resultType="com.example.demo.model.UserInfo">select *from userinfowhere username like concat('%', #{username}, '%');
</select>

执行上述测试类,观察运行结果:

在这里插入图片描述

上述代码就运行成功了,而 concat(‘%’,#{username},‘%’) 就达到了 %?% 效果,有了正确的SQL语句:select * from userinfo where username like ‘ %冯% ’;


四、返回类型:resultType 和 resultMap

如果是增、删、改返回的是受影响的⾏数,那么在 mapper.xml 中是可以不设置返回的类型的,如下图所示:
在这里插入图片描述

然⽽即使是最简单查询⽤户的名称也要设置返回的类型,否则会出现如下错误

查询不设置返回类型的错误示例演示:

controller 代码:

@RequestMapping("/getname")
public String getNameById(Integer id) {return userService.getNameById(id);
}

xml 文件实现:

<select id="getNameById">select username from userinfo where id=#{id}
</select>

使用 postman 测试如下:

在这里插入图片描述

显示运⾏了⼀个查询但没有找到结果映射,也就是说对于 查询标签来说⾄少需要两个属性:

  1. id 属性:⽤于标识实现接⼝中的那个⽅法;
  2. 结果映射属性:结果映射有两种实现标签:< resultMap > 和 < resultType >

4.1 返回类型:resultType

绝⼤数查询场景可以使⽤ resultType 进⾏返回,如下代码所示:

<!-- select id中的id表示要实现接口中的具体方法  resultType:表示查询返回的类型,也就是开头定义的实体类-->
<select id="getUserById" resultType="com.example.demo.model.UserInfo">select *from userinfowhere id = #{id};
</select>

它的优点是使⽤⽅便,直接定义到某个实体类即可

4.2 返回字典映射:resultMap

resultMap 使⽤场景:

  1. 字段名称和程序中的属性名不同的情况,可使⽤ resultMap 配置映射
  2. ⼀对⼀和⼀对多关系可以使⽤ resultMap 映射并查询数据

字段名和属性名不同的情况:

表中的字段名为: username
在这里插入图片描述

程序实体类中的属性为:name
在这里插入图片描述

当我们使用 resultType 去查询用户时,如下:

<select id="getUserById" resultType="com.example.demo.model.UserInfo">select *from userinfowhere id = #{id};
</select>

使用 postman 测试得:

在这里插入图片描述

显而易见, resultType 在这里已经失效了,这个时候就可以使⽤ resultMap 了,resultMap 的使⽤如下:

在这里插入图片描述

代码实现:

    <!-- resultMap id是给它取一个名字,type 表示要映射的实体类 --><resultMap id="BaseMap" type="com.example.demo.model.UserInfo"><!-- 主键映射 --><id column="id" property="id"></id><!-- 普通属性映射:column为数据库中的字段名,property为程序中的属性,下面可以解决不匹配问题--><result column="username" property="name"></result><result column="password" property="password"></result><result column="photo" property="photo"></result><result column="createtime" property="createtime"></result><result column="updatetime" property="updatetime"></result><result column="state" property="state"></result></resultMap><select id="getUserById" resultMap="BaseMap">select *from userinfowhere id = #{id};</select>

注意:

单表查询时,resultMap里面的字段映射不写全是可以查到用户信息的,因为自己对自己已经知根知底,知道哪些字段设置了,但是多表查询时字段必须要设置完整

再次使用 postman 测试得:

在这里插入图片描述

即使属性名和字段名不一样的情况,上述也通过 resultMap 查询到了相应的信息 !而 ⼀对⼀和⼀对多关系也可以使⽤ resultMap 映射并查询数据,下面我们就在多表查询中来学习 !!


五、多表查询

⼀对⼀和⼀对多关系也可以使⽤ resultMap 映射并查询数据 !

一对一的关系映射( ⼀篇⽂章只对应⼀个作者 )

我们实现 根据文章 id 来查询文章信息,里面包含了 作者信息 !

  1. 定义文章实体类 ArticleInfo,里面包含了属性 userInfo
@Data
public class ArticleInfo {private int id;private String title;private String content;private String createtime;private String updatetime;private int uid;private int rcount;private int state;private UserInfo userInfo;//一篇文章有一个作者  定义一个外键
}
  1. 实现 ArticleInfoMapper 接口
@Mapper
public interface ArticleInfoMapper {//根据文章 id 获取文章public ArticleInfo getArticleById(@Param("id") Integer id);
}
  1. ArticleInfoMapper.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.ArticleInfoMapper"><resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo"><id column="id" property="id"></id><result column="title" property="title"></result><result column="content" property="content"></result><result column="createtime" property="createtime"></result><result column="updatetime" property="updatetime"></result><result column="uid" property="uid"></result><result column="rcount" property="rcount"></result><result column="state" property="state"></result><!-- association:实现一对一查询 property设置属性是啥,resultMap设置要映射哪个实体类 columnPrefix:解决相同字段覆盖的问题--><association property="userInfo" resultMap="com.example.demo.mapper.UserInfoMapper.BaseMap" columnPrefix="u_"></association></resultMap><!-- 想要获取到用户信息,还是必须要进行多表查询 --><!-- 进行多表联查时,如果两个表有相同的字段,前面表的该字段会将后面表的该字段覆盖,如下代码,两个表都存在id字段,当修改userinfo中admin的id=2时,运行结果还是此id=1,确实被articleinfo中的id=1给覆盖 所以可以采用加前缀方法解决 --><select id="getArticleById" resultMap="BaseMap">select a.*, u.id u_id, u.username u_username, u.password u_passwordfrom articleinfo aleft join userinfo u on a.uid = u.idwhere a.id = #{id}</select>
</mapper>

注意: 进行多表联查时,如果两个表有相同的字段,前面表的该字段会将后面表的该字段覆盖,如两个表都存在id字段,当修改userinfo中admin的id=2时,运行结果还是此id=1,确实被articleinfo中的id=1给覆盖 所以可以采用加前缀方法 columnPrefix=“u_” 解决

< association >标签,表示⼀对⼀的结果映射 !文章实体类中有一个属性表示作者信息,且一篇文章对应一个作者。其中property设置属性为文章实体类中的作者用户属性,resultMap设置要映射为用户实体类 !上述 resultMap=“BaseMap” 中的BaseMap 就是 UserInfoMapper.xml 中的设置,如下:

    <resultMap id="BaseMap" type="com.example.demo.model.UserInfo"><!-- 主键映射 --><id column="id" property="id"></id><result column="username" property="username"></result><result column="password" property="password"></result><result column="photo" property="photo"></result><result column="createtime" property="createtime"></result><result column="updatetime" property="updatetime"></result><result column="state" property="state"></result></resultMap>
  1. 测试代码
@SpringBootTest
@Slf4j
class ArticleInfoMapperTest {@Resourceprivate ArticleInfoMapper articleInfoMapper;@Test// 1 对 1 场景,一篇文章对应一个作者void getArticleById() {ArticleInfo articleInfo = articleInfoMapper.getArticleById(1);log.info("文章信息:" + articleInfo);}
}
  1. 运行结果
    在这里插入图片描述

显而易见,我们成功通过文章 id 查询到了文章信息,且信息里面包含了作者的信息 !!


⼀对多的关系映射(⼀个⽤户多篇⽂章案例)

实现根据用户 id 查询用户信息,里面包含了用户发表的所有文章信息

  1. 定义用户实体类 UserInfo ,加入属性 articleInfoList
@Data
public class UserInfo {private Integer id;//private String name; 当实体类中的属性与数据库中的字段不匹配时,可以使用resultMap 来解决private String username;private String password;private String photo;private String createtime;private String updatetime;private int state;private List<ArticleInfo> articleInfoList;//一个作者对应多篇文章  1对多场景
}
  1. 实现 UserInfoMapper 接口
//一对多场景:一个作者对应多篇文章  根据用户id获取用户信息和发表的所有文章
public UserInfo getUserAndArticleByUid(@Param("uid") Integer uid);
  1. UserInfoMapper.xml 实现
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo"><!-- 主键映射 --><id column="id" property="id"></id><result column="username" property="username"></result><result column="password" property="password"></result><result column="photo" property="photo"></result><result column="createtime" property="createtime"></result><result column="updatetime" property="updatetime"></result><result column="state" property="state"></result><collection property="articleInfoList" resultMap="com.example.demo.mapper.ArticleInfoMapper.BaseMap"columnPrefix="a_"></collection></resultMap><select id="getUserAndArticleByUid" resultMap="BaseMap">select u.*,a.id         a_id,a.title      a_title,a.content    a_content,a.createtime a_createtime,a.updatetime a_updatetimefrom userinfo uleft join articleinfo a on u.id = a.uidwhere u.id = #{uid}</select>

⼀对多需要使⽤ < collection > 标签,⽤法和 < association > 相同,后续的验证我们就不再写了,与前面一对一相似 !


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

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

相关文章

mysql主从复制搭建--待实践完善

一、什么是mysql主从复制 参考链接&#xff1a;MySQL主从介绍_rain_yunlx的博客-CSDN博客 Linux下搭建Mysql主从复制详细步骤&#xff08;Mysql版本5.7.35&#xff09;_linuxmysql主从_Direct_的博客-CSDN博客 Linux环境MySQL数据库主从复制保姆级教程_linux主从复制_小学生…

【SWAT水文模型】ArcSWAT输入准备:土地利用/土壤/气象数据

ArcSWAT输入准备&#xff1a;土地利用/土壤/气象数据 1 土地利用数据的处理1.1 数据下载 2 土壤库建立2.1 数据下载 3 气象数据库参考 1 土地利用数据的处理 1.1 数据下载 下载地址如下&#xff1a; 中科院1km土地利用数据 清华大学高精度土地利用数据 2 土壤库建立 SW…

如何将pdf图片文字转换成word 文字word图片怎么转换pdf

如今大家在工作中常常会运用到电脑来办公&#xff0c;电脑的运用大大提高了我们的工作效率&#xff0c;在带来机会的同时同样也带来了新挑战。 pdf图片怎么转换成word文档&#xff1f;PDF格式是一种常用的文档格式&#xff0c;它可以保持文档内容和格式的完整性&#xff0c;但是…

【UE】制作简单的山脉地形

在上一篇博客中&#xff08;【UE】使用Quixel Bridge下载免费贴图&#xff09;&#xff0c;介绍了如何下载免费贴图&#xff0c;本篇博客介绍如何使用这些贴图制作地形贴图。 1. 创建地形 2. 用雕刻工具绘制地形 3. 新建两个材质函数&#xff0c;分别命名为“GrassAuto”、“R…

离散数学期末复习第一章 数理逻辑

离散数学 离散数学是研究各种各样的离散量的结构及离散量之间的关系一门学科&#xff0c;是计算机科学中基础理论的核心课程。 什么是连续变量&#xff1f; 在一定区间内可以任意取值的变量叫连续变量&#xff0c;其数值是连续不断的&#xff0c;相邻两个数值可作无限分割&a…

在线问诊小程序系统方案以及价值

方案价值zlzwgz0127 1.扩大医院流量 a.预约到院 在线展示专家的介绍&#xff0c;更能彰显实力&#xff0c;吸引患者来院就医&#xff0c; 用户可选择在线问诊和预约到院 b.社区团购导流 与我们合作社区团购给医院的体检产品导流 c.专家直播导流 通过专家直播吸引潜在患者…

观察者设计模式(Observer Design Pattern)[论点:概念、组成角色、相关图示、示例代码、框架中的运用、适用场景]

文章目录 概念组成角色相关图示示例代码框架中的运用适用场景 概念 观察者设计模式&#xff08;Observer Design Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一种对象间的一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象&#xff0c;当…

机器学习 Rider数据集分析和预测

介绍数据集 ride_id&#xff1a;乘车ID rideable_type&#xff1a;乘车类型 started_at&#xff1a;开始日期 ended_at &#xff1a;结束日期 start_station_name&#xff1a;开始站的名字 start_station_id&#xff1a;开始站的ID end_station_name&#xff1a;结束站的名字 …

内网渗透之横向移动rdpwinrmwinrsspnkerberos

0x00 准备 环境&#xff1a;god.org cs上线 win2008web 提权利用ms14-058 抓取hash和明文密码(当获取到其他主机就重复提权和抓取密码) 扫描存活主机&#xff0c;扫描端口 代理转发-转发上线&#xff0c;生成反向连接木马&#xff0c;绑定监听器&#xff0c;上传至web根目录(方…

中小企业真的需要CRM吗?

如果你的企业没有CRM客户关系管理系统&#xff0c;企业主需要问问自己&#xff0c;他们将利用什么来扩展业务。福布斯进行的研究恰当地表明&#xff0c;充分利用CRM系统的企业可以将销售额提高29%。 中小企业定期产生大量客户&#xff0c;这可能会难以管理。这正是CRM系统在有…

《编码——隐藏在计算机软硬件背后的语言》精炼——第12章(二进制加法器)

“I hear and I forget. I see and I remember. I do and I understand”——Confucius 人类计算二进制数的方法 我们先计算本位的结果&#xff0c;称为加法位&#xff1b;再计算是否进位&#xff0c;称为进位位。 搭建二进制加法器 我们用逻辑门来搭建二进制加法器。它的思想…

高可用消息服务消息一致、可靠性、链路稳定性核心关注点

面临的问题 初期业务主要的场景是直播间的群聊消息以及一小部分的单聊消息。由于是教育场景&#xff0c;所以业务在划分聊天室的时候是以班级为单位进行划分的&#xff0c;假设每个聊天室的人数为500人。 问题一&#xff1a;用户的维护 直播场景的群聊与微信等常见的群聊在用…

OpenPCDet复现过程记录

0、前言 OpenPCDet项目之前我就复现过&#xff0c;一个很优秀的项目&#xff0c;这几天又需要用到这个项目&#xff0c;再次复现遇到了不少问题&#xff0c;特此记录复现的流程 1、环境准备 1.1、前置条件 以下是我安装的版本 CUDA 11.3CUDNN 8.2.1 CUDA和CUDNN安装可以参考…

61 openEuler 22.03-LTS 搭建MySQL数据库服务器-管理数据库用户

文章目录 61 openEuler 22.03-LTS 搭建MySQL数据库服务器-管理数据库用户61.1 创建用户示例 61.2 查看用户示例 61.3 修改用户61.3.1 修改用户名61.3.2 修改用户示例61.3.3 修改用户密码61.3.4 修改用户密码示例 61.4 删除用户示例 61.5 用户授权示例 61.6 删除用户权限示例 61…

c实例练习笔记(拓展)

本博文参考题目的地址看右边----》C技能树 我跟你说&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;c语言有bool类型&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;你是不是百度跟你说没有只有c才有&#xff1f; 是有的&#xff01;&#xf…

统计学 实验5

8.14 总体平均值&#xff08;μ&#xff09;&#xff1a;7.0 cm 总体方差&#xff08;σ&#xff09;&#xff1a;0.03 cm 样本平均值&#xff08;x̄&#xff09;&#xff1a;6.97 cm 样本方差&#xff08;s&#xff09;&#xff1a;0.0375 cm 样本大小&#xff08;n&#xff…

数百家数科公司齐聚用友BIP技术大会,共享企业数智化领先实践

4月19日&#xff5e;4月21日&#xff0c;由用友公司主办的“2023用友BIP技术大会“在用友产业园&#xff08;北京&#xff09;盛大召开&#xff0c;用友介绍了更懂企业业务的用友BIP-iuap平台&#xff0c;并发布了全面数智化能力体系&#xff0c;助力企业升级数智化底座&#x…

构建高效数据中台——数据只有被使用起来,才能创造价值

产品经理们时常会碰到这种问题&#xff1a; 我刚上线一个功能&#xff0c;请研发同志们帮我拉个数据出来分析&#xff0c;却被残酷告知需要排期。 我这里急得跺脚&#xff0c;但也只能理解。 数据研发们每天有查不完的数据和写不完的表&#xff0c;业务部门要的数据迟迟拿不到&…

CCBN 2023看点分析:国产8K摄像机国产化替代趋势增强

4月21日&#xff0c;为期3天的CCBN 2023&#xff08;第29届中国国际广播电视信息网络展览会&#xff09;在北京首钢会展中心圆满落幕&#xff0c;CCBN展会是亚洲广播电视设备展览会&#xff0c;也是中国广电行业规模最大、影响力最强的国际性展会之一&#xff0c;更是广电行业内…

同为科技(TOWE)防雷科普篇(二)——雷击灾害急救方法大全

前 言 当雷击发生时&#xff0c;空气中的各种微粒互相碰撞和摩擦便会使该空气介质两面的正负电荷的量持续积累&#xff0c;这时加于该空气介质的电压也会同时增加&#xff0c;当局部电压达到当时条件下空气的击穿电压时&#xff0c;该空气介质的局部便会发生电击穿而持续成为等…