【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作

【JavaEE】MyBatis框架要点总结(2)

在这里插入图片描述

【JavaEE】MyBatis框架要点总结(2)

在上一篇文章中,我们主要讲解的内容就是MyBatis是什么,MyBatis环境的搭建,简单地体验了MyBatis的写法去查询数据库

而本文的内容就是,MyBatis更深入的讲解,以及更多的数据库操作,如增删改查…

  • 值得注意的是,写法就是写法,记住它就好了,要使用框架就要遵守写法的规定,熟悉什么地方应该写什么…
    1. 没有 “为什么这么写” 等问题,就是规定
    2. 开发者不需要过多注意底层原理,“坐享其成”即可~
    3. 知道代码意味着什么,这么写框架会帮我们做什么就行

**知道怎么进行开发,这才是重点!**之后再去了解一下xml文件去实现接口的一些原理~

1. 单表查看操作

1.1 (条件查询)通过id查找用户

1.1.1 接口上声明方法

参数最好不要写基本数据类型,这是因为前端不传参数过来的话,可以传默认值

  • 但是基本数据类型的默认值是有含义的,例如0、0.0、‘/u0000’、false
  • 并且,如果前端没有传相应的参数,是会赋值为null的,基本数据类型无法赋值为null!

在这里插入图片描述

  • 服务器报错,服务器并不会关闭,只是捕捉到了异常打印了日志

对象中的成员是基本数据类型除外:

在这里插入图片描述

只有null表示真正意义的空

在这里插入图片描述

这个@Param注解的作用就是,确定这个参数替换到sql语句的哪~

  • 这里爆红是因为我们【还没在xml中实现】/或者【没有正确实现】这个方法(这个提示是MyBatisX这个插件提供的)
1.1.2 xml文件中去实现方法

在这里插入图片描述

  • resultType,顾名思义,也就是返回的类型,设置为UserInfo,其实不加也行,因为可以通过方法的返回值类型来判断,不过一些版本不支持~
  • 这里的返回值UserInfo类型的

返回规则:根据返回的信息,也就是一张结果表,列名对应属性名

  1. 结果的列名有对应的属性,赋值给属性的值
  2. 列有的却没有对应的属性,赋值给了空气
  3. 属性有的却没有对应的列,无能为力,属性只能是默认值
  4. 如果有多个结果对象,则返回的是个集合List

接口内就不报错了:

在这里插入图片描述

在这里插入图片描述

  • 参数的处理可以使用这种方式:${id}这个花括号里的id对应的就是那个参数的注解括号里面的值
    • ${}的方式去写,原理就是简单的字符串拼接,所以如果传递的是字符串的话,还得自己传引号过去,比较麻烦
    • 并且这种写法,MyBatis并不是运行[一整条传参过来的自己拼接的sql字符串]而是提前定好[sql语句的模板]
      • 这里不是java代码,是xml,当然不能用+号拼接
    • 你也发现它不需要引号包裹,因为MyBatis规定这标签之间的值就是要执行的sql代码,不应该将写代码看成写字符串~
  • 包装类对象也会自动转化到sql字符串的,也就是对应的值~

在这里插入图片描述

不加注解,那么它对应的就是参数的名字,但是不是所有的电脑都适合这种写法,而注解则是所有人都适合

在这里插入图片描述

1.1.3 测试

在这里插入图片描述

在这里插入图片描述

  • 如果id为null的话,sql语句就为select * from userinfo where id = null,显然是错误的,那么后端就会传500的响应,代表服务器出现异常,在前端就会被ajax的error属性接受,没被success接受,那么这次请求就相当于失效了

Postman中发送请求:

在这里插入图片描述

前端不传id过去,Integer就为空:

在这里插入图片描述

1.2 传递参数的重点问题:sql注入问题

sql注入是什么?

${} 和 #{} 有什么区别?

这是MyBatis中很常见的面试题!

1.2.1 ${}方式

