Mybatis-动态SQL
1. <if>标签
我们在注册用户或者别的业务场景的时候会出现这样一个问题.
注册分为两种字段: 必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现,这时候就需要使用动态标签来判断了,比如添加的性别gender为非必填字段,具体实现如下:
接口定义:
Integer insertUserByCondition(UserInfo userInfo);
XML实现
<insert id="insertUserByCondition">insert into userinfo (username, password, age,<if test="gender != null">gender,</if>phone)values (#{userName},#{password},#{age},<if test="gender != null">#{gender},</if>#{phone})
</insert>
或者使用注解的方式(不推荐)
把上面的SQL(包括标签),使用<script></script>标签括起来就可以
@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);
注意test中的gender,是传入对象的属性,不是数据库字段
2. <trim>标签
之前插入的用户功能,只有一个gender字段可能是选填项,如果有多个字段,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式。
标签中有如下属性:
- prefix:表示整个语句块,以prefix的值作为前缀
- suffix:表示整个语句块,以suffix的值作为后缀
- prefixOverrides:表示整个语句块要去除的前缀
- suffixOverrides:表示整个语句块要去除的后缀
调整Mapper.xml的插入语句为:
<insert id="insertUserByCondition">insert into mybatis_test.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>
或者使用注解(不推荐)
@Insert("<script>" +"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>"+"</script>")
Integer insertUserByCondition(UserInfo userInfo);
在以上sql动态解析时,会将第一个部分做如下处理:
基于prefix配置,开始部分加上(
基于suffix配置,结束部分加上 )
多个组织的语句都以,结尾,在最后拼接好的字符串还会以,结尾,会基于suffixOverrides配置去掉最后一个,
注意<if test = "userName != null" > 中的userName是传入对象的属性
3. <where>标签
看下面这个场景,系统会根据我们的筛选条件,动态组装where条件
这种该如何实现?
接下来我们看代码实现:
需求:传入的用户对象,根据属性作where条件查询,用户中的属性不为null的,都为查询条件。如u为a,则查询条件为where userName = "a"
原有SQL
SELECT * FROM userinfo WHERE age = 18 AND gender = 1 AND delete_flag = 0
接口定义
List<UserInfo> queryByCondition();
XML实现
<select id="queryByCondition" resultType="com.adviser.springmybaitstest.model.UserInfo">select id, username, password, age, gender, phone, delete_flag, create_time, update_timefrom 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
或者使用注解的方式
@Select("<script>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>" +"</script>")
List<UserInfo> queryByCondition(UserInfo userInfo);
4. <set>标签
需求:根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。
接口定义:根据传入的用户id属性,修改其他不为null的属性
Integer updateUserByCondition(UserInfo userInfo);
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关键字,并会删除额外的逗号
5. <foreach>标签
对集合进行遍历的时候可以使用该标签。标签有如下属性:
collection:绑定方法参数中的集合,如List,Set,Map或数组对象
item:遍历时的每一个对象
open:语句块开头的字符串
close:语句块结束的字符串
separator:每次遍历之间间隔的字符串
需求:根据多个userid,删除用户数据
接口方法
void deleteByIds(List<Integer> ids);
XML实现
<delete id="deleteByIds">delete from userinfowhere id in <foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach>
</delete>
或者使用注解
@Delete("<script>" +"delete from userinfo where id in" +"<foreach collection='ids' item='id' separator=',' open='('
close=')'>" +"#{id}" +"</foreach>" +"</script>")
Integer deleteUser(Integer id);
6. <include>标签
在XML映射文件中配置的SQL,有时可能会有很多重复的片段,此时就会存在很多冗余的代码
我们可以对重复的代码片段进行抽取,将其通过<sql>标签封装到一个SQL片段,然后在通过<include>标签进行引用。
<sql>定义可重用的sql片段
<include>:通过属性refid,指定包含的SQL片段
<sql id= "allColumn">id, username, password, age, gender, phone, delete_flag, create_time, update_time
</sql>
通过<include>标签在原来抽取的地方进行引用:
<select id="queryByCondition" resultType="com.adviser.springmybaitstest.model.UserInfo">select <include refid="allColumn"></include>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><select id="queryAllUser" resultType="com.adviser.springmybaitstest.model.UserInfo">select <include refid="allColumn"></include>from userinfo where id = #{id}</select>