目录
动态SQL 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接。
官方文档:Mybatis动态SQL
1. <if>标签
在某些注册用户界面,有必填字段和非必填字段两种,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢? 这个时候就需要使用动态标签来判断了,比如添加的时候性别 gender 为非必填字段,具体实现为:
接口定义:
Integer insertUserByCondition(UserInfo userInfo);
Mapper.xml
<insert id="insertUserByCondition">
INSERT INTO userinfo (
username,
`password`,
age,
<if test="gender != null">
gender,
</if>
phone)
VALUES (
#{username},
#{age},
<if test="gender != null">
#{gender},
</if>
#{phone})
</insert>
注意 test 中的 gender,是传入对象中的属性,不是数据库字段。
注解方式实现(不推荐,因为麻烦且容易出错):
@Insert("<script>" +
"INSERT INTO userinfo (username,`password`,age," +
"<if test='gender!=null'>gender,</if>" +
"phone)" +
"VALUES(#{username},#{age}," +
"<if test='gender!=null'>
#{gender},
</if>" +
"#{phone})"+
"</script>")
Integer insertUserByCondition(UserInfo userInfo);
2. <trim>标签
如果选填项有多个字段,就会考虑使用标签结合标签,对多个字段都采用动态生成的方式。
标签中有如下属性:
-
prefix:表示整个语句块,以prefix的值作为前缀
-
suffix:表示整个语句块,以suffix的值作为后缀
-
prefixOverrides:表示整个语句块要去除掉的前缀
- suffixOverrides:表示整个语句块要去除掉的后缀
<insert id="insertUserByCondition">
INSERT INTO userinfo
<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>
注解方式同上,此处及下面不再举例。
- 基于 prefix 配置,开始部分加上 (
- 基于 suffix 配置,结束部分加上 )
- 多个组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于suffixOverrides 配置去掉最后一个
- 注意 <if test="username !=null"> 中的 username 是传入对象的属性
3. <where> 标签
当筛选条件有多个的时候会动态组装where条件。
举例:传入的用户对象,根据属性做where条件查询,用户对象中属性不为 null 的,都为查询条件。 如 username 为 "a",则查询条件为 where username="a"
原有SQL:
SELECT * FROM userinfo WHERE
age = 18
AND gender = 1
AND delete_flag =0;
List<UserInfo> queryByCondition();
Mapper.xml实现:
<select id="queryByCondition" resultType="com.example.demo.model.UserInfo">
select id, username, age, gender, phone, delete_flag, create_time,
update_time
from userinfo
<where>
<if test="age != null">
and age = #{age}
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="deleteFlag != null">
and delete_flag = #{deleteFlag}
</if>
</where>
</select>
<where> 只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头AND或 OR。以上标签也可以使用 <trim prefix="where" prefixOverrides="and"> 替换, 但是此种情况下, 当子元素都没有内容时, where关键字也会保留。
4. <set>标签
Integer updateUserByCondition(UserInfo userInfo);
Mapper.xml:
<update id="updateUserByCondition">
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>
<set> :动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)
以上标签也可以使用 <trim prefix="set" suffixOverrides=","> 替换。
5. <foreach>标签
- collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
- item:遍历时的每一个对象
- open:语句块开头的字符串
- close:语句块结束的字符串
- separator:每次遍历之间间隔的字符串
void deleteByIds(List<Integer> ids);
<delete id="deleteByIds">
delete from userinfo
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
6. <include>标签
在xml映射文件中配置的SQL,有时可能会存在很多重复的片段,此时就会存在很多冗余的代码:
可以对重复的代码片段进行抽取,将其通过 <sql> 标签封装到一个SQL片段,然后再通过 <include> 标签进行引用。
- <sql> :定义可重用的SQL片段
- <include> :通过属性refid,指定包含的SQL片段
<sql id="allColumn">
id, username, age, gender, phone, delete_flag, create_time, update_time
</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>