mybatisplus学习笔记
1. 简介
Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。这是官方给的定义,关于mybatis-plus的更多介绍及特性,可以参考

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.快速入门
地址:https://baomidou.com/guide/quick-start.html#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B7%A5%E7%A8%8B
步骤
1.创建数据库 mybatis_plus
创建表
DROP TABLE IF EXISTS user;
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
--真实开发环境中,version(乐观锁),deleted(逻辑删除),gmt_create,gmt_modified
插入数据
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
3.导入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
说明:我们使用mybatis-plus 可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus因为版本有差异!
5.连接数据库!这一步和mybatis相同
# mysql 5 驱动不同 com.mysql.jdbc.Driver
# mysql 8 驱动不同 com.mysql.cj.jdbc.Driver . 需要增加时区的配置 serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
6.传统的方式pojo-dao(连接mybatis,配置mapper.xml文件)-service-controller
6.使用了mybatis-plus之后
-
pojo
-
mapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.codeyuaiiao.pojo.User;
import org.springframework.stereotype.Repository;
//在对应的mapper上面继承基本的类 BaseMapper
注意点:需要在主启动类
MybatisPlusApplication上扫描我们Mapper包下的所有接口@MapperScan("com.aly.mapper") -
测试类中测试
-
查询结果:

2. 配置日志
我们所有的sql是不可见的,我们希望知道他是怎么执行的,所以我们必须看日志!
# 配置日志 (默认控制台输出)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

3. CRUD扩展
1. 插入数据
//插入数据
@Test
public void testInsert() {
User user = new User();
user.setName("lkk");
user.setAge(3);
user.setEmail("956402755@qq.com");
int result = userMapper.insert(user);
System.out.println(result);
System.out.println(user);
}
注意点:数据库插入的id默认值为:全局的唯一id
2. 主键生成策略
默认 ID_WORKER 全局唯一id
对应数据库中的主键(uuid.自增id.雪花算法.redis.zookeeper)
雪花算法😦Twitter的snowflake算法)
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0.可以保证几乎全球唯一
主键自增
我们需要配置主键自增:
-
实体类字段上
@TableId(type = IdType.AUTO) -
数据库字段一定要是自增
![在这里插入图片描述]()
![在这里插入图片描述]()