可见这是一种简单的替换,可以理解为字符串拼接,也就是说,字符串类型的数据我们要手动加引号:

以用户名查找用户为例:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

效果:

在这里插入图片描述

在这里插入图片描述

但是如果不加双引号:

在这里插入图片描述

就会报错,这个是sql语句执行失败的意思~

  • (我们之前的一个配置让sql执行就打印相关日志)~

在这里插入图片描述

并且,字符串拼接的话,就会出现影响原sql语句结构的问题,即sql注入问题:

例如传的字符串是这样的:

在这里插入图片描述

sql语句变为:

在这里插入图片描述

那么,就可能会出现危险的一种情况,就是改变原sql语句的结构,然后多加一条sql语句,对数据库进行攻击!

轻则sql语句因为非法而失效,服务器报500

重则:

  1. 查询你的表的其他信息!
    • 加个or 1 = 1,根据其他条件查询信息,甚至暴露其他库其他表的所有信息…
    • 例如,破解密码:即使输入密码是错的,加上or 1 = 1也能查询得到
      • 或者 1 = ‘1’,sql语法中数字是可以跟字符串进行比较的~
  2. 删除/修改你的数据!
    • 修改用户信息,删库删表…

总之,可以 巧妙地设计 出很多==危险的sql语句==

也就是说,${}是**即使执行的,比较危险!**

例子:

  • 用户登录密码错误仍能登录成功:

在这里插入图片描述

在这里插入图片描述

正常情况下:
在这里插入图片描述

恶意传参:

  • 不需要密码也能登录

在这里插入图片描述

服务器的日志:

在这里插入图片描述

1.2.2 #{}方式

而这个方式对应就不会出现这个问题:

在这里插入图片描述

在这里插入图片描述

查看日志:

在这里插入图片描述

这也就是 之前JDBC的 ? 占位符,然后用参数【value替换】的思想,也就是username的值就是这个String类型的值,id就是这个Integer类型的值

  • mybatis这一点就省去了原本的繁琐步骤,因为传过来的参数就能决定是setInt还是setString了…

底层帮我们去正确地赋值(避免了手动对字符串类型加引号的过程,也保证了不会出现添加字符串是危险sql语句而导致数据库被恶意影响的情况出生,因为只会被看成一个整体,一个普通的字符串的内容而已)~

  • 所以#{}也可以完成一些奇葩字符串的保存进表里,例如"''"'""',这种字符串如果拼接的话,很有可能会错(因为外部引号包裹的规定)
  • 但是#{}会把这个看成整体,就跟普通字符串除了值不同外没啥区别,就是一坨二进制数据,并不会有所影响

在这里插入图片描述

在这里插入图片描述

  • 被视为了整体字符串~

所以,#{}预编译的,比较安全!

  • 可以防止sql注入!
1.2.3 必须用到${}的情景

那么${} 这么危险,为什么还存在呢?

  • 这是因为一些情况下,是#{} 无法实现的
  • 例如, 传过去的字符串,是sql语句的关键字/本体的一部分!

一个典型的例子就是:

select * from userinfo order by id ${option};

在sql后面如果是desc,就是逆序;如果是asc或者啥也没有,就是顺序~

  • 当然,这个也能用动态sql来解决的,但现在的情景是,传参过来的静态sql

在这里插入图片描述

效果:

  1. desc

在这里插入图片描述

日志:

在这里插入图片描述

  1. asc

在这里插入图片描述

日志:

在这里插入图片描述

空字符串也可以:

在这里插入图片描述

