这篇文章将讲解MyBatis的基础使用,MyBatis的学习是非常重要的,在前面学习servlet的时候,我们就能感受到将数据持久化存储的重要性,当时在使用JDBC的时候非常繁琐麻烦,但是在Spring里,提供了一种框架可以轻松且惬意的实现数据存储的功能:MyBatis。
MyBatis的学习只分为俩部分:1.配置MyBatis的环境 2.设置配置信息
1.MyBatis的环境搭建
在Spring里的MyBatis因为依靠SpringBoot实现的,因此使用MyBatis的话,环境搭建如下:
在依赖注入里面引入这俩依赖,MyBatis配置就完成了
2.设置MyBatis的配置信息
2.1 设置数据库连接的相关信息
在application中进行:
如果使⽤ mysql-connector-java 是 5.x 之前的使⽤的是“com.mysql.jdbc.Driver”,如果是⼤于 5.x使⽤的是“com.mysql.cj.jdbc.Driver”
2.2 设置MyBatis的xml保存路径和命名方式
这时候 环境就搭建完成咯~~
3.Mybatis基本使用
上图来看 每个Mybatis的使用(命令)包括俩个组件:Inteface和xml,Interface是供Controller使用的接口,而xml就是sql语句放在这里,但是xml怎么实现接口的方法呢?Mybatis通过代理的方式:生成一个代理对象:会将interface方法的声明和xml方法的实现组成一个代理对象,进行填充。这里可以用一个例子来解释,你有一个苹果手机和一个安卓数据线,这时候你无法进行充电,但是如果说有一个转换头,就可以实现充电的功能,这个转换头就是Mybatis,将Interface和xml进行组合变成一个虚拟对象,就可供其他类或者方法进行调用了。
Mybatis有俩部分组成:Inteface(让其他层进行调用/注入) 和 xml(提供了具体的实现sql语句)
我们想要从数据库里获取数据到页面上,我们就需要做以下步骤:
由于在xml文件中已经规范了Mybatis的保持路径和命名规范了,因此:
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.UserMapper">
<select id="getAll" resultType="com.example.demo.entity.UserEntity">
select * from userinfo
</select>
</mapper>
select标签内 id 是方法名字,resultType 是返回的类所在的位置。
如果我们想不通过输入网址的方式来实现测试功能的话,我们就要涉及到单元测试的相关知识了
接下来简单讲一下单元测试的使用方法:
4.单元测试基础使用
由于在Spring里内置了相关单元测试的框架,因此在使用单元测试的时候,我们就不必再引入一个新的依赖了,具体使用如下:
@SpringBootTest的注解表明当前的类还是在Spring上运行的
那就写一个按id查询返回操作的代码吧:
在接口的代码中,关于传参还是要用到注解,这里的@Param注解内部写的“id”意思是把你传入的参数id命名为id存进spring中(换句话说存到了MyBatis中)。
4.2 获取动态参数
在xml文件中的sql语句里我们在设置条件的时候 用到了 ${} 这样的形式来获取动态参数,这里其实就是直接替换的方式,啥意思呢?就是直接将 id = 后面的值直接更换成在Spring中存取的id的值,还有一种方式 是 #{} ,它的意思是先写成 id = ? 的形式,就是先用个占位符,然后再替换值,二者有啥区别?
正如上面的代码,因为是id数值型数据,我们无法感知到啥区别,但如果传递的参数是String的呢?用直接替换的方式的话,需要在外面加上个 ' ',因为sql语句进行存在String操作的时候需要加上单引号,而直接替换的方式,无法进行自动加' ' ,#{}则不同,先使用的是占位符,然后识别到String 再进行替换,替换的时候就相当于自动加上了' '
在 MyBatis 中,#{} 和 ${} 都是用于 SQL 语句中的变量替换,但它们的使用方式和作用是不同的。
#{} 主要用于向 SQL 中传递参数,可以防止 SQL 注入攻击。在 SQL 执行前,MyBatis 会将 #{} 替换成一个占位符 "?", 然后使用 PreparedStatement 进行预编译,最终执行 SQL 语句时将参数值设置到占位符中。因此,使用 #{} 可以提高 SQL 执行的安全性和效率。
例如:
SELECT * FROM user WHERE id = #{id}
${} 主要用于替换 SQL 语句中的表名、列名等部分,不能防止 SQL 注入攻击,不会对参数进行预编译处理。在 SQL 执行前,MyBatis 会直接将 ${} 替换成变量的值,然后与其他 SQL 文本一起拼接成最终的 SQL 语句。因此,使用 ${} 可以方便地构造动态 SQL,但同时也带来了安全隐患。
例如:
SELECT * FROM ${tablename} WHERE id = ${id}
总结一下,#{} 主要用于传递参数,防止 SQL 注入攻击;${} 主要用于替换 SQL 语句中的表名、列名等部分,方便构建动态 SQL,但存在 SQL 注入风险
如果是排序查询的话,那就只能使用${},因为如果是使用占位符的形式的话,因为spring识别到 #{}里面的数据是String类型,在替换的时候,会在外面加上个' '(sql),无法进行正常的排序查询。
5.增删改操作
5.1修改操作
5.2 删除操作
5.3 增加操作
5.3.2 返回id的增加操作
在xml文件修改一下
6. 查询操作
6.1 单表查询
查询特定Id的userinfo表:
6.2 模糊查询
模糊查询就是在sql语句中使用了like关键字,但是我们如果直接使用 #{} 的话会出问题的,因为先将目标位置替换之后,因为要替换的是String类型, 替换之后会出现 like ' ... ' 的形式~~
这时候sql中一个concat函数就能够有效的帮助我们。
虽然在增删改操作可以不设置返回的数据类型,返回一个受影响的行数就ok了,但是在查询操作中,必须是要设置返回的类型是啥,我们至今使用的是resultType ,resultType在使用的时候是非常方便的,直接指明你要返回的类型是哪个类,但是如果是实体类中的数据名称,与sql语段的名称,直接使用resultType似乎鸡肋了~~,这时候resultMap就大显神威了。
6.3 resultMap
我们先将实体类中的password改成pwd,使用上面BaseMap也能正常运行。
6.2 多表查询
6.2.1 一对一
想象一下场景:我们想获取文章表的数据并且在数据里面有用户表的username属性。
这样就ok了。
6.2.2 一对多
我们再假设个场景:当你输入一个uid也就是文章表的用户id,就查询到该用户的所有文章。
7. 动态SQL
先讲讲为什么用动态sql吧:
使用动态 SQL 可以让 SQL 语句更加灵活、可维护和易于扩展。具体来说,动态 SQL 的优点包括:
条件分支:动态 SQL 可以根据不同的条件拼接不同的SQL语句,从而实现不同的查询需求,这比使用多个固定 SQL 的方式更加灵活。
参数化:动态 SQL 可以使用参数化语法,从而可以避免 SQL 注入攻击,并且可以减少 SQL 执行计划的生成次数,提高 SQL 的执行效率。
可维护性:使用动态 SQL 可以将 SQL 语句拆分成多个小段,每个小段都有明确的含义和作用,这样就方便后期维护和修改,而不必修改整个 SQL 语句。
可复用性:使用动态 SQL 可以将多个相似的 SQL 语句抽象成一个通用的 SQL 模板,不仅可以减少代码量,还可以提高代码复用性。
总之,使用动态SQL可以让开发者更加简便地编写 SQL 语句,并且可以在不牺牲性能的情况下提高系统的灵活性、可维护性和可扩展性。
7.1 if标签
这里常见的适用场景是在我们进入用户信息填写的时候,有的信息是必填也有的是不必填的,那些不是必须要填的,我们往往会空着,意思是就是在insert语句中values后面的值没有他就ok了,那这种类似条件判断的实现就要靠if标签实现了。
我们设计一下添加用户表的场景 具体代码如下:
7.2 trim标签
注意下面的代码,trim标签里面prefix的意思是这个整体前面加个 "(" suffix则是在结尾处加个 ")"
而suffixOverrides则是去掉最后面的元素(指的是在 )前面的元素),意思就是如果说pwd不为空传进去的话,此时会加上 “ ,” 就会报错,实现suffixOverrides的话就可以避免这种情况了。
7.3 where标签
当然有了上面的trim标签似乎也可以实现动态的trim标签就是删除多的元素嘛,开头加个where就ok了,但是还有更简单的方式去实现动态where,那就是where标签。
实现一个 :你可以传与不传username和id,根据你传还是没传具体的数据来查询表结构。
<select id="getByDiy" resultType="com.example.demo.entity.UserEntity">
select * from userinfo
<where>
<if test="username != null">
and username = #{username}
</if>
<if test="id">
and id = #{id}
</if>
</where>
</select>
7.4 set标签
一样的道理,set标签相当于是trim简化版~~但需要注意的是where默认是删除前面的,set是默认删除后面的多余的字符。
7.5 foreach标签
单元测试代码
//加个事务标签进行回滚操作
@Transactional
@Test
void delByList() {
List<Integer> list = new ArrayList<>();
for(int i = 0; i < 2;i++){
list.add(i);
}
articleMapper.delByList(list);
}
xml文件
<delete id="delByList">
delete from articleinfo where id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
接口/声明方法
int delByList(List<Integer> list);
collection表明的是具体的传过去的参数集合,item就是遍历时的每一个对象,open、close就是开头结尾加上这俩东西,separator则是自动的在自动的检测添加,给遍历的对象。