从BaseMapper到LambdaWrapper:MyBatis-Plus的封神之路

一、MyBatis-Plus简介

1.1、介绍

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生,官网地址:https://baomidou.com/

1.2、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

1.3、支持数据库

任何能使用 MyBatis 进行增删改查,并且支持标准 SQL 的数据库应该都在 MyBatis-Plus 的支持范围内

1.4、框架结构

二、MyBatis-Plus快速上手

2.1、数据准备

此处依旧采用前篇我们已有的数据库用户表(创建表的SQL语句见前篇):

2.2、项目配置

创建springboot项目,添加如图所示的依赖即可:

maven中根据 spring boot 的版本添加依赖:

Spring Boot 3:

XML 复制代码
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.5</version>
        </dependency>

Spring Boot 2:

XML 复制代码
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
</dependency>

配置数据库:

XML 复制代码
# 数据库连接配置
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

2.3、创建实体类

创建实体类UserInfo

java 复制代码
@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

编写mapper接口类

进入BaseMapper类查看源码:

java 复制代码
public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteById(T entity);
    
    ...
}

该类接收一个泛型参数作为操作对象,因此需要将UserInfo 传入BaseMapper 的**<>**括号中

MyBatis-Plus 提供了基础的 BaseMapper 接口,该接口已实现单表 CRUD 操作。我们只需让自定义的 Mapper 继承 BaseMapper,即可直接使用这些基础功能,无需自行实现单表增删改查操作

2.4、单元测试(CRUD)

java 复制代码
@SpringBootTest
public class MyBatisplusTests {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @Test
    void testSelectByIds(){
        List<UserInfo> list=userInfoMapper.selectBatchIds(List.of(10,11,12));
        list.forEach(System.out::println);
    }

    @Test
    void testInsert(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(15);
        userInfo.setAge(35);
        userInfo.setPassword("123456");
        userInfo.setGender(1);
        userInfo.setUsername("程序员");
        userInfo.setPhone("10010010011");
        userInfoMapper.insert(userInfo);
    }

    @Test
    void testUpdate(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(13);
        userInfo.setPhone("20022002200");
        userInfoMapper.updateById(userInfo);
    }
}

运行结果;

三、MyBatis-Plus复杂操作

3.1、主要注解

UserInfoMapper 继承 BaseMapper 时需指定泛型类型 ,这里的 UserInfo 就是与数据库表对应的实体类,MyBatis-Plus 会根据该实体类自动推断表结构信息

默认映射规则如下:

  1. 表名:将实体类的驼峰命名转换为下划线命名(如 UserInfo → user_info)
  2. 字段:将属性名的驼峰命名转换为下划线命名(如 deleteFlag → delete_flag)
  3. 主键:默认使用 id 字段

若实体类与数据库表结构不符合上述默认规则,MyBatis-Plus 提供了多种注解来显式指定映射关系:

3.1.1、@TableName

那么我们根据上述规则进行修改实体类的名称:UserInfo -> Userinfo ,再次执行查询操作:

这种情况我们就可以通过**@TableName注解来标识实体类对应的表**:

3.1.2、@TableField

@TableName注解来标识实体类对应的表,那么@TableField这个注解就是用来标识属性对应的字段名

3.1.3、@TableId

@TableId注解用来指定属性对应的主键

当属性名与数据库字段名不一致时,需在@TableId 注解中明确指定对应字段名;若两者一致,则直接使用@TableId 注解即可

3.2、配置日志

XML 复制代码
mybatis-plus:
  configuration: # 配置打印 MyBatis⽇志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.3、条件构造器

MyBatis-Plus也提供了强大的条件构造器(Wrapper )来支持复杂查询场景。这套Wrapper机制允许开发者通过链式调用方式构建查询条件,既能提升开发效率,又能有效避免SQL注入风险

