动态sql
动态sql是MyBatis的强大特性之一, 能够完成不同条件下的sql拼接.
<if>标签
在注册用户的问题时, 可能会有这样的一个问题:就是说注册时有一些信息是必填的, 而有一些信息是选填的. 那么如果在添加用户的时候有不确定字段的传入, 程序应该如何实现呢?
这时就可以使用动态标签来判断了, 比如添加的时候, 性别gender为非必填字段, 我们通过使用<if>标签来指定这个非必填的内容, 它的语法格式如下:
<if test="条件">语句</if>: 如果test后面的条件成立的话, 就会拼接if标签里的语句.
接口定义:
Integer insertByCondition(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>
该实现是能够解决一些非必填的情况的, 但是如果有多个, 甚者是拼接在头尾的, 有时就可能出现问题, 比如说:
1.如果phone也是非必填字段, 但最后并没有填, 这就会使得gender后面的 , 结尾的情况下导致sql语句错误. 但也有老铁们就要说了, 我就不能把 , 都放在每个字段的前面吗(除了username)?这就是2的问题
2.如果username也是非必填, 但是最后没有填, 后面的有些填了, 这就使得会出现 , 开头的情况, 也会导致语法错误.
<trim>标签
之前的用户插入功能, 只是有一个gender字段可能是选填项, 如果有多个字段, 一般考虑使用标签结合标签, 对多个字段都产生动态生成的方式.
标签中有如下属性:
prefix: 表示整个语句块, 以prefix的值作为前缀.
suffix: 表示整个语句块, 以suffix的值作为后缀.
prefixOverrides: 表示整个语句块要去掉的前缀.
suffixOverrides: 表示整个语句块要去掉的后缀
有了这个, 上面的问题也就迎刃而解了, 我们通过可以设置逗号为前缀/后缀的方式灵活地操作逗号的存在, 具体的语句如下:
<insert id="insertByCondition">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>
具体来说, 在以上sql动态解析时, 会将第一个部分做如下处理:
基于prefix配置, 开始部分加上 (
基于suffix配置, 结束部分加上 )
多个组织的语句都以逗号结尾, 在最后拼接好的字符串还会以逗号结尾, 会基于suffixOverrides配置去掉最后一个逗号.
特别注意: <if test="usernae != null">中的username是传入对象的属性.
<where>标签
看下面这个场景, 系统会根据我们的筛选条件, 动态组装where条件.
接下来看代码实现:
需求: 传入的用户对象, 根据属性做where条件查询, 用户对象中属性不为null的, 都为查询条件. 如果username为"a", 则查询条件为where username = "a", 如果有多个条件, 中间会拼接"and".
原有sql:
SELECT*
FROMuserinfo
WHEREage = 18AND gender = 1AND delete_flag =0
接口定义:
List<UserInfo> queryByCondition();
Mapper.xml实现:
<select id="queryUserByCondition" resultType="com.bit.mybatis.testmybatis.model.UserInfo">select * from userinfo<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>只会在子元素有内容的情况下才插入where子句, 而且会自动去除子句的开头AND或OR
以上标签也可以使用<trim prefix="where" prefixOverrides="and">替换, 但是此种情况下, 当子元素都没有内容时, where关键字也会保留.
<set>标签
需求: 根据传入的用户对象属性来更新用户数据, 可以使用标签来指定动态内容.
接口定义: 根据传入的用户id属性, 修改其它不为null的属性.
Integer updateUserByCondition(UserInfo userInfo);
Mapper.xml
<update id="updateByCondition">update userinfo<set><if test="username != null">username = #{username},</if><if test="password != null">password = #{password},</if></set>where id = #{id}</update>
<set>:动态地在SQL语句中插入set关键字, 并删掉额外的逗号.(用于update语句中).
以上标签也可以使用<trim prefix="set" suffixOverrides=",">替换
<foreach>标签
之前我们在MySQL中学习条件查询的时候, 学到过类似 in (a, b, c, d)这样的语句, 表示满足任意一个条件, 都会返回true.
对集合进行遍历时可以使用该标签. 该标签有如下属性:
collection: 绑定方法参数中的集合, 如List, Set, Map或数组对象
item:遍历时的每个对象
open: 语句块开头的字符串
close: 语句块结束的字符串
separator: 每次遍历之间间隔的字符串.
需求: 根据多个userid,删除用户数据:
接口方法:
void deleteByIds(List<Integer> ids);
ArticleMapper.xml中新增删除sql:
<delete id="batchDelete">delete from userinfowhere id in
<!-- (1, 2, 3, 4)--><foreach collection="ids" open="(" close=")" item="id" separator=",">#{id}</foreach></delete>
<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>
通过<include>标签在原来抽取的地方进行引用. 操作如下:
<select id="queryAllUser" resultMap="BaseMap">select<include refid="allColumn"></include>from userinfo
</select>