神级框架 - MyBatis【进阶】

news2024/12/28 4:53:13

目录

1. 单表查询的进阶知识

1.1 参数占位符 #{} 和 ${} 的区别

1.1.1 #{} 和 ${} 的区别一 (#{} 胜一分)

1.1.2 #{} 和 ${} 的区别二 (${} 胜一分)

1.1.3 #{} 和 ${} 的区别三 - 最主要的区别 (${} 惨败)

1.2 like 查询

2. 多表查询的进阶知识

2.1 查询的返回类型: resultMap

2.2 多表查询示例

3. 动态 SQL 的使用 (抽空补完)


1. 单表查询的进阶知识


1.1 参数占位符 #{} 和 ${} 的区别

1.1.1 #{} 和 ${} 的区别一 (#{} 胜一分)

1. #{} 预编译处理

  • MyBatis 在处理 #{} 时, 底层还是会使用 JDBC那一套, 将 SQL 中的 #{} 替换成 '?' , 然后使用预编译 PreparedStatement 的 setXXX()  方法进行赋值.

2. ${} 字符串直接替换

  • MyBatis 在处理 ${} 时, 会直接把 ${} 替换成变量的值.

【代码示例】 根据 username 查询用户 

1. 接口中的方法

根据用户名的全字匹配, 假设数据库中的名字唯一.

public UserInfo selectByName(@Param("username") String username);

2. 对应 XML 的具体实现(对比两种写法)

写法一  #{}:

<select id="selectByName" resultType="com.example.demo.model.UserInfo">
    select * from userinfo where username=#{username}
</select>

写法二 ${}:

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

3. 对该功能分别使用两种 xml 的写法进行单元测试:

#{} 参数占位符的测试结果:  能够正确查询到我们想要的数据

${} 参数占位符的测试结果: 

上述测试结果报的错误其实就等同于以下 SQL 语句:

 【结论】: 使用 ${} 参数占位符时, 相当于参数直接替换, 它的问题就是会带来越权查询和操作数据等问题. (当传递的参数是整型时, 两种参数占位符的测试结果就会给我们一个效果相同的假象)

【扩展】既然 ${} 是直接替换, 那我是否可以给所有的参数都先加上引号  '${}', 大力出奇迹, 这样不就解决上述问题吗 ?

  • 对于字符串类型, 加上引号确实可以解决
  •  但是对于是整型, double 类型的数据等等, 我们也这样去给它们去加引号, 就会存在一个问题.

1. 从结果上来看, 对于整型数据,, 加上引号和不加引号的查询结果一致. 但是对于给整型加引号这种方式存在 "隐式类型转换" 的问题, MySQL 帮我们做了这个事情, 查询的时候一旦有类型转换, 查询的过程就不会走 "索引" 了.此时查询性能就会非常非常低.

2. 此处是因为数据量少, 所以查询的时候看起来效果一样, 如果数据量有几百个t, 那么走索引的查询可能话费 2ms, 而存在类型转换的查询所需要的时间可能就是 20 分钟!!

1.1.2 #{} 和 ${} 的区别二 (${} 胜一分)

>>> 一个程序里面传参, 使用 #{} 已经可以解决 99.99% 的问题了, 那为啥还需要搞出一个  ${} ?

虽然 #{} 已经很完美了, 但是还有一小部分的事情还是需要 ${} 来解决. 正所谓事物的存在, 是有一定的道理的, 它的价值就在于传参时, 能够执行 MySQL 的关键字.

${} 参数占位符接收关键字参数

【代码示例】排序查询

1. 接口中的方法:

public List<UserInfo> selectAllOrder(@Param("order") String order);

2. 对应 xml 中的具体实现:

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

3. 单元测试

测试结果:

如果使用 #{} 参数占位符, 在预处理的时候就会给排序规则加上引号, 就相当于犯了以下 SQL 语句的错误:

1.1.3 #{} 和 ${} 的区别三 - 最主要的区别 (${} 惨败)

【SQL 注入问题】

什么是 SQL 注入? 

