Mybatis操作数据库(进阶)
一、动态SQL
dongtaiSQL是Mybatis的强大特性之一,能够完成不同条件下不同的sql拼接,可以参考官方文档:
https://mybatis.net.cn/dynamic-sql.html
(一)<if>标签(输入判断)
场景:当我们登录某网站进行注册,在资料提交时。
注册分为两种字段:必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢? 这个时候就需要使用动态标签来判断了,比如添加的时候性别 gender 为非必填字段,具体实现如下:
UserInfoMapperXML.java文件:
package com.bite.mybatis.mapper;import com.bite.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;
@Mapper
public interface UserInfoMapperXML {Integer insertUserByCondition(UserInfo userInfo);
}
UserInfoMapperXML.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.bite.mybatis.mapper.UserInfoMapperXML"><insert id="insertUserByCondition">INSERT INTO user_info (username,`password`,age,<if test="gender != null">gender,</if>phone)VALUES (#{username},#{password},#{age},<if test="gender != null">#{gender},</if>#{phone})</insert>
</mapper>
UserInfoMapperXMLTest.java文件:
package com.bite.mybatis.mapper;import com.bite.mybatis.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserInfoMapperXMLTest {@Autowiredprivate UserInfoMapperXML userInfoMapperXML;@Testvoid insertUserByCondition() {UserInfo userInfo = new UserInfo();userInfo.setUsername("zz");userInfo.setPassword("password");userInfo.setAge(18);userInfo.setPhone("10086");userInfoMapperXML.insertUserByCondition(userInfo);}}
测试结果:

从以上测试中可知,可以使用<if>标签来设置非必填项,如果用户没有输入该数据,就不会被填入,实现了动态SQL。
(二)<trim>标签(处理多个选填项)
之前的插入用户功能,只是有一个 gender 字段可能是选填项,如果有多个字段都为选填项,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式。
标签中有如下属性:
- prefix:表示整个语句块,以 prefix 的值作为前缀
- suffix:表示整个语句块,以 suffix 的值作为后缀
- prefixOverrides:表示整个语句块要去除掉的前缀
- suffixOverrides:表示整个语句块要去除掉的后缀
UserInfoMapperXML.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.bite.mybatis.mapper.UserInfoMapperXML"><insert id="insertUserByCondition">INSERT INTO user_info<trim prefix="(" suffix=")" suffixOverrides=","><if test="username != null">username,</if><if test="password != null">`password`,</if><if test="age != null">age,</if><if test="gender != null">gender,</if><if test="phone != null">phone,</if></trim>VALUES<trim prefix="(" suffix=")" suffixOverrides=","><if test="username != null">#{username},</if><if test="password != null">#{password},</if><if test="age != null">#{age},</if><if test="gender != null">#{gender},</if><if test="phone != null">#{phone},</if></trim>
</insert>
</mapper>
上述代码会将参数的前后加上括号,并且去掉整个参数后面的逗号。
(三)<where>标签(条件查询)
当我们浏览购物平台时,会遇到对商品进行筛选的场景。
这种如何实现呢? 接下来我们看代码实现:需求:传入的用户对象,根据属性做where条件查询,用户对象中属性不为 null 的,都为查询条件。如username 为 "a",则查询条件为 where username="a"。
接口定义:
List<UserInfo> queryByCondition();
UserInfoMapperXML.xml文件:
<select id="queryByCondition">select * from user_info<where><!-- 只有当属性不为null时,才会拼接该条件 --><if test="username != null">and username = #{username}</if><if test="age != null">and age = #{age}</if><if test="gender != null">and gender = #{gender}</if><if test="phone != null">and phone = #{phone}</if><if test="deleteFlag != null">and delete_flag = #{deleteFlag}</if></where></select>
当使用where标签时,如果语句块中有查询条件,会添加where关键字,还会去除最前面的and关键字。
(四)<set>标签(更新数据)
需求:根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。
接口定义:根据传入的用户 id 属性,修改其他不为 null 的属性。
接口定义:
Integer updateUserByCondition(UserInfo userInfo);
UserInfoMapperXML.xml文件:
根据指定的id修改数据库中的对应值。
<update id="updateUserByCondition" parameterType="com.bite.mybatis.model.UserInfo">UPDATE userinfo<set><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="deleteFlag != null">delete_flag = #{deleteFlag},</if></set>WHERE id = #{id}</update>
测试代码:
@Testvoid updateUserByCondition() {UserInfo userInfo=new UserInfo();userInfo.setId(7);userInfo.setUsername("nn");userInfo.setAge(17);userInfoMapperXML.updateUserByCondition(userInfo);}
先创建出一个UserInfo对象,设置它的属性,然后在数据库中找到要修改的对象,将它的值改成才创建的对象的值。
使用<set>标签时,动态地在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中) 以上标签也可以使用 <trim prefix="set" suffixOverrides=",">替换。
(五)<foreach>标签(处理集合)
对集合进行遍历时可以使用该标签。标签有如下属性:
- collection:绑定方法参数中的集合,如 List,Set,Map 或数组对象
- item:遍历时的每一个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的字符串
需求:根据多个userID,删除用户数据。
比如:sql语句为 delete from user_info where id in (1,2,3,4,5)
接口方法:
void deleteByIds(List<Integer> ids);
UserInfoMapperXML.xml文件:
<delete id="deleteByIds">delete from user_infowhere id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></delete>
测试代码:
@Testvoid deleteByIds() {List<Integer> ids=new ArrayList<>();ids.add(4);ids.add(8);userInfoMapperXML.deleteByIds(ids);}
(六)<include>标签
在xml映射文件中配置的sql,有时候可能会存在许多重复的片段,此时就会存在冗余的代码:

