神级框架 - MyBatis【进阶】

目录

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 在处理 ${} 时, 会直接把 ${} 替换成变量的值.

神级框架 - MyBatis【进阶】_第1张图片

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

1. 接口中的方法

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

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

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

写法一  #{}:

写法二 ${}:

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

神级框架 - MyBatis【进阶】_第2张图片

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

神级框架 - MyBatis【进阶】_第3张图片

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

神级框架 - MyBatis【进阶】_第4张图片

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

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

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

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

神级框架 - MyBatis【进阶】_第5张图片

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

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

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

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

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

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

【代码示例】排序查询

1. 接口中的方法:

public List selectAllOrder(@Param("order") String order);

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

3. 单元测试

神级框架 - MyBatis【进阶】_第6张图片

测试结果:

神级框架 - MyBatis【进阶】_第7张图片

如果使用 #{} 参数占位符, 在预处理的时候就会给排序规则加上引号, 就相当于犯了以下 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 中的具体实现

3. 单元测试

神级框架 - MyBatis【进阶】_第8张图片

 单元测试结果:

神级框架 - MyBatis【进阶】_第9张图片

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

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

1.2 like 查询

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

【代码示例】

1. 接口中的方法

public List selectLike(@Param("username") String username);

2. 对应 xml 的具体实现

3. 单元测试

神级框架 - MyBatis【进阶】_第10张图片

神级框架 - MyBatis【进阶】_第11张图片

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

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

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

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

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

神级框架 - MyBatis【进阶】_第12张图片

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

神级框架 - MyBatis【进阶】_第13张图片

 

 

2. 多表查询的进阶知识

2.1 查询的返回类型: resultMap

对于查询 select a.*, u.username from articleinfo a left join userinfo u on a.uid = u.id

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

神级框架 - MyBatis【进阶】_第22张图片

神级框架 - MyBatis【进阶】_第23张图片

 查询结果正确!!

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

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

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

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

神级框架 - MyBatis【进阶】_第24张图片

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

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

神级框架 - MyBatis【进阶】_第25张图片

3. 动态 SQL 的使用

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

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

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

3.1  标签

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

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

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

神级框架 - MyBatis【进阶】_第26张图片

 

 

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

你可能感兴趣的:(JavaEE进阶,mybatis)