SQL 注入就是在不知道你用户名和密码的情况下, 直接就能够查到你的信息. SQL 注入就是一串简单的字符串, 例如 " ' or 1='1 ",  它利用的就是 MySQL 的漏洞搞事情.

【示例】

为了方便演示, 我只留一条数据.

此时我在不知道用户名和密码的情况下, 输入以下 SQL 语句就能轻松拿到用户信息了.

select * from userinfo where username='张三' and password='' or 1='1';

上述 SQL 语句, password 中的 ' or 1= '1 参数, 左边的单引号 ' 和前面的单引号匹配上了, 然后 or 后面的 1='1' 属于隐式转换, 等式恒成立. 于是整个 SQL 就恒成立, 所以在不知道密码和用户名的情况下就可以获取到用户信息了.

在程序中 #{} 可以避免 SQL 的问题, 而使用 ${} 就会出现 SQL 问题.

【代码示例】演示 ${} 的问题

1. 接口中的方法

public UserInfo selectByNameAndPwd(@Param("username") String username,
                                      @Param("password") String password);

2. 对应 xml 中的具体实现

<select id="selectByNameAndPwd" resultType="com.example.demo.model.UserInfo">
    select * from userinfo where username=${username} and password=${password}
</select>

3. 单元测试

 单元测试结果:

1. 由此可见, 如果我们的参数占位符是 ${} , 就一定存在 SQL 注入的问题, #{} 参数占位符在测试的时候, 是查询不到结果的,  会显示 null , 因为它是预编译处理的, 它认为 ' or 1='1 就是一个字符串, 所以就不会出现 SQL 注入的情况.  这里就不验证了, 大可以自己测试一下.

2. 所以我们前面在使用 ${} 接收参数的时候, 务必要在 controller 层验证一下, 如果传递过来的参数是 'asc' 或者 'desc' 才继续往下走, 否则后面的代码就不要去执行了. 这样也能避免 SQL 注入.

1.2 like 查询

模糊查询的时候使用 #{} 就不太合适.

【代码示例】

1. 接口中的方法

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

2. 对应 xml 的具体实现

<select id="selectLike" resultType="com.example.demo.model.UserInfo">
    select * from userinfo where username like '%#{username}%'
</select>

3. 单元测试

1. 还是因为 #{} 是预编译处理, 所以会给username 加引号, 最后就会变成  '%'username'%', 这样的 SQL 语句在语法上就有错.

2. 而使用 ${} 确实能解决问题, 但是前面讲了它存在 SQL 注入的风险, 不安全,  需要在 Controller 层去验证参数的, 像前面的排序查询, 是可以穷举的, 要么是 asc , 要么是 desc, 而此处的模糊查询是不可穷举的. 所以也不会使用 ${}

>>> 那么模糊查询需要怎么接收参数呢 ?

模糊查询可以使用 MySQL 的内置函数 concat() 来处理, 具体 xml 代码:

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

下面进行单元测试检验查询结果:

再从数据库进行模糊查询进行对比, 结果正确: 

 

 

2. 多表查询的进阶知识

2.1 查询的返回类型: resultMap

对于查询 <select> 标签来说至少有两个属性:

  • id 属性: 用于标识实现接口中的哪个方法
  • 结果映射属性: resultType/resultMap

绝大多数情况都可以 使用 resultType, 它有很明显的优势, 使用方便, 直接定义到某个实体类即可, 在 Mybatis 初阶博客 里面的查询, 以及前面的模糊查询都是使用的 resultType, 此处就不演示了. 而 resultMap 使用起来就相对来说要麻烦一些, 但是它可以解决字段名和属性名不相同的情况.

resultMap 主要应用场景

1. 字段名和属性名不同的场景. (针对查询的影响)

2. 一对一, 一对多关系可以使用 resultMap 映射并查询数据.

>>> 为什么会有字段名和属性名不同的情况 ? 

