SpringBoot与Mybatis-Plus的详细整合及最常用实战教程极速上手

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

  1. 本教程会尽量模拟真实开发环境使用,算比较全,涵盖了 mybatis-plus 最常用的的使用过程
  2. 本项目里使用的 maven 依赖 jar,都是当前较新的版本,我自己也用的,不用担心 mp 和 druid 等相关的版本依赖问题,可放心使用

一:先建个学生表: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.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.6</version>
        </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>
        </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. mp 的版本选择了 3.4.2,连接池用了 druid,版本 1.2.6,都比较新,版本可兼容
  3. 至于分页功能,其实 mp 也提供了分页插件供使用,但是本人真不喜欢用自带的,因为使用麻烦、对代码入侵性较高,所以我更喜欢用 pagehelper 分页,更为方便些

2. 创建项目结构包

项目结构

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

3. application.yml

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/my_test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver # 注意:如果你的mysql的jar包引入的是8.0以上版本,则驱动为:com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 5 # 初始化时建立物理连接的个数
      min-idle: 5 # 最小连接池数量
      max-active: 100 # 最大连接池数量
      max-wait: 60000 # 获取连接时最大等待时间
      stat-view-servlet:
        login-username: admin # 配置监控页面访问登录名称
        login-password: admin # 配置监控页面访问密码
      filter:
        stat:
          log-slow-sql: true # 是否开启慢sql查询监控
          slow-sql-millis: 1 # 慢SQL执行时间
      pool-prepared-statements: true
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      max-open-prepared-statements: 20
      validation-query: SELECT 1 FROM DUAL

  application:
    name: MP-Demo

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配置
pagehelper:
  helper-dialect: mysql
  pageSizeZero: true
  params: count=countSql
  reasonable: true
  support-methods-arguments: true

这里主要说下 mybatis-plus 的配置:其实和之前的 mybatis 配置很像,比如启用驼峰法则、实体类路径、xml路径,都和之前的一样。
至于 log-impl 这个配置,其实就是打印 mp 的执行sql,个人建议刚用的时候开启,用熟练了后就注掉这行,这样日志会少些。
重点说下 global-config 配置,这里配的就是全局逻辑删除 delFlag,即伪删除,mp 在自动查询的时候,会在 where 语句后面自动加上 del_flag = 0,过滤掉已删除的数据,更新、删除的时候同样也会自动加上 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. 配置自动填充器

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

import java.time.LocalDateTime;

/**
 * 自定义MyBatis-Plus填充处理器
 */
@Slf4j
@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();
            log.info("【insertFill】localDateTime={}", localDateTime.toString());
            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)) {
            LocalDateTime localDateTime = LocalDateTime.now();
            log.info("【updateFill】localDateTime={}", localDateTime.toString());
            this.strictUpdateFill(metaObject, UPDATE_TIME, () -> localDateTime, LocalDateTime.class); // 起始版本 3.3.3(推荐)
        }
    }
}
  1. 我们希望表里的 create_time、update_time 字段能在新增或更新的时候能自动填充,不需要在手动的设置,所以才配置上面的自动填充器
  2. 这里演示的填充器,其实可以更丰富,如再加上:createBy、updateBy,即创建人、更新人,可以根据实际需要添加
  3. 这里的时间类型,没有用 Date 类型,而是 jdk8 里的 LocalDateTime 类型(当然Date 类型也可以)

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 {

    /** 创建时间 */
    @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;
}

由于上面已经配了自动填充器,所以这里使用时要在字段上加上相应注解,这样就不用在插入更新时不用在手动 set 值了

(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 implements Serializable {

    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 接口,studentMapper 就是对应的类就是 Student
  2. 接口上不用加 @Mapper、@Repository 这种注解,因为启动类上已经扫描了整个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));

        // 上面用service能做的,用mapper同样能做到,因为service里内部还是走的mapper去查询,可以自己摸索尝试
        // 还有更多常用的特殊查询,如模糊查询,在最后面介绍
        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.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,删除时一样,总之,熟悉之后灵活运用。

原文地址:https://blog.csdn.net/qq_36737803/article/details/122036386

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


1.pom.xml引入依赖 &lt;dependency&gt; &lt;groupId&gt;com.github.pagehelper&lt;/groupId&gt; &lt;artifactId&gt;pagehelper&lt;/artifactId&gt; &lt;version&gt;5
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt; &lt;!DOCTYPE configuration PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot; &qu
准备工作 ① 创建数据库&amp;数据表 ## 创建数据库 CREATE DATABASE `dbtest1`; ## 创建数据表 CREATE TABLE `t_user` ( `id` INT NOT NULL AUTO_INCREMENT, `username` VARCHAR(20) DEF
MyBatis逆向工程是指根据数据库表结构自动生成对应的实体类、Mapper接口以及SQL映射文件的过程。这个过程可以通过MyBatis提供的逆向工程工具来完成,极大地方便了开发人员,避免了重复的代码编写,提高了开发效率。 创建逆向工程的步骤 1、添加依赖&amp;插件 &lt;!-- 控制Mave
MyBatis获取参数值的两种方式:${}和#{} ${}的本质就是字符串拼接,#{}的本质就是占位符赋值。 ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自
resultMap作用是处理数据表中字段与java实体类中属性的映射关系。 准备工作 ① 创建数据库&amp;数据表 CREATE DATABASE `dbtest1`; CREATE TABLE `t_emp` ( `emp_id` int NOT NULL AUTO_INCREMENT, `em
EHCache缓存针对于MyBatis的二级缓存。 MyBatis默认二级缓存是SqlSessionFactory级别的。 添加依赖 &lt;!-- MyBatis-EHCache整合包 --&gt; &lt;dependency&gt; &lt;groupId&gt;org.mybatis.cac
MyBatis 提供了一级缓存和二级缓存的支持,用于提高数据库查询的性能,减少不必要的数据库访问。 一级缓存(SqlSession 级别的缓存) 一级缓存是 MyBatis 中最细粒度的缓存,也称为本地缓存。它存在于每个 SqlSession 的生命周期中,当 SqlSession 被关闭或清空时,
动态SQL是 MyBatis 中非常强大且灵活的功能,允许你根据不同的条件构建SQL查询。 这主要通过 &lt;if&gt;、&lt;choose&gt;、&lt;when&gt;、&lt;otherwise&gt;、&lt;foreach&gt;等标签实现。 查询场景 /** * 根据条件查询员工
本教程操作系统:windows10系统、DELL G3电脑。 MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。在 MyBatis 中,配置数据库连接是非常重要的第一步。下面将详细介绍如何配置 MyBatis 的
今天小编给大家分享的是MyBatis批量查询、插入、更新、删除如何实现,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。
今天小编给大家分享的是Mybatis操作多数据源实现的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获...
本篇文章和大家了解一下mybatis集成到spring的方式有哪些。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。1 前言1.1 集成spring前使用mybat...
今天小编给大家分享的是mybatis-plus分页查询的3种方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获...
本篇内容主要讲解“mybatis之BaseTypeHandler怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“mybatis...
这篇文章主要介绍了mybatisforeach怎么传两个参数的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇mybatisforeach怎...
这篇“MyBatis映射文件中parameterType与resultType怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的...
这篇文章主要介绍“MyBatis怎么获取自动生成的键值”,在日常操作中,相信很多人在MyBatis怎么获取自动生成的键值问题上存在疑惑,小编查阅了各式资料,整理出
这篇文章主要讲解了“怎么去掉IntelliJIDEA中mybatis对应的xml文件警告”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入...
这篇文章主要介绍“MybatisPlus使用@TableId主键id自增长无效如何解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这...