其余源码解释
public enum IdType {
AUTO(0),//数据库ID自增
NONE(1),//该类型为未设置主键类型
INPUT(2),//用户输入ID
//该类型可以通过自己注册自动填充插件进行填充
//以下3种类型、只有当插入对象ID 为空,才自动填充。
ID_WORKER(3),//全局唯一ID (idWorker)
UUID(4),//全局唯一ID (UUID)
ID_WORKER_STR(5);//字符串全局唯一ID (idWorker 的字符串表示)
3. 更新数据
动态sql
注意:updateById()参数是 一个对象!
//测试更新
@Test
public void testUpdate() {
User user = new User();
user.setId(2L);
user.setName("阿峧不是山交");
//注意:updateById()参数是 一个对象!
int i = userMapper.updateById(user);
System.out.println(i);
}


4. 自动填充
阿里巴巴开发手册:所有的数据库表gmt_create .gmt_modified几乎所有的表都要配置上!而且需要自动化!
方式一:数据库级别(navicat不好设置)
1.在表中新增字段 create_time , update_time

2.再次测试插入方法,我们需要先把实体类同步
private Date creatTime; private Date updateTime;
再次更新查看结果即可

方式二:代码级别
1.删除数据库默认值

2.实体类字段属性上添加注解
//记住用util包下的Date!!
//字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
3.编写处理器来处理这个注解
package com.aly.hander;
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.util.Date;
/**
* @author aly
* @version 1.0
* @date 2020/11/8 21:24
*/
@Slf4j
@Component //把处理器加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("Start insert fill.... ");
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("Start update fill.... ");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
4.测试插入
5.测试更新,观察时间即可
5. 乐观锁
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新值测试
悲观锁:顾名思义十分悲观,他总是认为出现问题,无论干什么都会上锁!再去操作!
OptimisticLockerInnerInterceptor乐观锁插件
当要更新一条记录的时候,希望这条记录没有被别人更新 乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
说明:
-
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
-
整数类型下
newVersion = oldVersion + 1 -
newVersion会回写到entity中 -
仅支持
updateById(id)与update(entity, wrapper)方法 -
在
update(entity, wrapper)方法下,wrapper不能复用!!!
使用方法使用方法
// 版本号,用于实现乐观锁,数据库中也增加相应的version字段
@Version // 这个注解是关键
private Integer version;
package com.aly.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author aly
* @version 1.0
* @date 2020/11/8 21:55
*/
@EnableTransactionManagement
@Configuration
@MapperScan("com.aly.mapper")
public class MybatisConfig {// mp的乐观锁插件
// @Bean 已废弃
// public OptimisticLockerInterceptor optimisticLockerInterceptor() {
// return new OptimisticLockerInterceptor();
// }
// 注册乐观锁插件(新版:3.4.0)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件
return interceptor;
}
}
// 乐观锁测试
@Test
public void testLock() {
User user1 = userMapper.selectById(1325417883190177795L);
User user2 = userMapper.selectById(1325417883190177795L);
user1.setAge(50);
userMapper.updateById(user1);
user1.setAge(80);
userMapper.updateById(user2);
}
6.查询操作
//测试查询
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
//测试批量查询
public void testSelectBatchId(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
//按条件查询之--使用Map操作
@Test
public void testSelectBatchIds(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","阿峧说java");
map.put("age","18");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
7.逻辑删除
说明: 只对自动注入的sql起效: 插入: 不作限制 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段 删除: 转变为 更新 例如: 删除: update user set deleted=1 where id = 1 and deleted=0 查找: select id,name,deleted from user where deleted=0 字段类型支持说明: 支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime) 如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now() 附录: 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
物理删除:从数据库中直接移除
逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效! deleted=0=>deleted=1
管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!
测试:
1.在数据表中增加一个deleted字段

2.实体类中增加属性
//逻辑删除 @TableLogic private Integer deleted;
3.配置
# 配置逻辑删除 mybatis-plus.global-config.db-config.logic-delete-value=1 #逻辑已删除值(默认为 1) mybatis-plus.global-config.db-config.logic-not-delete-value=0 #逻辑未删除值(默认为 0)
走的是更新操作,不是删除操作
查询的时候会自动过滤删除的数据
8.分页查询
@EnableTransactionManagement
@Configuration
@MapperScan("com.aly.mapper")
public class MybatisConfig {// mp的乐观锁插件
// 注册乐观锁插件(新版:3.4.0)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
//分页插件查询
@Test
public void queryUserForPage() {
IPage<User> userPage = new Page<>(1, 2);//参数一是当前页,参数二是每页个数
userPage = userMapper.selectPage(userPage, null);
System.out.println("总条数:" + userPage.getTotal());
System.out.println("当前页码:" + userPage.getCurrent());
System.out.println("总页码:" + userPage.getPages());
System.out.println("每页显示条数:" + userPage.getSize());
System.out.println("返回的数据:" + userPage.getRecords());
List<User> list = userPage.getRecords();
for (User user : list) {
System.out.println(user);
}
}
4. 性能分析插件
性能分析插件能够很好的分析每条sql语句执行的时间,以至于我们可以去进行sql优化
我们只需要在配置类中添加
/**
* SQL 执行性能分析插件
* 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
* dev:开发环境
* test:测试环境
* prod:生产环境
*/
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//ms,超过此处设置的ms则sql不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
然后我们在application.propertis中添加
#环境设置:dev、test、prod spring.profiles.active=dev
表示当前环境为开发环境
然后我们随意执行一条sql语句
查看控制台打印的东西:
这个表示执行这条代码所花费的时间以及执行的sql语句和插入的数据,如果超过了我们设置的时间,就会抛出异常,我们通过性能分析,可以很好的对sql语句进行性能优化
5. 条件构造器
官网:https://mybatis.plus/guide/wrapper.html#abstractwrapper
Wrapper条件构造器:我们写一些复杂的sql就可以使用它来替代!
介绍 : 上图绿色框为抽象类abstract 蓝色框为正常class类,可new对象 黄色箭头指向为父子类关系,箭头指向为父类 wapper介绍 : Wrapper :条件构造抽象类,最顶端父类,抽象类中提供4个方法西面贴源码展示 AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件 AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。 LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper LambdaUpdateWrapper : Lambda 更新封装Wrapper QueryWrapper : Entity 对象封装操作类,不是用lambda语法 UpdateWrapper : Update 条件封装,用于Entity对象更新操作 AbstractWrapper 说明: QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类 用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件 注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
常用词解释:
where WHERE 语句,拼接 + WHERE 条件
and AND 语句,拼接 + AND 字段=值
andNew AND 语句,拼接 + AND (字段=值) (新的括号)
or OR 语句,拼接 + OR 字段=值
orNew OR 语句,拼接 + OR (字段=值)
eq 等于=
allEq 基于 map 内容等于=
ne 不等于<>
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=
like 模糊查询 LIKE
notLike 模糊查询 NOT LIKE
in IN 查询
notIn NOT IN 查询
isNull NULL 值查询
isNotNull IS NOT NULL
groupBy 分组 GROUP BY
having HAVING 关键词
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 条件语句
notExists NOT EXISTS 条件语句
between BETWEEN 条件语句
notBetween NOT BETWEEN 条件语句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last("LIMIT 1")
测试
@SpringBootTest
public class WapperTest {
//继承了BaseMapper, 所有的方法都来自己父类
//我们也可以编写自己的扩展方法
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
// 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.ge("age", 12);
userMapper.selectList(wrapper).forEach(System.out::println); // 和我们刚才学习的map对比一下
}
@Test
void test2() {
// 查询名字狂神说
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "狂神说");
User user = userMapper.selectOne(wrapper); // 查询一个数据,出现多个结果使用List或者 Map
System.out.println(user);
}
@Test
void test3() {
// 查询年龄在 20 ~ 30 岁之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30); // 区间
Integer count = userMapper.selectCount(wrapper);// 查询结果数
System.out.println(count);
}
// 模糊查询
@Test
void test4() {
// 查询年龄在 20 ~ 30 岁之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 左和右 t%
wrapper
.notLike("name", "e")
.likeRight("email", "t");//指的是%在左还是右
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
// 模糊查询
@Test
void test5() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// id 在子查询中查出来
wrapper.inSql("id", "select id from user where id<3");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
//测试六
@Test
void test6() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 通过id进行排序
wrapper.orderByAsc("id");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
}
6. 代码自动生成器
dao、pojo、service、controller都给我自己去编写完成!
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
<properties>
<mybatis-plus-generator.version>3.2.0</mybatis-plus-generator.version>
<velocity.version>2.1</velocity.version>
<freemarker.version>2.3.29</freemarker.version>
<ibeetl.version>3.0.13.RELEASE</ibeetl.version>
</properties>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus-generator.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>${ibeetl.version}</version>
</dependency>
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.junit.Test;
/**
* 运行此方法生成mybatis代码
* 生成代码自动放入对应目录
*/
public class MyBatisGeneratorRun {
//数据库类型
private final DbType dbType = DbType.MYSQL;
//数据库连结信息
private final String dbUrl = "jdbc:mysql://localhost:3306/zzf?useUnicode=true&characterEncoding=UTF-8";
private final String driver = "com.mysql.jdbc.Driver";
private final String userName = "root";
private final String password = "root";
//项目名
private final String projectName = "spring-boot";
//指定包名
private final String packageName = "com.zzf.springboot";
//controller基础类
private final String superControllerClass = packageName + ".common.BaseController";
//entity基础类
private final String superEntityClass = packageName + ".common.BaseEntity";
//模块名 如果有模块名,则需在模块名前加. 例:.log
private final String moduleName = "";
//作者名
private final String author = "zhuzhifeng";
//指定生成的表名
private final String[] tableNames = new String[]{"sys_role"};


