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的条件机制是您最可靠的伙伴。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
<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的条件机制是您最可靠的伙伴。