动态sql是mybatis的强大特性之一,能够完成不同的sql拼接
官网文档为:
本节是对上一章内容经行的扩充
一、<if>标签

在注册用户时,我们经常会遇到必填字段和非必填字段的情况。例如,性别(gender)可能是一个非必填字段,程序需要根据用户是否填写该字段来动态生成sql语句。这时就可以使用mybatis的<if>标签来实现条件判断。
1.1 接口定义
// 根据条件插入用户信息 integer insertuserbycondition(userinfo userinfo);
1.2 mapper.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>1.3 注解方式实现(不推荐)
@insert("<script>" +
"insert into userinfo (username, `password`, age, " +
"<if test='gender!=null'>gender,</if> " +
"phone) " +
"values(#{username}, #{password}, #{age}, " +
"<if test='gender!=null'>#{gender},</if> " +
"#{phone})" +
"</script>")
integer insertuserbycondition(userinfo userinfo);注意事项:
- test属性:
test="gender != null"中的gender是传入java对象的属性名,不是数据库字段名。 - 字段默认值问题:不能简单地将字段设置为null,因为如果数据库字段有默认值,插入null会使用默认值而不是保持为空。
- 注解方式:需要使用
<script></script>标签包裹动态sql,但idea不会进行格式检查,容易出错,建议初学者使用xml方式。
二、<trim>标签
当有多个字段都可能成为选填项时,使用多个<if>标签会导致sql语句末尾出现多余的逗号。这时可以使用<trim>标签结合<if>标签,对多个字段进行动态生成。
2.1 <trim>标签属性
- prefix:表示整个语句块,以prefix的值作为前缀
- suffix:表示整个语句块,以suffix的值作为后缀
- prefixoverrides:表示整个语句块要去除掉的前缀
- suffixoverrides:表示整个语句块要去除掉的后缀
2.2 mapper.xml实现
<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>2.3 动态解析过程
在sql动态解析时,第一个<trim>部分会进行如下处理:
- 基于
prefix配置,开始部分加上( - 基于
suffix配置,结束部分加上) - 多个
<if>组织的语句都以,结尾,在最后拼接好的字符串还会以,结尾,会基于suffixoverrides配置去掉最后一个,
2.4 注解方式实现(不推荐)
@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);三、<where>标签
在实际业务中,我们经常需要根据不同的筛选条件动态组装where子句。<where>标签可以智能地处理这种情况,避免sql语法错误。
3.1 应用场景
需求:传入用户对象,根据属性做where条件查询,用户对象中属性不为null的,都作为查询条件。例如username为"a",则查询条件为where username="a"。
3.2 接口定义
// 根据条件查询用户 list<userinfo> querybycondition(userinfo userinfo);
3.3 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>3.4 <where>标签特性
- 智能处理:只会在子元素有内容的情况下才插入where子句
- 自动去除:会自动去除子句开头的and或or,避免sql语法错误
- 替代方案:可以使用
<trim prefix="where" prefixoverrides="and">替换,但这种方式在子元素都没有内容时,where关键字也会保留
3.5 注解方式实现
@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);四、<set>标签
在更新操作中,我们经常需要根据传入的对象属性来动态更新字段。<set>标签可以智能地处理update语句中的set部分,避免多余的逗号问题。
4.1 应用场景
需求:根据传入的用户id属性,修改其他不为null的属性。只更新有值的字段,避免将null值更新到数据库。
4.2 接口定义
// 根据条件更新用户信息 integer updateuserbycondition(userinfo userinfo);
4.3 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>4.4 <set>标签特性
- 智能插入:动态地在sql语句中插入set关键字
- 自动处理逗号:会删掉额外的逗号,避免sql语法错误
- 替代方案:可以使用
<trim prefix="set" suffixoverrides=",">替换,实现相同的功能
4.5 注解方式实现
@update("<script>" +
"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}" +
"</script>")
integer updateuserbycondition(userinfo userinfo);4.6 使用注意事项
- 条件判断:
test属性中的字段名是java对象的属性名 - 逗号处理:即使最后一个条件不满足,
<set>标签也会自动处理末尾的逗号 - 性能考虑:只更新必要的字段,减少数据库的写操作
六、<include>标签
在xml映射文件中配置sql时,经常会遇到很多重复的sql片段,导致代码冗余。<include>标签结合<sql>标签可以解决这个问题,通过抽取可重用的sql片段来提高代码的复用性和可维护性。
6.1 问题分析
在开发过程中,多个sql语句可能包含相同的字段列表或条件片段,例如:
<!-- 查询所有用户 -->
<select id="queryalluser" resultmap="basemap">
select id, username, age, gender, phone, delete_flag, create_time, update_time
from userinfo
</select>
<!-- 根据id查询用户 -->
<select id="querybyid" resulttype="com.example.demo.model.userinfo">
select id, username, age, gender, phone, delete_flag, create_time, update_time
from userinfo where id = #{id}
</select>可以看到,两个查询语句都包含了相同的字段列表,这违反了dry(don't repeat yourself)原则。
6.2 <sql>标签:定义可重用sql片段
<sql>标签用于定义可重用的sql片段:
<sql id="allcolumn">
id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>特性说明:
- id属性:sql片段的唯一标识符,用于在其他地方引用
- 内容:可以是字段列表、where条件、join语句等任何sql片段
- 位置:通常放在mapper.xml文件的顶部或底部,便于管理
6.3 <include>标签:引用sql片段
<include>标签通过refid属性引用已定义的sql片段:
<!-- 查询所有用户 -->
<select id="queryalluser" resultmap="basemap">
select
<include refid="allcolumn"/>
from userinfo
</select>
<!-- 根据id查询用户 -->
<select id="querybyid" resulttype="com.example.demo.model.userinfo">
select
<include refid="allcolumn"/>
from userinfo where id = #{id}
</select>6.4 使用优势
- 代码复用:避免重复编写相同的sql片段
- 维护方便:当字段列表需要修改时,只需修改一处
- 可读性高:sql语句更加简洁清晰
- 减少错误:避免因复制粘贴导致的字段遗漏或错误
6.5 高级用法:带参数的sql片段
<include>标签还可以结合<property>标签传递参数:
<!-- 定义带表名前缀的sql片段 -->
<sql id="usercolumns">
${alias}.id, ${alias}.username, ${alias}.age
</sql>
<!-- 使用带参数的sql片段 -->
<select id="queryuserwithalias" resulttype="com.example.demo.model.userinfo">
select
<include refid="usercolumns">
<property name="alias" value="u"/>
</include>
from userinfo u
</select>6.6 最佳实践建议
- 合理划分片段:将常用的字段列表、where条件、join语句等抽取为sql片段
- 统一命名规范:使用有意义的id名称,如
basecolumns、whereconditions等 - 避免过度抽取:只抽取真正需要复用的sql片段,避免过度设计
- 注释说明:为每个sql片段添加注释,说明其用途和适用场景
6.7 实际应用场景
场景一:多表查询字段复用
<sql id="userbasecolumns">
u.id, u.username, u.email, u.create_time
</sql>
<sql id="orderbasecolumns">
o.id as order_id, o.order_no, o.amount, o.status
</sql>
<select id="queryuserwithorders" resultmap="userordermap">
select
<include refid="userbasecolumns"/>,
<include refid="orderbasecolumns"/>
from user u
left join orders o on u.id = o.user_id
where u.id = #{userid}
</select>场景二:复杂条件复用
<sql id="activeusercondition">
delete_flag = 0 and status = 1
</sql>
<select id="queryactiveusers" resulttype="com.example.demo.model.userinfo">
select * from userinfo
where <include refid="activeusercondition"/>
</select>
<select id="countactiveusers" resulttype="java.lang.integer">
select count(*) from userinfo
where <include refid="activeusercondition"/>
</select>七、小结
到此这篇关于mybatis动态if/trim/where/set标签详解的文章就介绍到这了,更多相关mybatis if/trim/where/set标签内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论