一,动态SQL
动态SQL是Mybatis的强大特性之一,能够完成不同条件下不同的sql拼接
1.1 <if>标签
在注册用户的时候,可能会有这样⼀个问题:
注册分为两种字段:必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢?
这个时候就需要使用动态标签来判断了,比如添加的时候性别gender为非必填字段,具体实现如 下:
接口定义:
java
Integer insertUserInfoByCondition(UserInfo userInfo);
Mapper.xml实现:
java
<insert id="insertUserInfoByCondition">
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>
不加<if>标签时,运行结果如下:

未传参的会赋值为null
加了标签之后,运行结果为:

其中未传参的会赋上默认值
或者使用注解方式
java
@Insert("""
<script>
insert into user_info
<trim suffixOverrides="," prefix="(" suffix=")">
<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 suffixOverrides="," prefix="(" suffix=")">
<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>
</script>
""")
Integer insertUserInfoByCondition(UserInfo userInfo);
注意test中的gender,是传入对象中的属性,不是数据库字段(上述例子中,test里面是判断条件,判断 #{gender}是否为空)
Q: 可不可以不进行判断,直接把字段设置为null呢?
A: 不可以, 这种情况下,如果gender字段有默认值,就会设置为默认值
1.2 <trim>标签
之前的插入用户功能,只是有⼀个gender字段可能是选填项,如果有多个字段,⼀般考虑使用标签结合标签,对多个字段都采取动态生成的方式。
标签中有如下属性:
• prefix:表示整个语句块,以prefix的值作为前缀
• suffix:表示整个语句块,以suffix的值作为后缀
• prefixOverrides:表示整个语句块要去除掉的前缀
• suffixOverrides:表示整个语句块要去除掉的后缀
调整Mapper.xml的插入语句为:
java
<insert id="insertUserInfoByCondition">
insert into user_info
<trim suffixOverrides="," prefix="(" suffix=")">
<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 suffixOverrides="," prefix="(" suffix=")">
<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>
在以上sql动态解析时,会将第⼀个部分做如下处理:
• 基于 prefix 配置,开始部分加上 (
• 基于 suffix 配置,结束部分加上 )
• 多个组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于suffixOverrides 配置去掉最后一个,
• 注意 username 是传入对象的属性
1.3 <where>标签
需求:传⼊的⽤⼾对象,根据属性做where条件查询,⽤⼾对象中属性不为null的,都为查询条件.如 username为"a",则查询条件为whereusername="a"
接⼝定义:
java
List<UserInfo> queryByCondition();
Mapper.xml实现:
java
<select id="queryByCondition" resultType="com.luoyaoyun.mybatisdemo.model.UserInfo">
<!-- SELECT * FROM user_info WHERE 1=1-->
<!-- <trim prefixOverrides="AND">-->
<!-- <if test="age!=null">-->
<!-- age = #{age}-->
<!-- </if>-->
<!-- <if test="gender!=null">-->
<!-- AND gender = #{gender}-->
<!-- </if>-->
<!-- <if test="deleteFlag!=null">-->
<!-- AND delete_flag = #{deleteFlag}-->
<!-- </if>-->
<!-- </trim>-->
SELECT * FROM user_info
<where>
<if test="age!=null">
age = #{age}
</if>
<if test="gender!=null">
AND gender = #{gender}
</if>
<if test="deleteFlag!=null">
AND delete_flag = #{deleteFlag}
</if>
</where>
</select>
上述代码有两种方法,其中where标签就完成了第一种方法中实现的两种方法,1)去除最前面的and 2)如果查询条件为空,则不添加where关键字
<where>只会在⼦元素有内容的情况下才插⼊where⼦句,⽽且会⾃动去除⼦句的开头的AND或 OR
以上标签也可以使用
<trim prefix="where" prefixOverrides="AND">
替换,但是此种情况下,当⼦元素都没有内容时,where关键字也会保留
1.4 <set>标签
需求:根据传入的⽤⼾对象属性来更新⽤⼾数据,可以使⽤标签来指定动态内容
接⼝定义:根据传⼊的⽤⼾id属性,修改其他不为null的属性
java
Integer updateUserByCondition(UserInfo userInfo);
Mapper.xml
java
<update id="updateByCondition">
<!-- update user_info-->
<!-- set-->
<!-- <trim suffixOverrides=",">-->
<!-- <if test="username!=null">-->
<!-- username=#{username},-->
<!-- </if>-->
<!-- <if test="age!=null">-->
<!-- age= #{age},-->
<!-- </if>-->
<!-- <if test="gender!=null">-->
<!-- gender = #{gender},-->
<!-- </if>-->
<!-- <if test="deleteFlag!=null">-->
<!-- delete_flag = #{deleteFlag}-->
<!-- </if>-->
<!-- </trim>-->
<!-- where id= #{id}-->
update user_info
<set>
<if test="username!=null">
username=#{username},
</if>
<if test="age!=null">
age= #{age},
</if>
<if test="gender!=null">
gender = #{gender},
</if>
<if test="deleteFlag!=null">
delete_flag = #{deleteFlag}
</if>
</set>
where id= #{id}
</update>
<set> : 动态的在SQL语句中插⼊set关键字,并会删掉额外的逗号.(用于update语句中)
1.5 <foreach>标签
对集合进⾏遍历时可以使⽤该标签。标签有如下属性:
• collection:绑定⽅法参数中的集合,如List,Set,Map或数组对象
• item:遍历时的每⼀个对象
• open:语句块开头的字符串
• close:语句块结束的字符串
• separator:每次遍历之间间隔的字符串
需求:根据多个userid,删除用户数据
接⼝⽅法:
java
void deleteByIds(List<Integer> ids);
UserInfoMapper.xml
java
<delete id="batchDelete">
delete from user_info
where id in
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
1.6 <include>标签
在xml映射⽂件中配置的SQL,有时可能会存在很多重复的⽚段,此时就会存在很多冗余的代码
我们可以对重复的代码⽚段进⾏抽取,将其通过<sql> 标签封装到⼀个SQL⽚段,然后再通过
<include>标签进⾏引⽤。
- <sql>:定义可重⽤的SQL⽚段
- <include>:通过属性refid,指定包含的SQL⽚段
java
<sql id="allColumn">
id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>
通过<include>标签在原来抽取的地⽅进⾏引⽤。
java
<select id="queryAllUser" resultMap="BaseMap">
select
<include refid="allColumn"></include>
from user_info
</select>
<select id="queryById" resultType="com.example.demo.model.UserInfo">
select
<include refid="allColumn"></include>
from user_info where id= #{id}
</select>
二. 案例练习
2.1 表白墙
2.1.1 数据准备
sql
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;
ON UPDATE now():当数据发生更新操作时,自动把该列的值设置为now(),
now() 可以替换成其他获取时间的标识符,⽐如:CURRENT_TIMESTAMP(),LOCALTIME()等
2.1.2 引入MyBatis和MySQL驱动依赖
修改pom文件:
java
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
2.1.4 编写后端代码
Model
java
@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
@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
@Service
public class MessageInfoService {
@Autowired
private MessageInfoMapper messageInfoMapper;
public List<MessageInfo> queryAll() {
return messageInfoMapper.queryAll();
}
public Integer addMessage(MessageInfo messageInfo) {
return messageInfoMapper.addMessage(messageInfo);
}
}
MessageController
java
@RequestMapping("/message")
@RestController
public class MessageController {
@Autowired
private MessageInfoService messageInfoService;
@GetMapping("/getList")
public List<MessageInfo> getList(){
return messageInfoService.queryAll();
}
@PostMapping(value = "/publish")
public boolean publish(MessageInfo messageInfo) {
System.out.println(messageInfo);
if (StringUtils.hasLength(messageInfo.getFrom())
&& StringUtils.hasLength(messageInfo.getTo())
&& StringUtils.hasLength(messageInfo.getMessage())) {
messageInfoService.addMessage(messageInfo);
return true;
}
return false;
}
}
访问这个网址即可
