MyBatisPlus基础知识
参考链接:https://www.bilibili.com/video/BV12T4y1B7C3
1 MyBatisPlus简介
1.1 简介
MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率。
1.2 开发方式
- 基于MyBatis使用MyBatisPlus
- 基于Spring使用MyBatisPlus
- 基于SpringBoot使用MyBatisPlus
1.3 SpringBoot整合MyBatis开发过程
1、创建SpringBoot工程

2、勾选配置使用的技术

1)删除无用的文件

2)手动添加mp依赖
【原因】mp未被收录到idea的系统内置配置,无法直接选择加入
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

3、设置dataSource相关属性(JDBC参数application.yml)
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatisplus_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&serverTimezone=UTC
username: root
password: qwe321456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
main:
banner-mode: off # 去掉springboot的图标
server:
port: 8888
# mp日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
globalConfig:
banner: false # 去掉mybatis-plus的banner
4、制作实体类与表结构
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` varchar(32) DEFAULT NULL COMMENT '用户名',
`password` varchar(32) DEFAULT NULL COMMENT '密码',
`age` int(3) DEFAULT NULL COMMENT '年龄',
`tel` varchar(32) DEFAULT NULL COMMENT '电话',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
User
package com.fanxl.mybatisplus.domain;/**
*@Author: fanxl
*@CreateTime: 2023-04-30 13:27
*/
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
5、定义数据层接口映射配置,继承BaseMapper<User>
package com.fanxl.mybatisplus.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fanxl.mybatisplus.domain.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserDao extends BaseMapper<User> {
}
6、再测试类中注入dao接口,测试功能
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll(){
List<User> userList = userDao.selectList(null);
System.out.println(userList);
}
}
2 标准数据层开发

| 名称 | 含义 |
|---|---|
| eq | 等于= |
| ne | 不等于!= |
| gt | 大于> |
| ge | 大于等于>= |
| lt | 小于< |
| le | 小于等于<= |
| between | 包含 例between(A,B) A<= x <=B |
| like | 模糊查询 %a% |
| likeRight | a% |
| likeLeft | %a |
2.1 测试分页
@Test
void testGetByPage(){
IPage page = new Page(1,2);
userDao.selectPage(page,null);
System.out.println("当前页码值"+page.getCurrent());
System.out.println("每页显示数"+page.getSize());
System.out.println("一共多少页"+page.getPages());
System.out.println("一共多少条数据"+page.getTotal());
System.out.println("数据"+page.getRecords());
}
输出结果:
当前页码值1
每页显示数2
一共多少页0
一共多少条数据0
数据[User(id=1, name=Tom11, password=tom, age=3, tel=1111), User(id=2, name=Jerry, password=jerry, age=4, tel=2222), User(id=3, name=Jock, password=jock, age=18, tel=3333), User(id=4, name=Timi, password=tmi, age=25, tel=4444), User(id=1652556289647108097, name=分水岭, password=123456, age=18, tel=8888)]
问题:page.getRecords()执行的查询结果相当于是select all,分页相当于是select * from user ??? limit 1,2
在SQL语句的基础上添加一些功能,拦截并增强
解决办法:
1)配置MyBatisPlus的拦截器,在拦截器中开启分页拦截器。

2)在类上添加Configuration注解。
package com.fanxl.mybatisplus.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mpInterceptor(){
// 1、定义mp拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
// 2、添加具体的拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
2.2 时间更新
2.2.1 数据库级别
1、在数据库中添加时间和修改时间字段
- 添加时间设置默认值:
CURRENT_TIMESTAMP,不勾选根据时间戳更新; - 修改时间设置默认值:
CURRENT_TIMESTAMP,并勾选根据当前时间戳更新。


2、在实体类中添加字段
private Date createTime;
private Date updateTime;
3、编写测试类
此时可以看到除了插入的记录,其余数据的创建时间和更新时间都为空。

解决办法:在创建表时设置该字段不为空


2.2.2 代码级别
1、删除数据库中字段的默认值和更新操作。

2、实体类字段属性上增加注解@TableField
fill:字段自动填充策略
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
3、编写处理器处理注解。在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口。
package com.fanxl.mybatisplus.controller;
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;
import java.util.Date;
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill......");
this.setFieldValByName("createTime",new Date(),metaObject);
//MetaObject[反射对象类]是Mybatis的工具类,通过MetaObject获取和设置对象的属性值。
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill.....");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
插入数据后的结果:

更新数据后的结果:

3 条件查询
- 条件查询——设置查询条件
- 格式一:常规格式
//查询年龄小于18岁的用户
QueryWrapper qw = new QueryWrapper();
qw.lt("age",18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
- 格式二:链式编程格式
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//查找10~30岁之间的用户
lqw.lt(User::getAge,30).gt(User::getAge,10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 格式三:lambda格式按条件查询(查询)
QueryWrapper<User> qw = new QueryWrapper();
qw.lambda().lt(User::getAge,18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
- 格式四:lambda格式(推荐)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge,18);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 条件查询——组合查询条件
- 并且(and)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//查找10~30岁之间的用户
lqw.lt(User::getAge,30).gt(User::getAge,10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 或者(or)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//查找小于10岁或者大于30岁的用户
lqw.lt(User::getAge,30).or().gt(User::getAge,10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 条件查询——null值处理
- User实体类
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
- 定义一个UserQuery继承User
@Data
public class UserQuery extends User {
private Integer age2; //描述年龄上限
}
- 编写测试类
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
// 先判断第一个参数是否为true,如果为true连接当前条件
lqw.lt(null!=uq.getAge2(),User::getAge,uq.getAge2());
lqw.gt(null!=uq.getAge(),User::getAge,uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
4 查询投影
设置查询出来显示的字段
- 查询结果包含模型类中部分属性
【lambda格式select里面只能列当前实体类内部的属性名】
错误写法:
LambdaQueryWrapper<Student> student = new LambdaQueryWrapper<>();
student.select("score");
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId,User::getName); //只适用于lambda的格式
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
- 查询结果包含模型类中未定义的属性
select里面的字段名为数据库表中的字段名
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count,age"); //统计个数
lqw.groupBy("age");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);
5 查询条件设置
5.1 等于
//条件查询
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//等于 =
lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");
User loginUser = userDao.selectOne(lqw); //专门查询一个对象
System.out.println(loginUser);
5.2 范围查询
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt(<)、le(<=)、gt(>)、ge(>=)、eq(=)、between
lqw.between(User::getAge,10,30); //第一个字段为小值,第2个字段为大值
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
5.3 模糊匹配
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//模糊匹配
// lqw.like(User::getName,"J");
// lqw.likeRight(User::getName,"J");
lqw.likeLeft(User::getName,"J");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
6 映射匹配兼容性
6.1 表字段与编码属性设计不同步
问题一:表字段与编码属性设计不同步

解决办法:使用@TableField属性注解,value设置数据库表字段名称。

6.2 编码中添加了数据库中未定义的属性
问题二:编码中添加了数据库中未定义的属性

解决办法:使用@TableField属性注解,设置exist字段属性为false。默认为true,此属性无法与value合并使用。

6.3 采用默认查询开放了更多的字段查看权限
问题三:使用默认查询时可查询密码字段不安全。

解决办法:使用@TableField属性注解,设置select属性是否参与查询,此属性与select()映射配置不冲突。

6.4 表名与编码开发设计不同步
问题四:表名与编码开发设计不同步

解决办法:使用TableName注解,value设置数据库表名称。

7 id生成策略控制
7.1 AUTO:使用数据库id自增策略控制id生成
1)设置主键类型为auto

2)将数据表的类型设为自动增长


3)编写测试类测试

7.2 INPUT:用户手工输入id
1)设置主键生成策略为input

2)取消自动递增

3)指定ID值


7.3 ASSIGN_ID:雪花算法生成id(可兼容数值型与字符串型)
雪花算法:生成1个由64位的二进制组成的串,最终是一个Long值。

8 全局配置
8.1 全局配置主键生成策略
在applicaiton.yml文件中添加以下属性,同时取消实体类中的配置 @TableId(type = IdType.ASSIGN_ID)
dbConfig:
idType: assign_id
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
globalConfig:
banner: false # 去掉mybatis-plus的banner
dbConfig:
idType: assign_id
8.2 全局配置数据库表名
在配置文件中添加tablePrefix: tbl_字段。
# mp日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
globalConfig:
banner: false # 去掉mybatis-plus的banner
dbConfig:
idType: assign_id
tablePrefix: tbl_
9 多数据删除与查询
9.1 deleteBatchIds
@Test
void testDelete(){
List<Long> list = new ArrayList<Long>();
list.add(1652931780614860802L);
list.add(1652934902670188545L);
list.add(1652936421721337858L);
userDao.deleteBatchIds(list);
}
9.2 selectBatchIds
List<Long> list = new ArrayList<Long>();
list.add(1L);
list.add(2L);
list.add(3L);
userDao.selectBatchIds(list);
10 逻辑删除
- 删除操作业务问题:业务数据从数据库中丢弃
- 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中。
10.1 操作步骤
1、在数据库中添加数据状态字段,并设置字段默认值为0,代表未删除。

2、在实体类中添加字段数据状态字段,并添加@TableLogic注解,0代表未删除,1代表删除了,
@Data
//@TableName("tbl_user")
public class User {
// @TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
@TableField(value="pwd",select = false)
private String password;
private Integer age;
private String tel;
@TableField(exist = false)
private Integer online;
//逻辑删除字段,标记当前记录是否被删除
@TableLogic(value = "0" ,delval = "1")
private Integer deleted;
}
3、编写测试类测试,执行删除操作,可看到执行的数据库语句为update,数据库字段变为1。


4、测试全局查询操作,过滤了已经被删除的数据。如果想看全部数据,需要自己写SQL语句。

10.2 全局配置逻辑删除
# mp日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
globalConfig:
banner: false # 去掉mybatis-plus的banner
dbConfig:
idType: assign_id
tablePrefix: tbl_
logicDeleteField: deleted #逻辑删除字段
logicDeleteValue: 1 #删除值
logicNotDeleteValue: 0 #未删除值
11 乐观锁
解决并发问题。
步骤:
1、在数据表中添加字段version,并设置默认值为1

2、实体类中添加字段

3、添加乐观锁拦截器实现锁机制对应的动态SQL语句拼装

测试:
1、将数据更新时,需要指定Version,否则version不会更新。
User user = new User();
user.setId(3L);
user.setVersion(1);
user.setName("Jock66");
userDao.updateById(user);
2、使用乐观锁机制在修改前必须先获取到对应数据的verison访客正常进行。
//注意事项:在进行修改时,version数据必须要收集!!!!!!!!
//先通过要修改数据的id将当前数据查询出来
User user1 = userDao.selectById(3L);
//将要修改的属性逐一设置进去
user1.setName("JocK88");
userDao.updateById(user1);
3、模拟多用户操作
多个用户修改时,
User user1 = userDao.selectById(3L); //version = 3
User user2 = userDao.selectById(3L); //verison = 3
//将要修改的属性逐一设置进去
user2.setName("JocK77");
userDao.updateById(user2);
user1.setName("JocK99"); //执行失败
userDao.updateById(user1);
- 执行修改前先执行查询语句:
SELECT id,name,age,tel,deleted,version FROM tbl_user WHERE id=? AND deleted=0 - 执行修改时使用version字段作为乐观锁检查依据:
SELECT id,name,age,tel,deleted,version FROM tbl_user WHERE id=? AND deleted=0
12 代码生成器
- 模版:MyBatisPlus提供
- 数据库相关配置:读取数据库获取信息
- 开发者自定义配置:手工配置
12.1 在pom文件中添加依赖
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
12.2 核心代码
- 创建代码生成器的对象 AutoGenerator
- 设置数据源 DataSourceConfig
- 设置全局配置 GlobalConfig
- 设置包名相关配置 PackageConfig
- 策略设置 StrategyConfig
- 执行代码生成器 autoGenerator.execute();
package com.fanxl.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
public class CodeGenerator {
public static void main(String[] args) {
//1、创建代码生成器的对象
AutoGenerator autoGenerator = new AutoGenerator();
//2、设置数据源
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("qwe321456");
autoGenerator.setDataSource(dataSource);
//3、设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
// 设置代码生成位置
globalConfig.setOutputDir("E:/IdeaProjects/练习项目文件夹/generator/src/main/java");
// 设置生成完毕后是否打开生成代码所在的目录,默认为false
globalConfig.setOpen(false);
// 设置作者
globalConfig.setAuthor("分水岭");
//设置是否覆盖原始生成的文件
globalConfig.setFileOverride(true);
//设置数据层接口名,%s为占位符,指代模块名称,即数据库表名
globalConfig.setMapperName("%sDao");
//设置Id生成策略
globalConfig.setIdType(IdType.ASSIGN_ID);
autoGenerator.setGlobalConfig(globalConfig);
//4、设置包名相关配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setEntity("domain"); //设置实体类包名
packageConfig.setMapper("dao"); //设置数据层包名
packageConfig.setParent("com.fanxl"); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径。
autoGenerator.setPackageInfo(packageConfig);
//5、策略设置
StrategyConfig strategyConfig = new StrategyConfig();
//设置当前参与生成的表名,参数为可变参数
strategyConfig.setInclude("tbl_user");
//设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 User = tbl_user - tbl_
strategyConfig.setTablePrefix("tbl_");
//设置是否启用rest风格 启用的话,controller层的注解为@RestController,否则为Controller
strategyConfig.setRestControllerStyle(true);
//设置乐观锁字段名
strategyConfig.setVersionFieldName("version");
//设置逻辑删除字段名
strategyConfig.setLogicDeleteFieldName("deleted");
//设置是否启用lombok
strategyConfig.setEntityLombokModel(true);
autoGenerator.setStrategy(strategyConfig);
//6、执行代码生成器
autoGenerator.execute();
}
}

浙公网安备 33010602011771号