我们可以对重复的代码片段进行抽取,将其通过 <sql> 标签封装到一个 SQL 片段,然后再通过 <include> 标签进行引用。
<sql>:定义可重用的 SQL 片段<include>:通过属性 refid,指定包含的 SQL 片段
使用<sql>语句来封装重复的SQL片段:
<sql id="allColumn">id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>
使用<include>语句来使用封装好的SQL片段:
<select id="queryAllUser" resultMap="BaseMap">select<include refid="allColumn"></include>from userinfo
</select><select id="queryById" resultType="com.example.demo.model.UserInfo">select<include refid="allColumn"></include>from userinfo where id = #{id}
</select>
二、案例练习(表白墙)
在之前学习的案例中(https://blog.csdn.net/2301_79204376/article/details/151903418?spm=1011.2415.3001.5331),我们写了表白墙,但是一旦服务器重启,所有的数据都会消失。
想要数据不丢失,需要把数据存储在数据库中,接下来咱们使用Mybatis来实现数据的操作。

(一)数据准备
创建数据库
DROP TABLE IF EXISTS message_info;
CREATE TABLE `message_info` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`from` VARCHAR ( 127 ) NOT NULL,`to` VARCHAR ( 127 ) NOT NULL,`message` VARCHAR ( 256 ) NOT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常,1-删除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
以上代码,在数据库中创建了一个名为message_info的表。
引入Mybatis和Mysql驱动依赖
有两种方式:
第一种:在pom文件中添加一下代码
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency>
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
第二种:使用插件EditStarters来引入依赖
在添加处 右键选择Generate中的EditStarters插件


勾选SQL中的Mybatis Framework和Mysql Driver选项,即可添加驱动依赖。
配置Mysql账号和密码
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration: # 配置打印 MyBatis日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true # 配置驼峰自动转换
(二)后端代码
MessageInfo.java:
package com.bite.demo.model;import lombok.Data;import java.util.Date;@Data
public class MessageInfo {private Integer id;private String from;private String to;private String message;private Integer deleteFlag;private Date createTime;private Date updateTime;
}
MessageInfoMapper.java:
package com.bite.demo.mapper;import com.bite.demo.model.MessageInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface MessageInfoMapper {@Select("select `id`, `from`, `to`, `message` from message_info where delete_flag=0")List<MessageInfo> queryAll();@Insert("insert into message_info (`from`, `to`, `message`) values(#{from}, #{to}, #{message})")Integer addMessage(MessageInfo messageInfo);
}
MessageInfoService.java:
package com.bite.demo.service;import com.bite.demo.mapper.MessageInfoMapper;
import com.bite.demo.model.MessageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class MessageInfoService {@Autowiredprivate MessageInfoMapper messageInfoMapper;public List<MessageInfo> queryAll() {return messageInfoMapper.queryAll();}public Integer addMessage(MessageInfo messageInfo) {return messageInfoMapper.addMessage(messageInfo);}
}
MessageInfoController.java:
package com.bite.demo.controller;import com.bite.demo.model.MessageInfo;
import com.bite.demo.service.MessageInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RequestMapping("/message")
@RestController
public class MessageController {@Autowiredprivate MessageInfoService messageInfoService;@PostMapping("/publish")public String publish(@RequestBody MessageInfo messageInfo){//判断前端上传的数据是否合法if(!StringUtils.hasLength(messageInfo.getFrom())||!StringUtils.hasLength(messageInfo.getMessage())||!StringUtils.hasLength(messageInfo.getTo())){//返回json格式return "{\"ok\": 0}";}messageInfoService.addMessage(messageInfo);return "{\"ok\": 1}";}//获取所有留言@RequestMapping("getList")public List<MessageInfo> getList(){return messageInfoService.queryAll();}
}