因为在大公司, Java 程序和数据库是有两个岗位的人去开发的, 不同的岗位在代码上的规范也是不同的, 数据库有数据库的规范, Java 有 Java 的一套规范.所以两拨人去写代码的时候, 难免会出现一些属性名和字段名不相同的情况. (例如当一个字段出现多个单词组合额情况, 数据库可以使用下划线组成, 而 Java 是使用小驼峰. 各有各的规范)

例如: 

 

先演示一下当属性名和字段名不相同的时候, 我们再使用 resultType, 此时单元测试的结果是什么:

【现象】

当属性名和字段名不一致时(username/name, id/uid), 单元测试不报错, 并且查询结果对应的属性是没有值的 (默认值)

【解决】

此时需要使用 resultMap 来解决, 对应的 xml 改写 (字典映射):

<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
    <id column="id" property="uid"></id>
    <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>

1. 首先要加上 <resultMap> 标签, 它有两个属性, 一个是 id , 一个是 type.

  • id 是用来标识当前是属于与哪个 xml 文件的,  <resultMap> 它的作用域属于当前 xml 文件, 所以其他 xml 文件下是允许重名的.
  • type 属性表示要映射的实体类.

2. <result> <id> 标签里面就是用来表示不同字段名和属性名对应的映射关系. column 中写的是字段名, property 中写的是属性名.

  • <id> 标签是用来给主键使用的
  • <result> 标签是给普通字段使用的.

3. 对应的查询标签中的 resultType 改成 resultMap.

 

 此时再针对普通查询进行单元测试, 此时查询结果, 对应的属性上就有值了.

>>> 虽然我们此处只有两个属性和字段名不匹配, 但是我们最好把所有的属性和字段的映射都写上去, 即使它们相同,在单表查询的时候不会出问题, 但是在一些特殊的情况下如果我们不写还是可能会出错的(例如一对多的多表查询). 总之, 我们遵守规范就一定不会出错.

>>> 另外想要解决字段名和属性名不一致的问题, 除了 resultMap, 其实还可以通过属性重命名的方式来解决, 后面演示示例.

2.2 多表查询示例

前面我们只创建了用户表, 此时我们再添加一个文章表:

【示例】查询文章表, 并显示文章对应的作者名字;

此处的查询涉及到两张表, 但是查询结果并不是想要得到两张表的数据, 而是想要拿到文章表和用户表相关的数据, 所以属于外连接, 且文章表为主体.

第一步, 在对应的文章 model 中添加作者属性:

第二步, 提供文章表使用的接口和查询方法:

@Mapper
public interface ArticleMapper {
    // 查询文章表, 并显示文章对应的作者名字
    public List<ArticleInfo> selectAll();
}

第三步, 写对应的 xml 实现:

<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>
    <result column="username" property="author"></result>
</resultMap>
<select id="selectAll" resultMap="BaseMap">
    select a.*, u.username from articleinfo a
    left join userinfo u
    on a.uid = u.id
</select>

然后进行单元测试, 验证结果是否正确:

 查询结果正确!!

>>> 假如我还想显示用户表的其他字段呢

无论是两张表, 三张表, 你需要什么字段, 直接在 ArticleInfo 实体类中加上相应的属性即可, 但是要注意的是, 要么保持字段属性名一致.要么使用 resultMap 字典映射. 其实还可以使用起别名的方式来解决.

【起别名解决属性字段名不匹配问题】

还是上面那个代码示例,  为了便于区分重命名的字段, 我先将其他属性和数据库字段名保持一致, 文章表中留一个 author 属性和字段名不一致来演示效果. 

此时 xml 不使用 resultMap, 依然使用 resultType, 但是 SQL 语句使用起别名的方式来做到 resultMap 的效果. (u.username as author)

<select id="selectAll" resultType="com.example.demo.model.ArticleInfo">
    select a.*, u.username as author from articleinfo a
    left join userinfo u
    on a.uid = u.id
</select>

单元测试结果如下, 依然能够正确的查询储结果, 并且 author 上面也有对应的作者名.

3. 动态 SQL 的使用

什么是动态 SQL ? 来看看官方是怎么说的:

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