条件构造器😄MyBatis-Plus提供的主要Wrapper类及其功能包括:

  1. AbstractWrapper:所有Wrapper类的基类,封装了通用的查询条件构建方法
  2. QueryWrapper:专用于查询条件的构造,在AbstractWrapper基础上扩展了select方法用于指定查询字段
  3. UpdateWrapper:用于构建更新操作的条件
  4. LambdaQueryWrapper:基于Lambda表达式的查询构造器,通过实体类属性引用避免字段名硬编码
  5. LambdaUpdateWrapper:基于Lambda表达式的更新构造器,同样避免字段名硬编码问题
3.3.1、QueryWrapper

QueryWrapper不仅适用于查询语句,还可用于构建修改、删除等操作的查询条件

java 复制代码
    @Test
    void testQueryWrapper(){
        QueryWrapper<Userinfo> queryWrapper=new QueryWrapper<Userinfo>()
                .select("id","username","age","phone")
                .eq("age",18)
                .like("phone","19");
        List<Userinfo> list=userInfoMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

请注意:

默认情况下,Mybatis-Plus会根据@TableField 注解自动生成字段别名。但如果指定了QueryWrapper的 select 属性,查询结果将仅包含属性值而丢失别名,导致结果映射失败

正常查询:

java 复制代码
    @Test
    void testQueryWrapper2(){
        QueryWrapper<Userinfo> wrapper = new QueryWrapper<>();
        List<Userinfo> users = userInfoMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

指定select时:

java 复制代码
    @Test
    void testQueryWrapper2(){
        QueryWrapper<Userinfo> wrapper = new QueryWrapper<>();
        wrapper.select("create_time"); // 明确指定查询字段
        List<Userinfo> users = userInfoMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

查询仅包含 create_time 字段,但返回类型为 UserInfo,导致打印结果中其他属性值均为 null

解决方案:

  1. 手动编写自定义SQL
  2. 确保实体类属性名与数据库列名完全一致
  3. 避免在QueryWrapper中显式指定select 字段
  4. 改用LambdaQueryWrapper进行查询

java 复制代码
    @Test
    void testQueryWrapper3(){
        QueryWrapper<Userinfo> wrapper = new QueryWrapper<Userinfo>()
                .lt("age",16);
        Userinfo userinfo=new Userinfo();
        userinfo.setPhone("00000000000");
        userInfoMapper.update(userinfo,wrapper);
    }
  • lt - "less than" 的缩写,表示"小于"
  • le - "less than or equal to" 的缩写,表示"小于等于"
  • ge - "greater than or equal to" 的缩写,表示"大于等于"
  • gt - "greater than" 的缩写,表示"大于"
  • eq - "equals" 的缩写,表示"等于"
  • ne - "not equals" 的缩写,表示"不等于"

java 复制代码
    @Test
    void testQueryWrapper4(){
        QueryWrapper<Userinfo> wrapper = new QueryWrapper<Userinfo>()
                .eq("delete_flag",1);
        userInfoMapper.delete(wrapper);
    }
3.3.2、UpdateWrapper

我们可以直接使用 UpdateWrapper 进行更新操作,无需创建实体对象即可直接设置更新字段和条件

基础更新:

java 复制代码
    @Test
    void testUpdateByUpdateWrapper(){
        UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<Userinfo>()
                .set("delete_flag",0)
                .set("age", 5)
                .in("id", List.of(1,2,3));
        userInfoMapper.update(updateWrapper);
    }

基于SQL更新:

java 复制代码
    @Test
    void testUpdateBySQLUpdateWrapper(){
        UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>()
                .setSql("age = age+10")
                .in("id", List.of(1,2,3));
        userInfoMapper.update(updateWrapper);
    }
3.3.3、LambdaQueryWrapper

使用QueryWrapperUpdateWrapper时存在一个明显问题:需要手动编写字段名称字符串。如果数据库字段名称发生变更,由于测试覆盖不足,很容易引发生产事故

MyBatis-Plus提供了基于Lambda 表达式的条件构造器解决方案,通过Lambda 表达式引用实体类属性,完美解决了字段名硬编码问题。这种方式不仅提升了代码可读性,也显著增强了代码的可维护性

主要实现类包括:

  • LambdaQueryWrapper
  • LambdaUpdateWrapper

这两个类分别对应传统的QueryWrapperUpdateWrapper功能

java 复制代码
    @Test
    void testQueryWrapper5(){
        QueryWrapper<Userinfo> queryWrapper=new QueryWrapper<Userinfo>();
        queryWrapper.lambda().select(Userinfo::getId , Userinfo::getAge,
                Userinfo::getUsername , Userinfo::getPhone)
                .eq(Userinfo::getAge,18)
                .like(Userinfo::getPhone,"19");
        List<Userinfo> list=userInfoMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }
3.3.4、LambdaUpdateWrapper
java 复制代码
    @Test
    void testLambdUpdateByUpdateWrapper(){
        UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>();
        updateWrapper.lambda()
                .set(UserInfo::getDeleteFlag, 0)
                .set(UserInfo::getAge, 5)
                .in(UserInfo::getUserId, List.of(1,2,3));
        userInfoMapper.update(updateWrapper);
    }

or 方法是 MyBatis-Plus 提供的核心查询构造方法之一,主要用于实现 OR 逻辑查询条件。该方法会改变后续查询条件的连接方式,将默认的 AND 连接转换为 OR 连接

3.4、自定义SQL

当MyBatis-Plus提供的标准操作无法满足我们的需求时,我们可以利用其提供的自定义SQL功能;通过Wrapper构建查询条件,再配合Mapper编写SQL语句,就能灵活实现各种复杂查询需求

java 复制代码
---------------mapper------------------
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
    @Select("select id,username,password,age FROM user_info ${ew.customSqlSegment}")
    List<UserInfo> queryUserByCustom(@Param(Constants.WRAPPER) Wrapper<UserInfo> wrapper);
}
---------------test--------------------
@Test
void testQueryUserByCustom(){
    QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<UserInfo>()
        .eq("username","admin");
    userInfoMapper.queryUserByCustom(queryWrapper).forEach(System.out::println);
}

注意事项:

  • 参数命名 :在自定义 SQL 时,若需传递 Wrapper 对象作为参数,必须使用 ew 作为参数名,或者通过 @Param(Constants.WRAPPER) 注解明确指定该参数为 Wrapper 对象
  • 使用方式 :在 SQL 语句中,通过 ${ew.customSqlSegment} 来引用 Wrapper 对象生成的 SQL 片段
  • 功能限制:使用自定义 SQL 时,Wrapper 对象不会基于实体类自动生成 where 子句,需要手动编写完整的 SQL 语句
相关推荐
熊猫片沃子14 分钟前
浅谈SpringBoot框架的优势
java·spring boot·后端
33255_40857_2805917 分钟前
RocketMQ高级特性实战:Java开发者的进阶指南
java·rocketmq
北京_宏哥26 分钟前
🔥《刚刚问世》系列初窥篇-Java+Playwright自动化测试-32- 操作日历时间控件-下篇(详细教程)
java·前端·面试
用户84913717547161 小时前
JDK 17 实战系列(第3期):性能优化与系统增强详解
java·后端·性能优化
Asu52022 小时前
思途spring学习0807
java·开发语言·spring boot·学习
遇见火星2 小时前
Jenkins全链路教程——Jenkins用户权限矩阵配置
java·矩阵·jenkins
埃泽漫笔2 小时前
什么是SpringBoot
java·spring boot
zhang1062092 小时前
PDF注释的加载和保存的实现
java·开发语言·pdf·pdfbox·批注
码银2 小时前
什么是逻辑外键?我们要怎么实现逻辑外键?
java·数据库·spring boot
SugarFreeOixi3 小时前
Idea打包可执行jar,MANIFEST.MF文件没有Main-Class属性:找不到或无法加载主类
java·jar