Java MyBatis条件查询案例编写指南:从入门到高级实战
目录导读
- 引言:为什么需要掌握MyBatis条件查询?
- MyBatis基础回顾:配置与Mapper映射
- 简单条件查询案例:单参数与多参数
- 动态SQL条件拼接:if、where、set、trim标签详解
- 复杂条件组合:choose、when、otherwise多分支逻辑
- 关联查询与条件过滤:一对一、一对多场景
- 高级技巧:分页条件查询与批量操作
- 常见问题与问答集锦
- 总结与最佳实践
引言:为什么需要掌握MyBatis条件查询?
在实际Java企业级开发中,MyBatis作为最流行的ORM框架之一,其核心优势在于灵活的条件查询能力,据统计,超过70%的数据库操作涉及动态条件拼接,无论是简单的用户登录验证,还是复杂的报表筛选,正确编写条件案例直接决定了系统的查询效率与代码可维护性,本文将通过大量实战案例,系统讲解MyBatis条件查询的编写方法,帮助开发者避开常见陷阱。

MyBatis基础回顾:配置与Mapper映射
在编写条件查询前,确保项目已正确配置MyBatis核心组件:
XML映射文件结构:
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectByConditions" resultType="User">
<!-- 具体SQL语句 -->
</select>
</mapper>
关键点:
resultType用于简单查询,resultMap用于复杂映射。- 参数传递支持
#{param}(预编译占位符)和${param}(字符串拼接,需防注入)。
简单条件查询案例:单参数与多参数
案例1:单参数条件
// 接口方法
User selectById(@Param("id") Long id);
// XML
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
注意:即使只有一个参数,也建议使用@Param注解明确参数名。
案例2:多参数条件
List<User> selectByNameAndAge(@Param("name") String name,
@Param("age") Integer age);
<select id="selectByNameAndAge" resultType="User">
SELECT * FROM user
WHERE name = #{name} AND age = #{age}
</select>
问答:
问:如果参数为null,直接使用WHERE name = #{name}会报错吗?
答:不会报错,但查询结果不包含该条件,若需忽略null条件,需使用动态SQL。
动态SQL条件拼接:核心标签实战
1 <if>标签:根据条件动态包含片段
<select id="searchUsers" resultType="User">
SELECT * FROM user WHERE 1=1
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
</select>
注意:为什么要加WHERE 1=1?避免所有条件为假时SQL变为WHERE AND...。
2 <where>标签:智能处理WHERE子句
<select id="searchUsers" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
优势:自动去除多余AND/OR,且无1=1冗余。
3 <set>标签:动态更新字段
<update id="updateUser">
UPDATE user
<set>
<if test="name != null">name = #{name},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>
原理:自动删除末尾逗号,避免SQL语法错误。
4 <trim>标签:自定义前缀/后缀
<select id="selectWithTrim" resultType="User">
SELECT * FROM user
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="name != null">AND name = #{name}</if>
<if test="age != null">OR age = #{age}</if>
</trim>
</select>
参数说明:prefix添加前缀,prefixOverrides截断多余连接词。
复杂条件组合:多分支逻辑
1 <choose><when><otherwise>:多路选择
<select id="selectByChoose" resultType="User">
SELECT * FROM user
<where>
<choose>
<when test="name != null">
AND name = #{name}
</when>
<when test="age != null">
AND age = #{age}
</when>
<otherwise>
AND status = 'ACTIVE'
</otherwise>
</choose>
</where>
</select>
场景:实现“按名称查询,若名称无则按年龄,若都无则查活跃用户”。
关联查询与条件过滤
1 一对一关联条件
<resultMap id="UserWithRoleMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="role" javaType="Role">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
</association>
</resultMap>
<select id="selectUserWithRole" resultMap="UserWithRoleMap">
SELECT u.*, r.role_name
FROM user u
LEFT JOIN role r ON u.role_id = r.role_id
<where>
<if test="roleName != null">
r.role_name = #{roleName}
</if>
</where>
</select>
2 一对多集合条件
<resultMap id="UserWithOrdersMap" type="User">
<id property="id" column="id"/>
<collection property="orders" ofType="Order"
select="selectOrdersByUserId"
column="id">
</collection>
</resultMap>
<select id="selectOrdersByUserId" resultType="Order">
SELECT * FROM orders WHERE user_id = #{userId}
<if test="orderStatus != null">
AND status = #{orderStatus}
</if>
</select>
注意:子查询中的条件参数需通过column传值。
高级技巧:分页条件查询与批量操作
1 分页+动态条件(使用PageHelper插件)
// Service层
PageHelper.startPage(pageNum, pageSize);
List<User> list = userMapper.selectByPage(name, age);
PageInfo<User> pageInfo = new PageInfo<>(list);
// XML无需改动,只保留动态条件
<select id="selectByPage" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">AND name = #{name}</if>
<if test="age != null">AND age = #{age}</if>
</where>
</select>
2 批量条件查询:<foreach>
<select id="selectByIds" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
参数:collection必须对应接口的@Param("idList")。
问答:
问:<foreach>处理大量数据时性能如何?
答:一般建议列表长度不超过1000,否则考虑分批次查询或临时表。
常见问题与问答集锦
Q1:动态条件中如何处理IN子查询的null判断?
<if test="idList != null and idList.size() > 0">
AND id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
Q2:条件查询时如何避免SQL注入?
答:始终使用占位符,禁止拼接字符串,仅用于传递表名/列名等非查询参数。
Q3:多条件查询时性能优化建议?
- 对经常作为条件的字段建立索引(如
name、age)。
- 避免在
<if>中使用LIKE '%keyword%',改用全文索引或分词。
- 使用
<where>替代1=1,提升可读性。
Q4:MyBatis的@Param注解何时必须用?
答:当方法有多个参数且未使用@Param时,MyBatis无法自动绑定参数名,需通过param1、param2访问,建议始终显式注解。
总结与最佳实践
核心要点
- 明确需求:区分静态条件与动态条件,避免过度使用
<if>。
- 善用:替代手动拼接
1=1,提高代码简洁性。
- 警惕空指针:字符串检查用
!= null and != '',集合用size() > 0。
- 合理使用:处理复杂前缀后缀需求。
- 关联查询分离:优先使用懒加载(如
@One、@Many)减少冗余查询。
实战建议
- 为每个Mapper增加单元测试,覆盖“所有条件都传”和“没有条件”两种极端场景。
- 日志开启MyBatis SQL打印(
mybatis.configuration.log-impl=STDOUT_LOGGING)方便调试。
- 条件查询的DTO(数据传输对象)应设计为
Optional字段,避免基础类型默认值干扰。
通过本文的系统学习,您应能独立编写从简单到复杂的MyBatis条件查询案例。动态SQL的核心是平衡灵活性与可维护性——在业务逻辑不断变化时,MyBatis的条件机制是您最可靠的伙伴。
<select id="selectByIds" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
参数:collection必须对应接口的@Param("idList")。
问答:
问:<foreach>处理大量数据时性能如何?
答:一般建议列表长度不超过1000,否则考虑分批次查询或临时表。
常见问题与问答集锦
Q1:动态条件中如何处理IN子查询的null判断?
<if test="idList != null and idList.size() > 0">
AND id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
Q2:条件查询时如何避免SQL注入?
答:始终使用占位符,禁止拼接字符串,仅用于传递表名/列名等非查询参数。
Q3:多条件查询时性能优化建议?
- 对经常作为条件的字段建立索引(如
name、age)。 - 避免在
<if>中使用LIKE '%keyword%',改用全文索引或分词。 - 使用
<where>替代1=1,提升可读性。
Q4:MyBatis的@Param注解何时必须用?
答:当方法有多个参数且未使用@Param时,MyBatis无法自动绑定参数名,需通过param1、param2访问,建议始终显式注解。
总结与最佳实践
核心要点
- 明确需求:区分静态条件与动态条件,避免过度使用
<if>。 - 善用:替代手动拼接
1=1,提高代码简洁性。 - 警惕空指针:字符串检查用
!= null and != '',集合用size() > 0。 - 合理使用:处理复杂前缀后缀需求。
- 关联查询分离:优先使用懒加载(如
@One、@Many)减少冗余查询。
实战建议
- 为每个Mapper增加单元测试,覆盖“所有条件都传”和“没有条件”两种极端场景。
- 日志开启MyBatis SQL打印(
mybatis.configuration.log-impl=STDOUT_LOGGING)方便调试。 - 条件查询的DTO(数据传输对象)应设计为
Optional字段,避免基础类型默认值干扰。
通过本文的系统学习,您应能独立编写从简单到复杂的MyBatis条件查询案例。动态SQL的核心是平衡灵活性与可维护性——在业务逻辑不断变化时,MyBatis的条件机制是您最可靠的伙伴。