上一系列介绍了MyBatis的背景,以及为什么我们使用MyBatis进行操作数据库,还实现了使用MyBatis进行查询数据库的,接下来我们继续将使用MyBatis操作数据库的其他三种基本操作进行总结.
目录
1. 前言
2. 增加用户操作
3. 修改用户操作
4. 删除用户操作
5. 多表查询操作(重点)
5.1 占位符(${} VS #{})
5.2 select标签返回值类型 resultType VS resultMap
5.2.1 resultType
5.2.2 resultMap
5.2.3 使用别名处理不一致问题
5.3 多表查询
6. 复杂情况:动态SQL使用
6.1 if 标签
6.2 trim标签
6.3 where 标签
6.4 set标签
6.5 foreach标签
总结
在UserMapper中实现接口方法add,传入的参数为用户对象,代码如下:
在XML文件中进行构造sql语句,插入操作使用insert标签
不为空的字段必须传值.
这里面进行替换参数的时候我们一般使用#{},有的时候也需要使用${}进行替换.具体后面会详细讲解.
我们接下来为了验证的方便,不在一一写Service层以及Controller层了,我们使用单元测试进行测试我们的代码.
1. 什么是单元测试呢?
单元测试是后端程序人员测试自己小部分代码正确性写的测试代码,当我们最后对称程序进行打包的时候,我们写的单元测试必须全部通过才能打包成功.而且我们的单元测试使用起来很方便,并且不会污染数据库中的数据.
SpringBoot项目创建的时候默认会使用单元测试框架spring-boot-test,而这个单元测试框架式依靠另一个注明的测试框架JUnit进行实现的.在pom.xml中就可以看见相关的依赖.
2. 那么如何使用单元测试呢?
2.1 首先我们得生成单元测试的类,在我们要进行测试的类点击generate
2.2 给测试类添加注解,表明这个类是用在什么环境下,我们此项目是在SpringBoot项目下,就添加注解SpringBootTest注解
2.3 在测试方法中进行构造自己的测试代码
我们对以上添加操作进行测试,结果如下:
如果我相反插入用户信息的自增ID,我们需要再xml文件构造sql语句的时候,对id,进行设置.
还是要按照上述的操作进行,先在UserMapper接口中实现相应的方法,然后在xml文件中使用户Update tablename set where 进行构造sql语句,最后在单元测试中进行测试.
继续使用上述操作流程,在xml文件中使用delete标签进行构造sql语句.
我们之前讲解过了,使用单表查询的操作.但是在实际的工作中并不是操作一张表这么简单的,我们设计到的一定是多表查询.
在总结多表查询之前我们,先把之前残留的一个问题先解决一下.就是占位符问题.
什么叫预处理编译呢: MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使用 PreparedStatement的 set 方法来赋值。
什么叫字符直接替换呢:MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
那么问题来了,这两种占位符我们应该怎么使用呢?
1. 当我们传入的是一个sql的关键字的时候,考虑使用${};
举例: 我们在淘宝中购买商品的时候,可以选择按照类型,价格等进行升序或者降序,那么前端传递给后端的英爱是一个desc或者asc的指令,我们拿到这个指令去进行构造sql语句.这是时候我们必须将该关键字进行替换,才能构造出正确的sql语句,如果使用#{},那么最后select * from userinfo group by id "desc";这样就是一个错误的sql语句了.还有一个关键的思想,我们在使用${}占位符的时候,传入的变量一定得是可预测的,或者说是可以竟枚举的,不然就会引起sql注入的问题.比如我们使用占位符在拼接"select * userinfo where usename = '${username}' and 'password=${password}'"的时候传入:password的值为' or 1='1,那么最后拼接成的就是:select * userinfo where usename ='张三' and password='' or 1='1'; 那么这个语句就可以获取用户表的所有信息数据,这就及其不安全,所以使用${}一定要考虑sql注入的问题.
2. 在使用like 的时候使用 #{} 报错.
上述进行构造之后: select * from userinfo where username like '%'username'%';
我们此时也不要使用${},我们可以使用MySQL自带的连接函数进行构造:
我们建议还是使用 #{} , 在真正不能使用 #{} 的时候再考虑使用 ${} .
在使用select标签的时候,我们必须设置两个参数,id='对应接口方法的名称' + 返回值类型
我们之前设置的返回值类型是resultType,这中方式在绝大数场景下都可以使用到,使用起来很简洁,直接定义到某个实体类.但是当我们的数据库字段名称和程序中字段名称不相同时就访问不到了.
数据库表结构如下:
程序对应如下:
我们可以很清晰的看出两个密码的字段是不匹配的,当我们还是按照之前的方法进行构造sql 的时候就不可以了
postman构造请求进行访问:
此时我们就需要进行使用resultMap了
在xml文件的首部添加
再进行设置sql语句
查询结果
字段和属性不一致的简单方案:
书写sql的时候,给不一致的字段使用别名
select id,username as name,password,photo,createtime,updatatime from userinfo
这一块只使用resultType是不可能将另外一个表的属性进行显示出来的.我们可以使用resultMap将关系进行一一映射,但是使用起来特别麻烦,我们未来的表字段有可能太多,所以不建议使用.
我们使用继承的方式进行实现
1. 我们在model文件夹下创建一个vo目录,用来存放给前端进行展示的类.
2. 实现一个类,继承文章表,并将文章表中没有的作者名字字段添加到新的展示类,并且实现序列化接口Serializable接口.
我们将返回值类型修改成我们自己的展示类类型
我们可以看出, 虽然我们继承了父类文章表,但是文章表的属性我们还是没有获取到,这是时候我们就要考虑我们使用的注解了,我们使用了@Data注解,他帮我们重写了tostring方法,我们要看一看这个ToString方法具体是怎么重写的,我们只需要去找到target文件下的Class目录.
我们看看字节码文件是怎么样实现的.
我们看到@Data注解生成的toString方法和我们预期的ToString方法不太一致,它只包含了两个属性,所以我们要自己进行重写toString方法.使其包含父类的属性.
再进行运行测试代码,就可以得到我们想要的:
这个时候就需要使用户动态标签
注意test 中的username 和 password 属性,是传入对象的属性名,而不是字段名.
那如果我们进行添加操作呢?
上述进行构造的sql能正常运行,但是如果非必填选项在最后呢?
有可能构造的sql语句为:insert intousername(username,photo,) values("haha","img.png",);这就不行了,是不合法的.所以就有了trim标签.
改造之后就是:
直接省略了where关键字,可以将语句中前面的and进行去除
以上
根据多个参数进行更新数据,可以去除最后面的逗号.
MyBatis的系列到此就结束了,基础的增删改查数据库就是这些内容,后续会根据项目实际使用MyBatis.
希望多多关注,谢谢❤️❤️❤️❤️