目录
1.MyBatis开发环境的搭建和测试
2.MyBatis基本操作
2.0 准备工作
2.1 新增操作
2.2 删除、修改、查询操作
2.3 #{param} 和 ${param}的使用和区别
2.4 实体对象属性和数据库字段名称不同时如何映射?
3. MyBatis多表查询
3.0 准备工作
3.1 一对一的表的映射
3.2 一对多的表的映射
4.MyBatis动态SQL
4.0 准备工作
4.1 if-标签
4.2 trim-标签
4.3 where-标签
4.4 set-标签
4.5 foreach-标签
1.MyBatis开发环境的搭建和测试
MyBatis环境的搭建和测试可以参考之前发布的:MyBatis基本介绍以及开发环境的搭建和测试
2.MyBatis基本操作
我们先来使用MyBatis实现基本表的增删改查操作,在熟悉了这些基本操作后,再来学习MyBatis多表查询的实现。在进行学习之前,我们还是来回顾下MyBatis在我们后端程序中的地位:
我们在上一篇文章中学习到,MyBatis是通过定义mapper接口并找到对应的实现接口的xml文件整合,最后动态生成sql,借助jdbc操作来操作数据库的。所以我们对数据库表的操作从整体上应当分为两步:
- 定义mapper接口
- 定义实现mapper接口的MyBatis的XML文件
接下来我们通过学习MyBatis的基本操作来理解上述它操作数据库的两个步骤。下面我们的操作都基于下面这个项目的框架模板进行:
2.0 准备工作
下面我们将要学习的的四种基本操作基于之前搭建环境时在数据库创建的一张user表来实现。在开始之前我们要先创建好用户相关的mapper接口和和数据库中user表对应的实体映射类,如下图:
2.1 新增操作
MyBatis实现基本新增操作的整体步骤:
- 在UserMapper接口中声明新增用户的方法
- 在对应的实现接口的xml文件中编写<insert>标签实现接口方法
实现过程中的注意事项和实现细节如下图:
2.2 删除、修改、查询操作
这些操作与2.1中的实现步骤类似,我们将其总结到了一块:
- 在UserMapper接口中定义相关的服务方法
- 在UserMapper.xml文件中选用对应的标签并编写sql语句实现接口中的方法
其中,各个操作的详细步骤和注意事项如下:
2.3 #{param} 和 ${param}的使用和区别
在上面的UserMapper的xml文件中,我们提到对于sql语句中未知量的赋值可以通过#{param}或者${param}赋值。那么既然都可以用来给sql语句赋值,那么它们之间有什么使用上的区别呢?(param:可能是传递的对象中的属性值也可能是传递的参数值)
1. '$'是直接替换,'#'是预处理。
这样说可能我们都不太好理解,我们来实际应用中看一下:
2.使用‘$’可能会存在sql注入的问题,使用'#'是安全的。
既然‘$’是直接替换,那么sql注入的安全问题就来了,下面我们来看一下使用$时带来的sql注入安全问题:
那既然$和#都能实现需求,为什么不直接全是用#而还要有$呢?
这是因为在需要传递的参数是数据库中的关键字时,比如需要升序或降序排序的场景下我们必须使用$来拼接as或者desc字段,只能使用$,使用#就无法实现这个功能了。
比如:
-- 正常情况下,使用$传递排序参数(asc、desc)数按照id字段升序排列用户的sql语句 select * from user order by id asc; -- 而使用 # 传递排序参数的sql语句形态为:(错误sql❌) select *from user order by id 'asc';
2.4 实体对象属性和数据库字段名称不同时如何映射?
不知道大家有没有发现,我们在上面User实体类中有些属性的命名是不规范的,这是为了符合数据库的规范以求能够映射数据到相应实体对象的字段上。
同时在实际的开发中,由于开发人员和数据库设计人员可能不是同一个人来完成的,同时数据库规范和Java开发规范优势不相同的。那么就非常可能存在数据库表的字段名和开发中实体类的属性名不相同。那么在默认查询时使用我们的select标签的resultType指定的实体对象中的属性名由于和数据库中对应表的字段名不同,就不能正常映射数据库表中字段的值了。那么应该如何解决这种映射问题呢?我总结了以下两种方法:
1. 写查询语句时将查询字段重命名为我们实体对象中对应的属性名,即可正确映射。
2.使用resultMap属性,设计表字段和实体类属性的映射关系。
3. MyBatis多表查询
3.0 准备工作
为了学习MyBatis多表查询,我们在数据库中再创建一张评论表,这个评论表中有一个用户编号属性来指明该条评论是那个用户发表的,如下:
对数据库中的评论表,在程序中创建Comment实体以及CommentMapper接口和实现的xml文件:
3.1 一对一的表的映射
这里我们通过查询评论表来学习一对一的表的映射。在我们返回给前端评论表中指定评论的信息时,需要返回的是评论的用户而不是评论表中的用户的id信息。评论表中的用户和用户表中的用户是一对一的关系,要实现返回给前端的数据中包含评论的相关信息以及评论的用户的名称而不是评论用户id,这个时候我们应该怎么做呢?
定义一个Comment视图实体对象,其中包含用户名属性。在进行数据库查询时使用联表查询,返回给前端这个视图实体对象中的信息即可!
如下图所示:
3.2 一对多的表的映射
如果现在我们要根据用户编号查询其发布的所有评论?又该如何实现呢?其实虽然用户和评论之间存在一对多的关系,但是仔细一看就会发现,我们的评论表中就存在着用户编号,所以我们通过单表查询就可以完成这个需求:
4.MyBatis动态SQL
4.0 准备工作
为了学习MyBatis动态SQL中使用的标签,我们接着上面的学习,再在MySQL的user用户表中新增一个默认约束的头像字段。下面我们的学习将使用到这个字段。
4.1 if-标签
在MyBatis中,if标签常用来判断:当某一条件满足时才进行SQL语句的拼装,这个判断条件写在MyBatis的xml文件中的if标签的test属性中。
<insert id="addDefault" useGeneratedKeys="true" keyProperty="id"> insert into user( username, password <if test="photo != null"> ,photo </if>) values ( #{username}, #{password} <if test="photo != null"> , #{photo} </if>) </insert>
比如现在的需求,用户在注册时可以不上传头像,此时默认使用数据库中默认约束所指定的头像信息。那么我们应该如何编写SQL语句呢?if标签使用的注意事项和细节又有哪些呢?让我们通过实现上面的需求来一起探索:
4.2 trim-标签
在4.1的需求中,只有photo是一个选填项。如果现在有一个需求:所有用于填写的字段都是选填项,在数据库中也有对应的默认值约束。这个时候又应该怎么实现呢?
很显然,此时就不能再继续使用if标签了,这是因为:
于是这个时候就需要我们的trim标签登场了!我们先来看下trim标签有哪些关键字:
- prefix——前缀值
- suffix——后缀值
- prefixOverrides——整个语句块要去掉的前缀
- suffixOverrides——整个语句块要去掉的后缀
<insert id="addAllDefault" useGeneratedKeys="true" keyProperty="id"> insert into user <trim prefix="(" suffix=")" suffixOverrides=","> <if test="username != null"> username, </if> <if test="password != null"> password, </if> <if test="photo != null"> photo, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="username != null"> #{username}, </if> <if test="password != null"> #{password}, </if> <if test="photo != null"> #{photo}, </if> </trim> </insert>
我们使用trim标签来实现上述当所有字段非必填时的用户注册的需求,同时总结一下trim标签的使用注意事项及细节:
4.3 where-标签
听名知意😄,where想必就是给我们SQL语句中where判读条件使用的。使用where标签时,有以下几个细节:
- 使用where标签时,我们不用再手写sql语句中的where关键字。
- 当where标签中没有任何条件时,where关键字会被省略
- where标签会帮助我们删除SQL中的前缀 ‘and’关键字
- 通trim标签一致,where标签通常也和if标签搭配使用
<select id="getUserByNameOrPwd" resultType="com.example.demo.entity.User"> select id,username,password,createtime 'createTime',photo from user <where> <if test="username != null"> and username=#{username} </if> <if test="password != null"> and password=#{password} </if> </where> </select>
上面这种学习where标签的方式还不太直观,假设现在我们要实现管理员根据用户名或者密码查询指定user表中符合条件的用户的操作,下面我们使用MyBatis提供的where标签实现动态查询SQL实现上述需求:
4.4 set-标签
用来动态设置我们再MyBatis的xml文件中编写的sql语句中的set字段后的值,set标签的使用细节及注意事项如下:
- set标签中没有我们必须要去指定的关键字
- set标签会帮助我们删除sql语句中的后缀英文逗号
<update id="updateUserInfo"> update user <set> <if test="username != null"> username=#{username}, </if> <if test="password"> password=#{password}, </if> <if test="photo"> photo=#{photo}, </if> </set> where id=#{id} </update>
了解了set标签的作用,我们就想到了它可以用于信息的修改的需求,当我们不知道用户究竟要修改某个表中的那个字段时,可以通过set标签结合前端传递的参数来动态的组合sql语句中set语句块的值,实现动态信息的修改。老规矩,我们还是实现下上述需求来总结下set标签使用的规则和注意事项:
4.5 foreach-标签
当我们在遇到需要批量操作数据的场景时,MyBatis提供的foreach标签是我们的不二选择。可能有的同学和我当初想的一样,直接在服务层加一层循环挨个处理持久层前端传过来得数据集合不就可以吗?是可以,但是效率就要低很多了!接下来我们将使用foreach标签啦进行批量请求处理。我们先来看下foreach标签中需要我们手动指定得一些关键字:
- collection——方法中集合参数名
- item——进行遍历的每一个对象名
- open——语句块开头的字符串
- close——语句块结束的字符串
- separator——每次遍历间隔之间的字符串
例:
<delete id="deleteUserBatch"> delete from user where id in <foreach collection="ids" open="(" close=")" item="id" separator=","> #{id} </foreach> </delete>
下面还是老规矩,我们借助foreach来实现批量删除用户的操作总结以下foreach标签的使用及其注意事项: