前言
MyBatis-Plus (简称 MP)是一个由国人苞米豆团队开发的 MyBatis 的增强工具,为的就是简化开发、提高效率,Mybatis 有的它都有,它在 MyBatis 的基础上:只做增强、不做改变,功能强大、非常好用(强烈推荐)

  1. 本教程会尽量模拟真实开发环境使用,算比较全,涵盖了 mybatis-plus 最常用的的使用过程
  2. 本项目里使用的 maven 依赖 jar,都是当前较新的版本,我自己也用的,不用担心 mp 等相关的版本依赖问题,可放心使用
  3. 在引入 mybatis-plus 后,就不用再引入 mybatis-springboot 的依赖了,并且同样支持写 mapper 接口、xml 里写自定义sql (保留原生不做改变)

一:先建个学生表:t_student

CREATE TABLE `t_student` (
  `id` bigint(20) NOT NULL,
  `name` varchar(16) NOT NULL COMMENT '姓名',
  `gender` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别(1男,2女,0未知)',
  `major_id` int(11) NOT NULL COMMENT '所属专业id',
  `phone` varchar(16) NOT NULL COMMENT '手机号',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `del_flag` tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标记(0未删除,1已删除)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
  1. 这里模拟的学生表 id,是 bigint 类型,且非自增(一般数据量很多的表不建议用自增主键)
  2. 里面有几乎每个表都存在的 create_time、update_time,还有个逻辑删除标记 del_flag,给了个默认值0,做伪删除用,这三个字段一般看做一个整体,因为是固定的,每个表都可以有

二:创建MP-Demo工程,配置依赖和环境

1. maven 依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.2</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--  spring相关jar包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 数据库连接jar包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!-- pageHelper分页(注意:为避免和mybatis-plus的冲突,需排除部分依赖) -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.3.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 必备工具包 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.17</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  1. 上面我只引入了本案例用的一些 jar 包,有其他需要的可自行再引入
  2. springboot 版本选用 2.5.2,mp 的版本选择了 3.4.2,连接池没有用 druid,使用默认的 hikari
  3. 至于分页功能,其实 mp 也提供了分页插件供使用,但是本人不喜欢用自带的,觉得使用麻烦、对代码入侵性较高,所以我还是选择用 pagehelper 分页,更为方便些

2. 创建项目结构包项目结构

handler包是处理器包,po包是实体类包,mapper就是mybatis的mapper接口,service就是service接口+impl实现类,至于这个result包是我自定义的统一接口返回封装,随意,你有自己的也行

3. application.yml

server:
  port: 8080

spring:
  application:
    name: MP-Demo

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/my_test?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 5
      maximum-pool-size: 15
      auto-commit: true
      idle-timeout: 30000
      pool-name: ${spring.application.name}-HikariCP
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰法则(可省略此行,默认已开启)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL打印(就是每次操作数据库都会在控制台打印出来SQL语句,建议使用熟练以后注掉此行,不然日志很多)
  type-aliases-package: com.taoge.po # 实体类包路径(也可省略此行,不用配置)
  mapper-locations: classpath*:mapper/**/*Mapper.xml # 存放sql语句的xml文件目录
  global-config: # 此项是否配置根据实际项目来
    db-config:
      logic-delete-field: delFlag # 全局逻辑删除的实体字段名(这里填表字段名好像也可以)
      logic-delete-value: 1 # 逻辑已删除值(为1,表示已删除)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为0, 表示未删除)

# 分页插件PageHelper(当reasonable=false时,若当前页超过最大页数则不返回数据,为true则依然会返回最后一页数据)
pagehelper:
  helper-dialect: mysql
  pageSizeZero: true
  params: count=countSql
  reasonable: false
  support-methods-arguments: true
  1. 这里主要说下 mybatis-plus 的配置:其实和之前的 mybatis 配置很像,比如启用驼峰法则、实体类路径、xml路径,都和之前的一样
  2. 至于 log-impl 这个配置,其实就是打印 mp 的执行sql,个人建议刚用的时候开启,用熟练了后就注掉或删除这行,这样日志会少些,方便以后通过日志查看问题
  3. 重点说下 global-config 配置:这里配的就是全局逻辑删除 delFlag,即伪删除。mp 在自动查询的时候,会在 where 语句后面自动加上 del_flag = 0,过滤掉已删除的数据,在更新、删除的时候同样也会自动加上这个条件。
    但是如果你手动写 SQL 语句的话,那就需要自己手动加上。这个功能配置呢,看情况,毕竟这个一旦配了,可能强制性会比较高,所以不配这个全局逻辑删除字段也行,你就在查询时手动多加上条件 del_flag = 0,总之,根据实际情况灵活运用

4. MPApplication

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.taoge.mapper") // 扫描mapper接口包
@SpringBootApplication
public class MPApplication {

    public static void main(String[] args) {
        SpringApplication.run(MPApplication.class, args);
    }
}

5. handler包下创建自动填充器

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * MyBatis-Plus自定义填充处理器
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    // 默认策略:如果属性有值则不覆盖,如果填充值为null则不填充

    private static final String CREATE_TIME = "createTime";
    private static final String UPDATE_TIME = "updateTime";

    /**
     * 做insert插入时自动填充的值(这里一般是create_time和update_time)
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        if (metaObject.hasGetter(CREATE_TIME) && metaObject.hasGetter(UPDATE_TIME)) { // 实体类有get方法,就是有这个字段
            LocalDateTime localDateTime = LocalDateTime.now();
            this.strictInsertFill(metaObject, CREATE_TIME, () -> localDateTime, LocalDateTime.class); // 起始版本 3.3.3(推荐)
            this.strictInsertFill(metaObject, UPDATE_TIME, () -> localDateTime, LocalDateTime.class); // 起始版本 3.3.3(推荐)
        }
    }

    /**
     * 做update更新时自动填充的值(更新就只针对update_time)
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        if (metaObject.hasGetter(UPDATE_TIME)) {
            metaObject.setValue(UPDATE_TIME, LocalDateTime.now());
            // this.strictUpdateFill(metaObject, UPDATE_TIME, () -> LocalDateTime.now(), LocalDateTime.class);
        }
    }
}
  1. 我们希望表里的 create_time、update_time 字段能在新增或更新的时候能自动填充,不需要在手动的设置,所以才配置上面的自动填充器
  2. 这里演示的填充器,其实可以更丰富,如再加上:createBy、updateBy,即创建人、更新人,可以根据实际情况添加,但一般就上面那两个(创建时间、更新时间)就够了
  3. 这里的时间类型,没有用 Date 类型,而是 jdk8 里的 LocalDateTime 类型,项目里时间类型都统一用LocalDateTime(推荐用LocalDateTime)
  4. 官方提供的更新自动填充器代码,其实有个小问题:当先查询后再更新,会发现updateTime值没有更新,这是因为默认策略问题:有值则不覆盖,所以上面代码我直接 metaObject.setValue(),注掉了下面一行,这样就会避免这个bug

6. 创建实体类
(1)po 包下新建 base 包,再创建 BaseEntity

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

/**
 * @author rlt
 * @date 2021/12/15 14:57
 */
@Getter
@Setter
public abstract class BaseEntity implements Serializable {

    private static final long serialVersionUID = -922201555125882232L;

    /** 创建时间 */
    @TableField(fill = FieldFill.INSERT) // 插入自动填充
    private LocalDateTime createTime;

    /** 更新时间 */
    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入或更新时自动填充
    private LocalDateTime updateTime;

    /** 删除标记(0未删除,1已删除) */
//    @TableLogic // 此注解表示该字段是逻辑删除字段(这里注掉是因为现用的mp版本是3.4.2,从3.3.0版本后就可以省略该注解)
    private Integer delFlag;
}
  1. 由于上面已经配了自动填充器,所以这里使用时要在字段上加上相应注解,这样在插入更新时就不用再手动 set 值了
  2. 请注意:上面配置的更新自动填充器在一种特殊情况下也会失效,就是当我们自己自定义更新某些字段时(比如自定义更新字段:把姓名=张三的人手机号改为xxxx),这个小bug官方目前也没直接修复,但是可以换法子解决,后面会介绍

(2)po 包下新建 Student 类,继承 BaseEntity

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.taoge.po.base.BaseEntity;
import lombok.Data;

import java.io.Serializable;

/**
 * 学生po
 */
@Data
@TableName(value = "t_student") // 指定数据库里对应的表名
public class Student extends BaseEntity {

    private static final long serialVersionUID = 3819669465179936254L;

    // @TableId(type = IdType.AUTO):表主键id注解,AUTO表示id是自增的
    // @TableField(value = "major_id"):表字段注解,value表示对应表里的字段,但只要表字段命名规范,实体类里驼峰命名,就可不加此注解

    /** 学生id */
//    @TableId(type = IdType.AUTO) // 如果你的表主键id是自增的,就加上这行注解,我这里的id是非自增,所以不用加@TableId注解
    private Long id;

    /** 姓名 */
    private String name;

    /** 性别:1男,2女,0未知 */
    private Integer gender;

    /** 专业id */
//    @TableField(value = "major_id") // 指定对应表里字段,为了方便,此注解可以不加
    private Integer majorId;

    /** 手机号 */
    private String phone;

    //======下面是我额外加的字段=========

    /** 所属专业名称 */
    @TableField(exist = false) // 非表字段(这种就得加上@TableField注解,exist默认为true,为false时表示非数据表字段)
    private String majorName; // 这个字段在mybatis-plus自动查询的时候就不会带上,不过当你在xml里自定义写多表查询sql时可映射此字段
}

7. 创建 mapper 接口:StudentMapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.taoge.po.Student;

public interface StudentMapper extends BaseMapper<Student> {
}
  1. mapper 接口直接继承 mp 的 BaseMapper 接口,指定对应的实体类就是 Student
  2. mapper 接口上不用加 @Mapper、@Repository 这种注解,因为启动类上已经加了 @MapperScan 注解,扫描了整个mapper包

8. 创建 service 接口及实现类
(1)StudentService 接口

import com.baomidou.mybatisplus.extension.service.IService;
import com.taoge.po.Student;

import java.util.List;

public interface StudentService extends IService<Student> {
}

service 接口直接继承 mp 的 IService 接口,同样指定实体类为 Student,这样就能用 IService 里自带的方法

(2)实现类

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.taoge.mapper.StudentMapper;
import com.taoge.po.Student;
import com.taoge.service.StudentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService {
}

这里是继承 ServiceImpl,指定对应的 mapper 和类,这样自动化的 curd 操作就完成了

三:创建测试Controller

1. 新增接口

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.taoge.mapper.StudentMapper;
import com.taoge.po.Student;
import com.taoge.result.CodeMsg;
import com.taoge.result.Result;
import com.taoge.service.StudentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author rlt
 * @date 2021/12/15 15:59
 */
@Slf4j
@RestController
@RequestMapping("/student")
@RequiredArgsConstructor // lombok注解:可代替@Autowired,但是必须加上final(其实就是通过构造器来注入)
public class StudentController {

    // 为了演示,我这里把service和mapper都注入了进来,实际开发只需注入一个service,主要是介绍service的mapper对的操作区别
    // 其实service和mapper在功能上基本上是一样的,service有的功能mapper也有,只是他两用的时候在方法名字上有区别
    // service里的方法内部再去调用了mapper,可以点进去看下源码就明白了
    // 实际中,可以在service中自定义一些方法,在serviceImpl中再调用mapper,当明白了mybatisplus的使用后可自由发挥

    private final StudentService studentService;
    private final StudentMapper studentMapper;

    /**
     * 新增
     */
    @PostMapping("/add")
    public Result add() {
        // service演示新增
        Student student = new Student();
        student.setName("张三");
        student.setGender(1);
        student.setMajorId(1);
        student.setPhone("18300001111");
        boolean save = studentService.save(student);
        // mapper演示新增
        Student student1 = new Student();
        student1.setName("小芳");
        student1.setGender(2);
        student1.setMajorId(1);
        student1.setPhone("18300002222");
        int insert = studentMapper.insert(student1); // 上面的save内部也是调用insert,只是使用时名称不一样
        log.info("【add】save={}, insert={}", save, insert);
        // 批量插入
        Student student2 = new Student();
        student2.setName("小三");
        student2.setGender(2);
        student2.setMajorId(2);
        student2.setPhone("18300003333");
        Student student3 = new Student();
        student3.setName("小明");
        student3.setGender(1);
        student3.setMajorId(1);
        student3.setPhone("18300004444");
        List<Student> studentList = new ArrayList<>();
        studentList.add(student2);
        studentList.add(student3);
        studentService.saveBatch(studentList); // saveBatch,只能用service去调用
        return Result.success(save && insert == 1);
    }
}

由于学生表的主键id是非自增的,我这里虽然没有setId,但是mybatis-plus会自动给我们生成一个长id,当然你也用你项目里的id生成策略来生成,然后手动setId,如雪花算法生成id,这样会比较好

用 postman 请求:http://localhost:8080/student/add,debug 过程如下:
add Debug
表数据
2. 查询接口

   /**
     * 查询
     */
    @GetMapping("/query/{id}")
    public Result query(@PathVariable("id") Long id) {
        // 1. 根据id查询
        Student student = studentService.getById(id);
//        student = studentMapper.selectById(id); // 等同于上面一行
        log.info("【query】student={}", JSON.toJSONString(student));

        // 2. 查询所有(查询列表时可以加上pagehelper分页,这里就不演示了,自己尝试,很简单)
        List<Student> studentAllList = studentService.list(); // 或者写成.list(null),两个是一样的
//        List<Student> studentAllList = studentMapper.selectList(null); // 等价于上面的写法
        log.info("【query】studentAllList={}", JSON.toJSONString(studentAllList));

        // 3. 查询器查询

        // 条件构造器(不建议用这个):查询name=张三
        QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", "张三"); // 条件查询(eq是等于,即name='张三')
        queryWrapper.select("id", "name", "major_id"); // 查询指定字段(不加这行会默认查询所有字段,注意:这里的字段填的是表里的字段)
        Student student1 = studentService.getOne(queryWrapper); // getOne:表示只去查询一条结果
//        Student student1 = studentMapper.selectOne(queryWrapper); 等同于上面一行
        log.info("【query】student1={}", JSON.toJSONString(student1)); // 注意:由于我上面加了指定字段查询,所以这里只有查的这几个字段才有值

        // lambda查询构造器(推荐):查询major_id=1的学生列表
        LambdaQueryWrapper<Student> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Student::getMajorId, 1); // 条件:where major_id = 1(如果多条件查询可以继续.eq这样添加)
        lambdaQueryWrapper.select(Student::getId, Student::getName, Student::getGender); // 我只想查这三个字段
        lambdaQueryWrapper.orderByDesc(Student::getCreateTime); // 排序:order by create_time desc
        List<Student> studentList = studentService.list(lambdaQueryWrapper);
        log.info("【query】studentList={}", JSON.toJSONString(studentList));

        // 这里再写一个service里我们自定义的方法:查询专业为1且性别为女
        List<Student> studentList2 = studentService.getByMajorIdAndGender(1, 2);
        log.info("【query】studentList2={}", JSON.toJSONString(studentList2));
        return Result.success(true);
    }

StudentServiceImpl:新增如下方法

    // 这里可以注入mapper进来进行操作,也可以用mp的baseMapper进行操作(因为mapper继承了baseMapper)
    // 如果你有复杂的sql或者自定义的sql,那么就按照原生的mybatis那样操作,去mapper里写接口,xml里写sql
    private final StudentMapper studentMapper;

    @Override
    public List<Student> getByMajorIdAndGender(Integer majorId, Integer gender) {
        // 同样,使用LambdaQueryWrapper
        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Student::getMajorId, majorId).eq(Student::getGender, gender);

        // 由于StudentService继承了IService,所以这里可以直接用service里的方法,也可以用mapper
        // 用service操作
        List<Student> serviceList = this.list(queryWrapper); // 这里实现类里可直接使用IService里的方法
        // 用mapper操作
        List<Student> mapperList = baseMapper.selectList(queryWrapper); // 这里也可以用:studentMapper.selectList(queryWrapper)
        log.info("【getByMajorIdAndGender】serviceList={}", serviceList);
        log.info("【getByMajorIdAndGender】mapperList={}", mapperList); // 两条查询肯定是一样的
        return mapperList;
    }

上面用 service 能做的,用 mapper 同样能做到,因为 service 里内部还是走的 mapper 去查询,可以自己摸索尝试,另外还有更多常用的特殊查询,如模糊查询,在最后面介绍

用 postman 请求:http://localhost:8080/student/query/1472863272568754178
id查询
3. 更新接口

   /**
     * 更新
     */
    @PostMapping("/update/{id}")
    public Result update(@PathVariable("id") Long id) {
        // 1. 根据id更新
        Student student = new Student();
        student.setId(id);
        student.setName("张三三"); // 根据id更新姓名为张三三
        boolean updateById = studentService.updateById(student); // 会自动填充updateTime
        log.info("【update】updateById={}", updateById);

        // 2. 指定条件更新:把姓名=张三三的专业改为3
        LambdaUpdateWrapper<Student> updateWrapper = new LambdaUpdateWrapper<>(); // 创建lambda更新器
        updateWrapper.set(Student::getMajorId, 3); // 更新器中set是要更新的字段,是最后结果
        updateWrapper.set(Student::getUpdateTime, DateUtil.toLocalDateTime(new Date())); // 这里需手动setUpdateTime,因为这里更新的字段值是我们自定义的,我们自定义了updateWrapper.set()
        updateWrapper.eq(Student::getName, "张三三"); // 而eq是更新条件,即sql是:set major_id = 3 where name = '张三三'
        boolean update = studentService.update(updateWrapper);
        log.info("【update】update={}", update);

        // 这里提一点,如果我要吧表中的某一个字段由原先的非null改为null,那么就不能用updateById,不会生效
        // 建议用指定字段更新来实现,当然也可以用原生的手动写sql语句实现
        // 比如上面的,我现在要把姓名=张三三的专业改为null,则改为updateWrapper.set(Student::getMajorId, null)
        return Result.success(updateById && update);
    }

注意一点:指定条件更新的时候,updateTime 自动填充会失效,因为这个更新是我们自定义的,mp 就不做填充,所以才手动加上 setUpdateTime

postman 请求:http://localhost:8080/student/update/1472863272568754178
id更新
条件更新
4. 删除接口

   /**
     * 删除
     */
    @PostMapping("/delete/{id}")
    public Result delete(@PathVariable("id") Long id) {
        // 一般删除前可以先查一下,不为空再删除
        Student student = studentService.getById(id);
        if (null == student) {
            return Result.error(CodeMsg.DELETE_FAIL);
        }

        // 说明:removeById表示根据id删除(常用),还有根据id批量删除,方法是removeByIds
        // 但如果yml里配的是伪删除,那么removeById由原本的执行delete变成走update,即把del_flag字段值改为1

        // 注意:由于现yml里配置了伪删除,如果走mp的删除接口,则会有个小bug:数据虽删了但update_time没有自动填充改变,还是原来的时间
        // 为避免这个bug,既然是伪删,我建议还是全走update,手动更新del_flag和update_time这两个字段,或者手动在xml里写sql实现

        // 1. 根据id删除(走根据id更新指定字段)
        LambdaUpdateWrapper<Student> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Student::getDelFlag, 1);
        updateWrapper.set(Student::getUpdateTime, DateUtil.toLocalDateTime(new Date()));
        updateWrapper.eq(Student::getId, id);
        boolean update = studentService.update(updateWrapper);
        log.info("【delete】update={}", update);

        // 2. 条件删除:删除专业=5的女学生(同样,伪删除走指定条件更新)
        updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Student::getDelFlag, 1);
        updateWrapper.set(Student::getUpdateTime, DateUtil.toLocalDateTime(new Date()));
        updateWrapper.eq(Student::getMajorId, 5);
        updateWrapper.eq(Student::getGender, 2);
        boolean update1 = studentService.update(updateWrapper); // 由于没有专业id=5的数据,所以返回false
        log.info("【delete】update1={}", update1);

//        // 如果你的表没有类似的del_flag字段,直接是真删,则删除为下面2种:
//        // 根据id删除
//        studentService.removeById(id); // 或者studentMapper.deleteById(id)
//        // 根据条件删除
//        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(Student::getMajorId, 5);
//        queryWrapper.eq(Student::getGender, 2);
//        studentService.remove(queryWrapper);
        return Result.success(update, "删除成功");
    }

postman 访问:http://localhost:8080/student/delete/1472863272568754178
id伪删
5. 特殊查询介绍
先把上面的删除的张三三的delFlag改为0,方便测试

   /**
     * 特殊查询介绍(这里我就都用service、lambda查询器来演示)
     */
    @GetMapping("/query2")
    public Result query2() {
        // 1. 模糊查询:姓名中有三的学生
        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(Student::getName, "三"); // name like '%三%'(对应还有个不常用的notLike)
//        queryWrapper.likeLeft(Student::getName, "三"); // name like '%三'
//        queryWrapper.likeRight(Student::getName, "三"); // name like '三%'
        queryWrapper.orderByDesc(Student::getCreateTime); // 倒序输出
        List<Student> likeList = studentService.list(queryWrapper);
        log.info("【query2】likeList={}", JSON.toJSONString(likeList));

        // 2. 范围查询:大于小于
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.gt(Student::getCreateTime, "2021.12.20 17:38:00"); // gt是大于的意思:create_time > 2021.12.17 06:00:00
        queryWrapper.lt(Student::getCreateTime, "2021.12.25 22:00:00"); // lt是小于的意思:create_time < 2021.12.18 22:00:00
        List<Student> gltList = studentService.list(queryWrapper);
        // 还有:ge(大于等于>=)、le(小于等于<=)、ne(不等于<>)、between(两值之间),照葫芦画瓢就行,不演示了
        log.info("【query2】gltList={}", JSON.toJSONString(gltList));

        // 3. 分组查询:groupBy
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.groupBy(Student::getMajorId);
        List<Student> groupList = studentService.list(queryWrapper);
        log.info("【query2】groupList={}", JSON.toJSONString(groupList));

        // 4. in查询
        queryWrapper = new LambdaQueryWrapper<>();
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        queryWrapper.in(Student::getName, list); // 对应的还有notIn
        List<Student> inList = studentService.list(queryWrapper);
        log.info("【query2】inList={}", JSON.toJSONString(inList));

        // 5. 空查询(不常用)
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.isNull(Student::getName); // name is null,对应的肯定还有isNotNull
        List<Student> nullList = studentService.list(queryWrapper);
        log.info("【query2】nullList={}", JSON.toJSONString(nullList));

        // 6. 动态查询
        // 上面的不管是eq,还是gt、in等,其实在执行时最前面还有个布尔参数,不传的话默认都是true
        // 像上面的所有查询,我们都没传这个参数,其实内部自动为我们设置成了true,点进去看源码就知道了
        // 这个布尔参数的意思:为true表示带上这个条件,为false则忽略此条件,其实就是做动态SQL用
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(false, Student::getName, "张三"); // 这里设成false,则此设置不生效,等于没有这行,依然会查询所有的学生
        queryWrapper.eq(nullList.size() > 0, Student::getMajorId, 1); // 如果nullList有数据此设置才生效,才会查询专业=1的学生
        List<Student> dynamicList = studentService.list(queryWrapper);
        log.info("【query2】dynamicList={}", JSON.toJSONString(dynamicList));
        return Result.success(true);
    }

postman 访问:http://localhost:8080/student/query2
模糊查询
范围查询
分组查询
动态查询
以上,就是 mybatis-plus 的最常用教程,所有代码都已通过我的实测,没有啥问题,需要注意的一点就是伪删除,如果你不是每张表都有个 del_flag 字段,可以不用在 yml 文件里做全局的逻辑删除配置,改为自己手动实现,如查询时手动加上条件:delFlag = 0,删除时一样,总之,熟悉之后灵活运用(觉得有帮助的还请点个赞)。

附上 MyBatis-Plus 官网:https://baomidou.com/(可以看看文档)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