前言
MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,为的就是简化开发、提高效率,Mybatis 有的它都有,它在 MyBatis 的基础上:只做增强、不做改变,且功能强大、非常好用(强烈推荐)
- 本教程会尽量模拟真实开发环境使用,算比较全,涵盖了 mybatis-plus 最常用的的使用过程
- 本项目里使用的 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='学生表';
- 这里模拟的学生表 id,是 bigint 类型,且非自增(一般数据量很多的表不使用自增主键)
- 里面有几乎每个表都存在的 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>
- 上面我只引入了本案例用的一些 jar 包,有其他需要的可自行再引入
- mp 的版本选择了 3.4.2,连接池用了 druid,版本 1.2.6,都比较新,版本可兼容
- 至于分页功能,其实 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(推荐)
}
}
}
- 我们希望表里的 create_time、update_time 字段能在新增或更新的时候能自动填充,不需要在手动的设置,所以才配置上面的自动填充器
- 这里演示的填充器,其实可以更丰富,如再加上:createBy、updateBy,即创建人、更新人,可以根据实际需要添加
- 这里的时间类型,没有用 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> {
}
- mapper 接口直接继承 mp 的 BaseMapper 接口,studentMapper 就是对应的类就是 Student
- 接口上不用加 @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 过程如下:
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
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
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
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,删除时一样,总之,熟悉之后灵活运用。