MyBatisPlus学习笔记
一、MybatisPlus概述
1.1、简介
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
1.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.3、快速入门
mybatis-plus写的sql与方法
步骤:
- 创建数据库
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)
);
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');
---- 真实开发中,version(乐观锁)、delete(逻辑删除)、gmt_create等
- 编写项目,初始化项目!使用Springboot初始化!
- 导入相关依赖
<!--mybatis-plus是自己开发的并非开源的(最新版的)-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
说明:使用mybatis-plus可以节省大量打啊代码,尽量不用同时导入mybatis和mybatis-plus!(可能会冲突)
- 数据库连接配置
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#Mysql8版本需要在url上单独配置时区(serverTimezone=Asia/Shanghai),同时两者的驱动也是有差异的
- 使用了Mybatis-plus之后
-
编写pojo
-
创建mapper接口
-
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
// 在对应的Mapper接口上面继承基本的类BaseMapper
@Mapper
//@Repository //代表是Mapper层即可
public interface UserMapper extends BaseMapper<User> {
//所有的crud已基本完成了
//不用需要像之前配置大量文件了
}
注意点:我们需要再主启动类上去扫描我们的mapper包下的所有接口
@MapperScan("com.yjr.springboot005mybatis_plus.mapper")
测试
@SpringBootTest
class Springboot005MybatisPlusApplicationTests {
// 继承了BaseMapper,所有的方法都来自自己的父类,我们可以编写自己的拓展方法。
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
//参数是一个Wrapper,条件构造器,这里不用写写为null
//查询所有的用户
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
结果
二、配置日志
2.1、日志配置
配置日志
# 配置日志--控制台输出
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
- insert插入
//测试插入
@Test
public void insert(){
User user=new User();
//mybatis-plus会自动生成id
user.setAge(3);
user.setName("radan");
user.setEmail("1.@qq.com");
int insert = userMapper.insert(user);//insert是受影响的行数
System.out.println(insert);
System.out.println(user);//当执行insert方法时,mybatis-plus会自动生成id
}
- 更新操作
public void update(){
User user=new User();
// user.setAge(3);
user.setId(4l);
// user.setName("radan");
user.setEmail("2.@qq.com");
//注意:updateById 但是参数是一个对像!重点:通过条件就会拼接sql
// UPDATE user SET name=?, age=?, email=? WHERE id=?
// UPDATE user SET email=? WHERE id=?
int i = userMapper.updateById(user);//受影响的行数
System.out.println(i);
}
2.2、主键生成策略
对应数据库中的组件 (UUID,自增id,雪花算法,redis生成)
默认ASSIGN_ID全局唯一策略
雪花算法:
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想:使用41bit作为毫秒数,10bit作为机器的ID(5bit是数据中心,5个bit是机器的ID),12bit作为毫秒内的流水号(意味着每个节点在没毫秒可以产生4096个ID),最后还有一个符号位,永远是0。
主键自增
- 实体类字段上添加注解 @TableId(type = IdType.AUTO)
- 数据库锁对应的字段必须是自增的
IdType 类其它的源码解释
- AUTO(0) 数据库id自增
- NONE(1) 不设置主键
- INPUT(2) 手动输入,必须自己配置id
- ASSIGN_ID(3) 雪花算法
- ASSIGN_UUID(4); //全局 全局唯一id uuid
2.3、 自动填充(时间操作)
代码级别操作
- 创建create_time和update_time字段
- 实体类属性上需要添加注解
//字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
- 编写处理器来处理
@Component //一定不要忘记将处理器加到IOC容器当中
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("插入填充");
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("更新填充");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
2.4、乐观锁
乐观锁:十分乐观,认为不会出现问题。无论干什么不去上锁!如果出现了问题,再次更新值测试。
悲观锁:无论干什么都会上锁,然后再去操作。
乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
配置乐观锁的实现步骤:
- 在数据库中对应的表中建立version字段
- 在实体类的字段上加上@Version注解
注意:- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下 newVersion = oldVersion + 1
- newVersion 会回写到 entity 中
- 仅支持 updateById(id) 与 update(entity, wrapper) 方法
- 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
- 编写配置类
@Configuration //标明这是一个配置类
@ComponentScan("com.yjr.springboot005mybatis_plus.mapper") //指定扫描的包
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
- 编写测试类
@Test
public void testOptimisticLockerInterceptor(){
//查询指定用户
User user = userMapper.selectById(2L);
user.setName("王冬");
//更新指定用户
userMapper.updateById(user);
}
测试
当oldVersion和newVersion信息不一致就会导致更新失败
2.5、查询数据
2.5.1、单个查询
// 1. 按照id进行单个用户的查询
User user = userMapper.selectById(1L);
2.5.2、多条件查询
// 3. 多条件查询之一:使用 map 操作
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("id",1l);
hashMap.put("name","radan");
List<User> users = userMapper.selectByMap(hashMap);
2.5.3、批量查询
// 2. 查询批量用户 前三个用户 SELECT id,name,age,email,version,create_time,update_time FROM user WHERE id IN ( ? , ? , ? )
for (User u1 : userMapper.selectBatchIds(Arrays.asList(1, 2, 3))) {
System.out.println(u1);
}
2.5.3、分页查询
- 原始的limit进行分页
- PageHelper第三方插件
- Mybatis-plus也内置了分页插件
mybatis-plus中的使用分页插件的步骤:
- 配置拦截器即可
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
- 直接使用Page对象即可
@Test
public void pageLimit(){
//参数一(当前页) 参数二:页面大小
Page<User> objectPage = new Page<>(2,5);
userMapper.selectPage(objectPage,null);
List<User> records = objectPage.getRecords();//objectPage.getRecords()得到分页后的所有记录
for (User user : records) {
System.out.println(user);
}
System.out.println("当前的记录总数:"+objectPage.getTotal());//得到当前的记录总数
System.out.println("当前的页数:"+objectPage.getCurrent());
}
2.6、删除操作
- 根据ID删除记录
//通过id进行删除
int result = userMapper.deleteById(1691640078306185217L); //返回值为受影响的行数
- 批量删除
//批量删除
int batchIds = userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
- 多条件删除
//多条件删除方式之一,map
HashMap<String,Object> map=new HashMap<>();
map.put("name","jom");
int i = userMapper.deleteByMap(map);
逻辑删除:从数据库中并没有移除,而是通过一个变量来让他失效!deleteState=0或1
物理删除:从数据库中直接移除
管理员可以查看到被删除的记录!防止数据的丢失,类似于回收站。
逻辑删除的使用步骤
- 在YML中配置
#逻辑删除组件
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
2 .在实体类字段上加上@TableLogic注解
@TableLogic //逻辑删除注解
private Integer deleted;
在查询的时候会自动过滤掉deleted为1的字段
常见问题:
- 如何进行插入操作
- 字段在数据库定义默认值(推荐)
- insert 前自己 set 值
- 使用 自动填充功能
三、性能分析插件
Mybatis-Plus提供共性能分析插件,如果超这个时间就会停止运行!
作用:性能分析拦截器,用于输出每条 SQL 语句及其执行时间
该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 3.1.0 以上版本
- 导入p6spy依赖
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>最新版本</version>
</dependency>
- application.yml配置
spring:
# 数据源
datasource:
username: root
password: 123456
# GMT%2B8:GMT+8 格林尼治东八时区
url: jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
- spy.properties 配置
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
测试结果
四、条件构造器Wrapper
复杂的sql就可以利用Warpper进行实现
4.1、Wrapper查询
- 查询name不为空的用户,并且邮箱不为空的数据,年龄大于等于12的
@Test
public void test(){
//查询name不为空的用户,并且邮箱不为空的数据,年龄大于等于12的
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name");
wrapper.isNotNull("email"); //name和email不为空
wrapper.ge("age",12); //age>=12
for (User user : userMapper.selectList(wrapper)) {
System.out.println(user);
}
}
//SELECT * FROM user WHERE deleted=0 AND (name IS NOT NULL AND email IS NOT NULL AND age >= 12)
- 查询名字为radan的数据
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","Tom");
userMapper.selectOne(wrapper); //查询一个数据,出现多个数据会报错的
//SELECT * FROM user WHERE deleted=0 AND (name = 'Tom')
注意:查询一个数据,查询多个数据结果使用list 或者 map
- 查询age在(12,20)之间的用户数
@Test
public void test12(){
//查询age在(12,20)之间的用户数
//SELECT COUNT( * ) AS total FROM user WHERE deleted=0 AND (age BETWEEN ? AND ?)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",12,20);
Long aLong = userMapper.selectCount(wrapper);//selectCount查询结果数
System.out.println(aLong);
}
- 模糊查询
public void test3(){
//模糊查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
//like==>%e%, likeLeft==>%t(末尾有t) likeRight==>t%(开头有t)
wrapper.like("name","e")
.likeRight("email","t");
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
for (Map<String, Object> map : maps) {
System.out.println(map);
}
// SELECT * FROM user WHERE deleted=0 AND (name LIKE '%e%' AND email LIKE 't%')
}
- 子查询
public void test6(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
// id在子查询中查出
wrapper.inSql("id","select id from user where id< 3");
for (User user : userMapper.selectList(wrapper)) {
System.out.println(user);
}
//SELECT * FROM user WHERE deleted=0 AND (id IN (select id from user where id< 3))
}
4.2、 排序
@Test
public void test7(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
// wrapper.orderByAsc("id");//通过id升序排序
wrapper.orderByDesc("id");//通过id降序排序
//SELECT * FROM user WHERE deleted=0 ORDER BY id DESC
for (User user : userMapper.selectList(wrapper)) {
System.out.println(user);
}
}
五、代码生成器
适用版本:mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!
MyBatis-Plus 的代码生成器可以生成 Entity、Mapper、Mapper XML、Service、Controller 模块代码。
5.1、依赖导入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis-plus是自己开发的并非开源的-->
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.2</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--代码生成器-->
<!--mybatis-plus代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!--velocity模板-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!--freemarker模板-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
5.2、配置文件
server.port=8088
spring.profiles.active=dev
##数据库连接配置
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plue?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
5.3、代码生成类
package com.yjr;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author radan
* @Description
* @date 2023/8/17 17:51
*/
public class PracticeApplication {
public static void main(String[] args) {
List<String> tables = new ArrayList<>();
tables.add("user"); //可以给tables添加多个表名
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plue","root","123456")
.globalConfig(builder -> {
builder.author("radan") //作者
.outputDir(System.getProperty("user.dir")+"\\src\\main\\java") //输出路径(写到java目录)
// .enableSwagger() //开启swagger
.commentDate("yyyy-MM-dd")
.disableOpenDir() //不打开文件管理
.fileOverride(); //开启覆盖之前生成的文件
})
.packageConfig(builder -> {
builder.parent("com.yjr")
// .moduleName("practice")
.entity("pojo")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.mapper("mapper")
.xml("mapper")
.pathInfo(Collections.singletonMap(OutputFile.mapperXml,System.getProperty("user.dir")+"\\src\\main\\resources\\mapper"));
})
.strategyConfig(builder -> {
builder.addInclude(tables)
// .addTablePrefix("p_")//过滤表前缀
.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl")
.entityBuilder()
.enableLombok()
.logicDeleteColumnName("deleted")
.enableTableFieldAnnotation()
.controllerBuilder()
// 映射路径使用连字符格式,而不是驼峰
.enableHyphenStyle()
.formatFileName("%sController")
.enableRestStyle()
.mapperBuilder()
//生成通用的resultMap
.enableBaseResultMap()
.superClass(BaseMapper.class)
.formatMapperFileName("%sMapper")
.enableMapperAnnotation()
.formatXmlFileName("%sMapper");
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
结果