简言之: 动态 sql 是Mybatis的强大特性之⼀,能够完成不同条件下不同的 sql 拼接.

3.1 <if> 标签

例如在注册/登录 的时候, 我们会遇到一个这样的问题:

  • 如果只有一个非必填字段的时候, 那么我只需要给出两个方法, 有一个有参数, 一个无参数.
  • 但是很多时候是有很多选项, 有些是必填项, 有些是非必填项, 这样我们在写代码的时候, 为了考虑用户的各种情况, 就需要对这些选项进行排列组合写出包含各种情况的方法. 

这样就会非常痛苦, 如果你想使用简单的 if else来解决, 这样会把你累死, 而且你的代码可维护性将会变得非常糟糕.  此时就需要动态 SQL 的 <if> 标签来解决了.

 

 

最近临近考试, 剩下的类容 (几个标签) 抽空再继续讲解!!

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

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

相关文章

node.js+uni计算机毕设项目基于微信平台的小龙虾养殖管理程序设计(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

【Lilishop商城】No4-1.业务逻辑的代码开发,涉及到:会员B端第三方登录使用及后端接口(微信、QQ等)

仅涉及后端&#xff0c;全部目录看顶部专栏&#xff0c;代码、文档、接口路径在&#xff1a; 【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇会结合业务介绍重点设计逻辑&#xff0c;其中重点包括接口类、业务类&#xff0c;具体的结合源代…

Mycat2(二)windows搭建mycat2、mycat2相关概念、配置文件解释

文章目录windows搭建mycat2步骤下载mycat2修改配置并启动mycat2命令mycat2相关概念mycat2配置文件用户相关配置属性数据源datasources集群cluster相关配置逻辑库表schemas配置windows搭建mycat2步骤 修改C:\Windows\System32\drivers\etc下的hosts文件&#xff0c;防止dns污染…

node.js+uni计算机毕设项目基于微信小程序某企业考勤系统(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

「设计模式」享元模式

「设计模式」享元模式 文章目录「设计模式」享元模式[toc]一、概述二、结构三、案例实现四、优缺点五、JDK中的享元模式六、小结一、概述 在面向对象程序设计过程中&#xff0c;有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源&#x…

node.js+uni计算机毕设项目个人财务管理小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

《图解TCP/IP》阅读笔记(第六章 6.5、6.6)——其他代表性的传输层协议与UDP、TCP首部

6.5 其他传输层协议 实际上&#xff0c;UDP与TCP在很长的一段时间&#xff0c;霸占了“传输至尊榜”中的前两位&#xff0c;难以分出高下&#xff0c;但是仍有几款“神兵利器”&#xff0c;被收入兵器榜前十位&#xff0c;接下来就来介绍一些已经被提案且在今后可能会被广泛使…

控制图简明原理及Plotly实现控制图Python实践

1. 控制图简明原理 1.1. 关于控制图概述 控制图&#xff08;Control Chart&#xff09;又叫管制图&#xff0c;图上有三条平行于横轴的直线&#xff1a;中心线&#xff08;CL&#xff0c;Central Line&#xff09;、上控制限&#xff08;UCL&#xff0c;Upper Control Limit&…

2023跨面代码(烟花+自定义文字+背景音乐+雪花+倒计时)

2023年快要到来啦&#xff0c;很高兴这次我们又能一起度过~ 目录 一、前言 二、跨年烟花 三、效果展示 倒计时 2023​编辑 兔圆圆​编辑 四、编码实现 index.html 烟花&#xff0c;雪花&#xff0c;背景音乐&#xff0c;页面样式 index.js 自定义文字 五、获取代码 需…

RabbitMQ 第一天 基础 2 RabbitMQ 的安装配置 2.2 RabbitMQ 管控台使用

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础2 RabbitMQ 的安装配置2.2 RabbitMQ 管控台使用2.2.1 RabbitMQ 控制台的使用第一天 基础 2 RabbitMQ 的安装配置 2.2 RabbitMQ 管控台使用 2.2.1 RabbitMQ 控制台…

算法学习笔记

最近无意中看到一个算法的网站&#xff0c;看着感觉介绍得挺系统的&#xff0c;虽然做算法以及指导学生开发各种算法这么些年了&#xff0c;却没有真正系统的学习过&#xff08;几年前啃过算法导论&#xff0c;但是苦于那蹩脚的中文翻译&#xff0c;也没有去看英文原文&#xf…

车用DC-DC模块 1224V转8V3A过认证大塑料外壳

名称&#xff1a;车用12V转8V3A电源转换器 型号&#xff1a;LM40J8V3A3S 性质&#xff1a;非隔离型的BUCK电源转换器&#xff0c; 特点&#xff1a;采用集成IC设计&#xff0c;具有转换效率高&#xff0c;体积小&#xff0c;稳定可靠的特点&#xff0c;采用灌胶工艺&#xf…

从零搭建机器学习平台Kubeflow

1 Kubeflow简介 1.1 什么是Kubeflow 来自官网的一段介绍&#xff1a; Kubeflow 项目致力于使机器学习 (ML) 工作流在 Kubernetes 上的部署变得简单、可移植和可扩展。 Kubeflow的目标不是重新创建其他服务&#xff0c;而是提供一种直接的方法&#xff0c;将用于 ML 的同类最佳…

Java项目:springboot田径运动会管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为管理员、学生两种角色&#xff0c; 管理员主要功能包括&#xff1a; 功能&#xff1a;登录、查看个人资料、修改密码、选手管理、赛事…

SpringBoot 2.7.1学习---构建SpringBoot的几种方式

开发环境: SpringBoot2.7.1需要JDK版本8,Spring版本需要5.3.20或更高版本 maven版本3.5以上 如果不使用内置Tomcat,需要tomcat9.0或以上 Tomcat9好像没有,先搞个 SpringBoot 2.7.1快速入门 构建SpringBoot的几种方式 1.maven方式构建 写一个启动类 且加上SpringBootAppli…

还在手动发早安吗?教你用java实现每日给女友微信发送早安

摘要&#xff1a;教你如何用java实现每日给女友微信发送早安等微信信息。本文分享自华为云社区《java实现每日给女友微信发送早安等微信信息》&#xff0c;作者&#xff1a;穆雄雄 。 前言 据说这个功能最近在抖音上很火&#xff0c;我没有抖音&#xff0c;没有看到。 但是我…

排序

章节目录&#xff1a;一、排序算法1.1 概述1.2 分类1.3 算法复杂度1.4 时间复杂度1.5 空间复杂度二、冒泡排序2.1 概述2.2 算法分析2.3 代码示例三、选择排序3.1 概述3.2 算法分析3.3 代码示例四、插入排序4.1 概述4.2 算法分析4.3 代码示例五、希尔排序5.1 概述5.2 算法分析5.…

裸露土堆识别系统 yolov7

裸露土堆识别系统基于yolov7深度学习架构模型&#xff0c;对现场画面实时分析检测&#xff0c;如检测到画面中的土堆有超过40%部分没被绿色防尘网覆盖&#xff0c;则立即抓拍存档告警。我们使用YOLO(你只看一次)算法进行对象检测。YOLO是一个聪明的卷积神经网络(CNN)&#xff0…

我靠steam搬砖,日赚几千,投入不到万元

什么做苦力、技能、直播卖货&#xff0c;电商等等对比我这个都是小钱。我这个方法是利用了大部分人的信息差来赚钱。 我就不藏着掖着了&#xff0c;授人以鱼不如授人以渔&#xff0c;反正你赚的又不是我的钱。 什么是“Steam游戏搬砖”呢&#xff1f; 简单来说&#xff0c;就…

Docker网络

网络基础知识 网络相关命令 查看Linux中的网卡 [rootlocalhost ~]# ip link show[rootlocalhost ~]# ls /sys/class/net[rootlocalhost ~]# ip a 状态: UP、DOWN、UNKNOW link/ether&#xff1a;MAC地址 inet&#xff1a;该网卡绑定的IPv4地址 [rootlocalhost ~]# ip link …