1.2.4 ${}和#{}的区别和注意事项
  1. ${}可以实现的,#{}基本都能实现,所以能用#{}就用#{}
    • 因为${}是及时执行的,mysql 编译完整的sql并执行,而#{}是预编译的,可以防止sql注入的问题
    • sql注入就是通过字符串替换语句中的${}巧妙地构造出危险的sql语句
  2. 但是${}也是有使用场景的,在传参的值是sql语句的本体的一部分/关键字的时候,则必须使用${}
    • 因为#{},被字符串替换,代表的含义一定是“值”而不是关键字,跟不能被认定为sql语句本体的一部分(可以理解为带了引号)
    • ${}是不安全的,所以应该为${option}提供有限个选项(可枚举的),不能有用户自主输入传递的字符串,而是选择,并且这些选项不能让sql语句最终是危险的!
      • 这样,最终的sql语句的执行的各种情况都是意料之中的,是可控的
    • 例如刚才的例子,就只有三个选项
      1. “desc”
      2. “asc”
      3. “”

2. 简单的增删改操作

对于这三个操作,语句的返回值一定是int型的“受影响行数”

我想,你现在有很多想法,试着根据刚才这些规则去实现吧!

2.1 修改操作

在这里插入图片描述

在这里插入图片描述

测试:

在这里插入图片描述

将username为ad的用户的state置为0:

在这里插入图片描述

查看数据库:

在这里插入图片描述

2.2 删除操作

在这里插入图片描述

测试:

在这里插入图片描述

在这里插入图片描述

数据库:

在这里插入图片描述

2.3 增加操作

目前我们的知识只能运行我们固定的去传值:

insert into userinfo (username, password) values('张三', '123456')

我的意思就是,这一段是我们写死了的:

在这里插入图片描述

不能根据实体类值的属性而决定,如属性值为空则添加的时候不设置,不为空则这里就会设置,因为我们现在是sql结构写死了的静态sql,所以无法实现这种情况,等待下一篇文章“动态sql”的学习!

在这里插入图片描述
在这里插入图片描述

测试:
在这里插入图片描述

效果:

在这里插入图片描述

在这里插入图片描述

数据库中:

在这里插入图片描述

补充:

  • 有时候我们增加完成之后,是需要用到自增id的~
  • 因为这个自增id我们不需要自己去设,自然不知道它的值(如果不查表的话)
    • 也就是说我们传递的对象id属性是null值(一般情况下我们传递的是实体类对象,而不是一个个参数,即使如此,也应该有id参数)

例如:提交博客后,跳转到刚才的博客的详情页里;或者注册用户信息后,跳转到主页(省略一次登录)

  • 这些功能就需要这个自增id~

写法:

  1. 设置useGeneratedKeys
    • 意思就是“使用 自主生成的 键值对”,为true,否则框架不会在sql执行后专门返回这个键值对,我们目前知道的自增键值对就是我们的自增主键!
  2. 设置映射关系,keyColumn和keyProperty
    • 也就是让自增主键返回到哪里
    • keyColumn为列名
    • keyProperty为赋值给对象的哪个属性的属性名

在这里插入图片描述

在这里插入图片描述

xml实现的方法的返回值,依旧是影响的行数,而id返回给对象
在这里插入图片描述

测试:

在这里插入图片描述

这个UserInfo对象参数,身兼数职

  1. 信息输入型参数
  2. 输出型参数

3. 模糊匹配like

在这里插入图片描述

根据我们目前的理解,应该写成这样:

在这里插入图片描述

  • 即,找到用户名包含likeString的用户并返回~

在这里插入图片描述

测试:

在这里插入图片描述

命令行报错:

在这里插入图片描述

原因可想而知,就是因为预编译的问题,导致的语法错误

这么说的话那么“拼接sql”,及时执行的${} 可以解决咯?

在这里插入图片描述

  • 确实可以解决
  • 但是会出现sql注入的问题!

我们可以用mysql中的concat(字符串拼接)方法来有效解决这个问题!

  • 可以用#{}了~

在这里插入图片描述

效果:

在这里插入图片描述

查看日志:

在这里插入图片描述

4. 常见疑问

4.1 返回对象的属性名与字段名对应不上

疑问:为什么有时候一些字段是存在值的,但是属性却是null值?

  • 大概率是因为你字段名和返回对象的属性名对应不上~

