跳到正文
Joeplover
学习笔记·2025-12-14·约 3 分钟阅读

MyBatis-Plus 学习:Wrapper 查询与条件构造器

MyBatis-Plus 入门项目,演示 BaseMapper、QueryWrapper 条件查询、LambdaQueryWrapper 和自定义方法。

数据持久化

MyBatis-Plus:Wrapper 查询与条件构造器实战

为什么用 MyBatis-Plus

MyBatis 本身已经很轻量,但日常使用中重复的 CRUD SQL 代码太多——每个表都要写 insert、selectById、updateById、deleteById。MyBatis-Plus 通过代码生成器 + BaseMapper 解决了这个问题,把开发者从模板代码中解放出来。

BaseMapper 完整 CRUD

不用写任何 SQL 就是 MyBatis-Plus 最大的卖点:

// 继承 BaseMapper 后,自动获得以下方法
public interface UserMapper extends BaseMapper<User> {
    // 无需写任何代码
}

// 使用
userMapper.selectById(1L);                          // 按 ID 查
userMapper.selectList(null);                         // 查全部
userMapper.selectPage(page, wrapper);                // 分页查
userMapper.insert(user);                             // 插入
userMapper.updateById(user);                         // 按 ID 更新
userMapper.deleteById(1L);                           // 按 ID 删除
userMapper.selectOne(wrapper);                       // 查单条
userMapper.selectCount(wrapper);                     // 计数

Wrapper 条件构造器

这是 MyBatis-Plus 最强的功能——用 Java 链式调用替代 XML 中复杂的 <if> 标签:

<!-- 传统 MyBatis XML:if 标签地狱 -->
<select id="findUsers">
    SELECT * FROM user WHERE 1=1
    <if test="name != null">AND name LIKE CONCAT('%', #{name}, '%')</if>
    <if test="age != null">AND age > #{age}</if>
    <if test="email != null">AND email = #{email}</if>
</select>
// MyBatis-Plus LambdaQueryWrapper:链式调用,类型安全
LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery()
    .like(StringUtils.isNotBlank(name), User::getName, name)
    .gt(age != null, User::getAge, age)
    .eq(StringUtils.isNotBlank(email), User::getEmail, email)
    .orderByDesc(User::getCreateTime);

List<User> users = userMapper.selectList(wrapper);

优势:

  1. 类型安全:User::getName 是方法引用,编译时就能检查字段是否存在
  2. 条件控制:第一个 boolean 参数控制是否拼接该条件
  3. IDE 补全:字段名、方法名都有自动补全,不用查表结构
  4. 无 XML:减少文件切换

常用 Wrapper 操作

// 范围查询
wrapper.between(User::getAge, 18, 60);
wrapper.in(User::getStatus, 1, 2, 3);

// 子查询
wrapper.inSql(User::getId, "SELECT user_id FROM order WHERE amount > 1000");

// 嵌套条件
wrapper.and(w -> w.lt(User::getAge, 30).or().gt(User::getAge, 50));

// 排序
wrapper.orderByAsc(User::getSortOrder);

// 指定查询字段(避免 SELECT *)
wrapper.select(User::getId, User::getName);

分页插件

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor paginationInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

// 使用
Page<User> page = userMapper.selectPage(
    new Page<>(1, 20),  // 第1页,每页20条
    wrapper
);
page.getRecords();     // 当前页数据
page.getTotal();       // 总记录数
page.getPages();       // 总页数

物理分页:插件在底层自动生成带 LIMIT 的 SQL,不是一次性查出所有数据再内存分页。性能上没有区别。

陷阱和建议

  1. 大批量插入不要用 BaseMapper:saveBatch 是逐条 insert,几万条数据性能很差。大批量插入建议手写 MyBatis 的 foreach insert 或使用 JDBC batch。

  2. Wrapper 写复杂查询:当查询逻辑太复杂(多表 JOIN、group by、having)时,还是回到 MyBatis XML 写 SQL。Warpper 适合单表简单查询。

  3. 自动填充:MyBatis-Plus 支持 @TableField(fill = FieldFill.INSERT),可以自动填充 createTime/updateTime 字段。但要注意 UPDATE 时 updateTime 的自动填充。

  4. 逻辑删除:@TableLogic 注解实现逻辑删除,查询时自动拼接 deleted=0 条件。适合需要保留删除记录的场景。