【 Spring Mybatis 复杂的查询操作 】

news2024/11/15 12:24:53

文章目录

  • 引言
  • 一、参数占位符 #{} 和 ${}
  • 二、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 userinfo
     where 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 userinfo
    where 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 userinfo
    where 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 userinfo
    where 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 userinfo
    where 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 userinfo
                where 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_password
        from articleinfo a
                 left join userinfo u on a.uid = u.id
        where 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 {

    @Resource
    private 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_updatetime
        from userinfo u
                 left join articleinfo a on u.id = a.uid
        where u.id = #{uid}
    </select>

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


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

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

相关文章

执着于考研考公却一再挫败,拿什么拯救你的职场后半生?

今天之所以想写一篇这样的文章&#xff0c;确确实实是有感而发&#xff0c;因为从近来接触的学员身上&#xff0c;能够最直观地感受到&#xff1a;考公考研失败后的同学&#xff0c;他们内心的那种焦虑感远超往期&#xff01; 用他们的话讲&#xff1a;“目前的状态就是感觉自…

540. 有序数组中的单一元素

给你一个仅由整数组成的有序数组&#xff0c;其中每个元素都会出现两次&#xff0c;唯有一个数只会出现一次。 请你找出并返回只出现一次的那个数。 你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。 示例 1: 输入: nums [1,1,2,3,3,4,4,8,8] 输出: 2 示…

“开发人员必备技能:Python接口自动化测试全攻略“:了解接口测试的基础知识,并通过Python编写测试用例,提升自己的测试技能

目录 摘要 一、基础知识 二、工具选择 三、实现步骤 1.安装依赖库 2.编写测试用例 3.运行测试用例 4.查看测试结果 四、代码实现 总结 摘要 随着互联网行业的不断发展&#xff0c;越来越多的企业开始注重自动化测试的重要性。而在自动化测试中&#xff0c;接口自动化…

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

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

我国激光打标机行业分析:防疫放开医疗行业激光打标机需求迫切 行业将迈向自动化、智能化的未来

1、激光打标机行业定义、分类及应用范围 激光打标机是用激光束在各种不同的物质表面打上永久标记的激光设备。激光打标技术相比传统的标识技术&#xff0c;不仅仅在对于材料的损耗&#xff0c;标识效果更具有优势&#xff0c;并且在生产加工的效率和产出方面更是有过之而无不及…

【复现与代码分析】Apache APISIX Dashboard API权限绕过导致RCE(CVE-2021-45232)

Apache APISIX Dashboard API权限绕过导致RCE&#xff08;CVE-2021-45232&#xff09; 0x00漏洞信息 Apache APISIX 是一个动态、实时、高性能的 API 网关&#xff0c; 提供负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。Apache APISIX …

节点流和处理流

节点流可以从一个特定的数据源读写数据&#xff0c;如FileReader&#xff0c;FileWriter处理流&#xff08;也叫包装流&#xff09;是“连接”在已存在的流&#xff08;节点流或处理流&#xff09;之上&#xff0c;为程序提供更为强大的读写功能&#xff0c;也更加灵活&#xf…

JavaEE IP协议详细讲解

详细讲解IP协议 O > O 文章目录 JavaEE & IP协议1. 地址管理1.1 IP协议报文格式1.2 动态分配IP地址1.3 NAT机制1.4 IP协议1.4 IP协议工作过程1.5 IPV6协议1.6 IP地址的组成1.7 特殊的IP地址 2. 路由选择 JavaEE & IP协议 1. 地址管理 我们希望&#xff0c;每个网络…

免费AI人工智能在线写作伪原创-百度ai自动写文章

免费伪原创洗稿工具 免费伪原创洗稿工具现在终于推出了&#xff01;你是否在写作的时候&#xff0c;经常因为缺乏灵感而苦恼&#xff1f;或者&#xff0c;你在撰写文章的时候&#xff0c;发现自己的语言表述不够丰富&#xff0c;缺乏变化&#xff0c;语句重复率太高&#xff1f…

从零学习SDK(9)SDK的局限

SDK是一种便捷而实用的工具&#xff0c;但软件开发者不能视SDK为万能的解决之道&#xff0c;SDK也有局限性&#xff0c;并且这些局限性可能会十分“致命”。软件开发者在选择SDK产品之前&#xff0c;需要先了解SDK的不足之处。本文将介绍SDK存在的三种问题&#xff0c;以引起软…

新手必看!ChatGPT常见问题总整理,你遇到了几个?

随着ChatGPT火爆全球,使用人数以指数型成长,许多使用上的问题呈现在网路上。 今天这篇文章会用实作的方式带大家了解ChatGPT有哪些常见问题,以此减少踩坑的机会。 并用简单的示例让大家感受GPT-3.5与GPT-4的能力差异,希望对大家有所帮助。 大家会有这些问题,其实就是希望…

【手把手做ROS2机器人系统开发一】开发环境搭建

【手把手做ROS2机器人系统开发一】开发环境搭建 目录 【手把手做ROS2机器人系统开发一】开发环境搭建 一、专栏介绍&#xff1a; 二、开发环境搭建&#xff1a; 1.Ubuntu系统安装 2.ROS2系统环境安装 3.测试系统运行 一、专栏介绍&#xff1a; 大家好&#xff0c;今天给大家…

哈希表企业应用-淘宝分布式文件系统核心存储引擎

哈希表企业应用-淘宝分布式文件系统核心存储引擎-基本概述 淘宝网 谁都知道 是一个电子商务网站,可能是第一批电商模式 b2c 当然从一个技术人员讲只有一点 淘宝店铺商品存储到哪里 感觉有点像废话 :肯定在仓库里 不,我说的是 :商品详情页以及商品介绍 存储 在哪里 我知道大家都…

【图论】弗洛伊德Floyd算法模板及实战(LeetCode743)

文章目录 框架实战 Floyd-Warshall算法本质是一种动态规划&#xff0c;可以用于解决任意两点间的最短路径&#xff08;多源最短路径问题&#xff09;&#xff0c;支持负权&#xff0c;而Dijkstra算法则不支持负权。 框架 第一步&#xff1a; 定义s[i][j]存储i到j的(最短)路径长…

字典树/前缀树Trie(附Java代码)

字典树/前缀树 1.字典树Trie1.1 字典树举例 2.代码实现2.0 代码结构2.1方式一&#xff1a;使用数组存储孩子节点2.2方式二&#xff1a;使用HashMap存储孩子节点 在计算机科学中&#xff0c;trie&#xff0c;又称前缀树或字典树&#xff0c;是一种有序树&#xff0c;用于保存关联…

一文带你上高速入门linux(含常用命令语法、说明、举例)

一文带你入门linux常用命令&#xff08;含举例和详细说明&#xff09; Linux系统操作介绍1. Linux发行版2. Linux文件系统3. Linux基本命令4. Linux用户和权限管理5. 软件包管理6. 系统管理7. 网络管理8. Shell脚本9. 文本处理10. 系统监控和日志11. 总结 Linux系统操作介绍 L…

塔望3W消费战略全案丨品类重新定义 打造金皇品高端速食第一面

金皇品 客户&#xff1a;上海皇品食品有限公司 品牌&#xff1a;金皇品 服务&#xff1a;3W消费战略 品牌全案 项目背景 金皇品隶属于上海皇品食品有限公司&#xff0c;品牌创立于1995年。产品以原汁作为核心卖点&#xff0c;通过线下渠道的密集耕耘&#xff0c;在浙江&…

NPM 发包 js 文件并支持 ts 使用(包含 gulp 打包压缩)

主篇 npm 上传发布自定义组件以及使用详细流程。 npm login 报错&#xff1a;Unexpected token < in JSON at position 0 while parsing near ‘<!DOCTYPE HTML PUBLI…’。 登录 npm $ npm login Username: dengzemiao Password: Email: (this IS public) xxxx163.co…

【李宏毅】GNN学习笔记

视频链接 [TA 補充課 Graph Neural Network (1/2) (由助教姜成翰同學講授) - YouTube] [TA 補充課 Graph Neural Network (2/2) (由助教姜成翰同學講授) - YouTube] [speech.ee.ntu.edu.tw/~tlkagk/courses/ML2020/GNN.pdf] Introduction 应用&#xff1a;分类、Generatio…

Docker 快速入门

1、Docker 简介 Docker是一个开源的容器引擎&#xff0c;它可以帮助我们更快地交付应用。Docker可将应用程序和基础设施层隔离&#xff0c;并且能将基础设施当作程序一样进行管理。使用Docker&#xff0c;可更快地打包、测试以及部署应用程序&#xff0c;并可减少从编写到部署…