在这里插入图片描述

在这里插入图片描述

这里提供几个解决方案:

其实这种对应不上的问题,在一开始写代码的时候规定下来就完事了,统一一下命名就行了,也就是写代码的时候规范一点,一般就不会出现

但是,有时候重复利用一段代码作用于另一个是实体类的时候,确实可能会出现这种情况~

  • 当然也有其他的原因~
4.1.1 修改成一致

这是个简单直接的代码,就是改变定义咯, name变为username

如果用了@Data 注解,修改定义就比较简单,改属性名后方法会自动重新定义

  • 但是,比较麻烦的就是,你的代码可能已经用到了Getter和Setter方法,一个个去修改有点麻烦
  • 修改定义不可能修改数据库表定义的表名的!
    • 貌似IDEA有个功能或者插件可以解决,可以去了解一下~

在这里插入图片描述

在这里插入图片描述

4.1.2 重命名列名

这也是比较有效的方法,利用的原理就是:

  • 返回值的赋值的依据,根据的是 最终呈现出来的表
  • 并不是将表转化为一个独立的对象,然后刚好是UserInfo对象,而是 可以赋值就赋值

也就是说,属性名和列名对应的上,也是可能出现属性名没有被赋值的情况的:

在这里插入图片描述

在这里插入图片描述

所以就可以通过重命名的方式来展现:

在这里插入图片描述

在这里插入图片描述

4.1.3 返回字典

通过定义resultMap的,将属性名和字段名进行手动规定的映射

resultMap的定义:

在这里插入图片描述

  • 含义就是,返回UserInfo对象,对这个对象进行手动赋值,或者叫做手动建立映射关系

select标签定义:

在这里插入图片描述

效果:

  • 值得一提的是,这个映射表只是起到一个 “补充” 的作用,其优先级会比**“名字对应得上就赋值,对应不上就不负责”**要高~
  • 也就是说,如果你在映射表中不写其他属性和列的对应关系,也没有关系,会根据属性名和列名的对应关系进行赋值~
    • 当然建议做戏做全套~

在这里插入图片描述

在这里插入图片描述

如果啥也没有,就跟之前的写法一致:

在这里插入图片描述

在这里插入图片描述

4.1.4 注解TableField

不过这个注解来自MyBatisPlus框架的,使用方法如下:

@TableField("username");
private String name;

这样就可以建立对应关系了~

不过MyBatis plus 我暂不讲解,感兴趣的同学可以去了解一下~

4.2 sql关键字作为属性名

疑问:会不会因为需要反引号去包起来属性名,才能正确的返回值?

如果是在sql语句中写字段名,那么就需要 反引号` 去包起来;

如果是作为返回值赋值给对象的属性的话,因为显示出来的列名本来就没有反引号

  • 所以就不需要担心对应不上的问题~

测试表:

在这里插入图片描述

测试:(代码省略)

在这里插入图片描述

4.3 传入的参数是多个对象

在这里插入图片描述

写代码是根据实际情况的,传入多个自定义对象作为参数很不合理!

  1. 传入多个对象,可以是List对象,后面复杂sql语句可以用到
  2. 传入多个对象,是自定义对象,甚至是多个不一样的自定义对象,是不合理的,即使框架有解决方法,也没啥意义,因为代码不是这么写的,不考虑实际情况的!

不要制造没有意义的冲突!

  • 如果要传递自定义对象,就只能单参传递,不然会导致“not found”!

我们要学的是正常的开发所用到的,而不是去研究一些奇葩的特殊情况的现象是咋样的,咋处理的,这样写会咋样,这样的不常规写法会咋样,研究这些作死一点的写法,返回的结果是咋样,这些都没意义!写正常的代码就行了!


文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆

代码仓库:mybatis_demo · 游离态/马拉圈2023年8月 - 码云 - 开源中国 (gitee.com)


  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s:103

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值