Featured image of post MyBatis篇-分页查询

MyBatis篇-分页查询

MyBatis分页查询学习笔记

1478字

MyBatis分页查询与动态SQL学习笔记

一、分页查询的两种方式

1. 物理分页(推荐)

特点:

  • 在SQL层面实现分页
  • 性能更好(大数据量场景)
  • 常用实现方式:PageHelper插件、手写分页SQL

2. 逻辑分页

特点:

  • 查询全部数据后在内存中分页
  • 性能较差(小数据量可用)
  • 实现方式:RowBounds

二、实现案例

案例1:使用PageHelper插件(含动态SQL)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<select id="selectUsersByCondition" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
    </where>
    ORDER BY create_time DESC
</select>

Java调用:

1
2
3
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectUsersByCondition(params);
PageInfo<User> pageInfo = new PageInfo<>(users);

案例2:手写SQL分页(MySQL)与动态SQL结合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<select id="selectByPageWithDynamic" resultType="User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="role == 'admin'">
                AND department_id = #{deptId}
            </when>
            <when test="role == 'manager'">
                AND team_id = #{teamId}
            </when>
            <otherwise>
                AND status = 1
            </otherwise>
        </choose>
    </where>
    LIMIT #{offset}, #{pageSize}
</select>

三、动态SQL核心知识点

1. 常用标签

标签 作用
<if> 条件判断
<choose> 类似Java的switch-case
<where> 自动处理WHERE关键字和AND/OR前缀
<set> 自动处理UPDATE语句的SET部分
<foreach> 遍历集合,常用于IN条件
<trim> 自定义字符串修剪(可替代where/set)
<bind> 创建变量并绑定到上下文

2. 典型应用场景

场景1:多条件查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<select id="findUsers" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null">
            AND name LIKE #{name}
        </if>
        <if test="minAge != null">
            AND age >= #{minAge}
        </if>
        <if test="statusList != null and statusList.size() > 0">
            AND status IN
            <foreach collection="statusList" item="status" 
                     open="(" separator="," close=")">
                #{status}
            </foreach>
        </if>
    </where>
</select>

场景2:动态UPDATE

1
2
3
4
5
6
7
8
9
<update id="updateUserSelective">
    UPDATE user
    <set>
        <if test="name != null">name = #{name},</if>
        <if test="email != null">email = #{email},</if>
        <if test="status != null">status = #{status}</if>
    </set>
    WHERE id = #{id}
</update>

场景3:批量插入

1
2
3
4
5
6
<insert id="batchInsert">
    INSERT INTO user (name, age) VALUES
    <foreach collection="users" item="user" separator=",">
        (#{user.name}, #{user.age})
    </foreach>
</insert>

四、面试常见问题与回答建议

Q1:MyBatis动态SQL的实现原理?

回答要点

  • 基于OGNL表达式解析
  • 通过XML标签生成最终SQL
  • 使用SqlNode和SqlSource构建动态SQL

示例回答

“MyBatis动态SQL通过解析XML中的特殊标签(如if/where等),根据参数值动态拼接SQL语句。底层使用OGNL表达式求值,最终生成StaticSqlSource或DynamicSqlSource对象。比如标签会智能处理AND/OR前缀,避免语法错误。”

Q2:如何优化动态SQL的性能?

回答建议

  1. 避免过度复杂的动态条件
  2. 使用替代手动WHERE 1=1
  3. 注意的批量操作数量控制

示例回答

“我们项目中对动态SQL的优化主要有:1) 对高频查询使用预处理模糊查询参数;2) 批量操作时控制的集合大小(分批次处理);3) 复杂查询拆分为多个简单SQL,通过Java代码组合结果。”

Q3:动态SQL和分页结合使用的注意事项?

回答要点

  • 确保分页在最终SQL上执行
  • PageHelper要放在动态SQL查询前
  • 注意排序字段的索引优化

示例回答

“结合使用时需注意:1) PageHelper.startPage()必须在动态SQL查询前调用,确保分页是对最终结果集生效;2) 动态WHERE条件中的字段建议建立复合索引;3) 排序字段如果是动态的,需要防范SQL注入。”

五、最佳实践总结

  1. 分页选择

    • 常规场景用PageHelper
    • 超大数据量考虑游标分页(基于索引)
  2. 动态SQL规范

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    <!-- 推荐写法 -->
    <where>
      <if test="param != null">
          AND column = #{param}
      </if>
    </where>
    
    <!-- 不推荐写法 -->
    WHERE 1=1
    <if test="param != null">
        AND column = #{param}
    </if>
    
  3. 性能监控

    • 开启MyBatis日志检查最终执行的SQL
    • 对复杂动态SQL进行压测

💡 面试加分点:可以举例说明如何解决实际项目中遇到的动态SQL问题,例如:“我们曾遇到一个多条件导出功能,通过动态SQL减少70%的冗余代码,同时用PageHelper的count查询优化解决了分页总数性能问题。”

如对内容有异议,请联系关邮箱2285786274@qq.com修改