大家好,我是 Leo😀,之前自学那会有简单了解过 MyBatisPlus 相关文档,但是随着时间长不用,一些 api 以及配置都忘了,想翻笔记发现当时的自己只是囫囵吞枣的学习了,任何学习记录都没有留下来,😅😅😅,所以大家可千万不要学我哈! 这不最近打算重新学习一下 MyBatisPlus(下文简称 MP),这里也是写篇博客来整理一下相关知识点,与君共勉🫡🫡🫡!
话不多说,我们先来看一张简单的思维导图,了解一下 MP 的主要核心功能,脑图在手,思路我有,开干🤔!
在快速开始我们入门 Demo 之前,我来首先简单介绍一下 MP
官网: https://baomidou.com/
简介: MyBatis-Plus
(opens new window)(简称 MP)是一个 MyBatis (opens new window) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景: 我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
特性:
我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能,在此之前,你需要学会:
现有一张 User
表,其表结构如下:
id | name | age | emial | address |
---|---|---|---|---|
1 | admin | 20 | admin@qq.com | 湖北武汉 |
3 | test | 20 | test@qq.com | 安徽合肥 |
4 | zhangsan | 17 | zhangsan@qq.com | 北京海淀 |
其对应的数据库 Schema 脚本如下:
DROP TABLE IF EXISTS user;
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
对应的数据库 Data 脚本如下:
DELETE FROM user;
INSERT INTO user (id, name, age, email,address) VALUES
(1, 'admin', 18, 'test1@admin.com','湖北武汉'),
(3, 'test', 20, 'test2@test.com','湖北武汉'),
(4, 'zhangsan', 21, 'test4@zhangsan.com','湖北武汉'),
本次教程会以 Maven
工程创建修改为 SpringBoot
工程。
我这里是在父工程下面创建许多子模块,这样就不需要每一个新的 demo 都需要去创建一个新的工程了,也方便 Demo 项目统一管理。
引入 SpringBoot Starter 父工程:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
</parent>
引入 spring-boot-starter
、 spring-boot-starter-test
、 mybatis-plus-boot-starter
、MySQL 依赖:
<dependencies>
<!--spring-boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBootTest-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
在 application.yml
配置文件中添加 MySQL 数据库的相关配置:
server:
port: 8800
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: root
password: root
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志
mapper-locations: classpath:mapper/*.xml
在 Spring Boot 启动类中添加 @MapperScan
注解,扫描 Mapper 文件夹:
package com.Leo.mp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
/**
* @author : Leo
* @version 1.0
* @date 2023/8/31 13:26
* @description : 启动类
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@MapperScan("com.Leo.mp.mapper")
public class Application
{
public static void main(String[] args)
{
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
Environment environment = context.getBean(Environment.class);
System.out.println("访问链接:http://localhost:" + environment.getProperty("server.port"));
System.out.println("(♥◠‿◠)ノ゙ 项目启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}
编写实体类 User.java
(此处使用了 Lombok (opens new window) 简化代码)
package com.Leo.mp.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : Leo
* @version 1.0
* @date 2023/8/31 13:32
* @description : User实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private String address;
}
编写 Mapper 包下的 UserMapper
接口
/**
* @author : Leo
* @version 1.0
* @date 2023/8/31 13:33
* @description :
*/
public interface UserMapper extends BaseMapper<User> {
}
以上工作都做完就可以开始进行功能测试了
package com.Leo.mp.controller;
import com.Leo.mp.mapper.UserMapper;
import com.Leo.mp.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author : Leo
* @version 1.0
* @date 2023/8/31 13:43
* @description :
*/
@RestController
public class UserController
{
@Autowired
private UserMapper userMapper;
@GetMapping("/list")
public List<User> userList()
{
return userMapper.selectList(null);
}
}
提示 UserMapper 中的
selectList()
方法的参数为 MP 内置的条件封装器Wrapper
,所以不填写就是无任何条件
启动项目,打开浏览器
通过以上几个简单的步骤,我们就实现了 User 表的 查询
功能,甚至连 XML 文件都不用编写!
从以上步骤中,我们可以看到集成 MyBatis-Plus
非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。
但 MP
的强大远不止这些功能,想要详细了解 那就继续往下看吧!
本文将介绍 MybatisPlus
注解包相关类详解(更多详细描述可点击查看源码注释)
实体类
@TableName("user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private String address;
}
关于 autoResultMap
的说明:
MP 会自动构建一个 resultMap
并注入到 MyBatis 里(一般用不上),请注意以下内容:
因为 MP 底层是 MyBatis,所以 MP 只是帮您注入了常用 CRUD 到 MyBatis 里,注入之前是动态的(根据您的 实体类字段
以及注解变化而变化),但是注入之后是静态的(等于 XML
配置中的内容)。
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
private String address;
}
IdType
public class User {
private Long id;
@TableField("username")
private String name;
private Integer age;
private String email;
private String address;
}
我们可以通过 Ctrl + 鼠标左键点进去看看
描述:乐观锁注解、标记
@Version
在字段
描述:表字段逻辑处理注解(逻辑删除)
描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
MP
默认实现 5 种主键生成策略,分别是:
nmber
。INPUT
。String
和 number
。IdType
默认的全局设置为 IdType.ASSIGN_ID, 即由 mybatis-plus 主动分配主键,默认情况下由默认主键生成器实现类 DefaultIdentifierGenerator 采用雪花算法填充主键。
public DbConfig() {
this.idType = IdType.ASSIGN_ID;
this.tableUnderline = true;
this.capitalMode = false;
this.logicDeleteValue = "1";
this.logicNotDeleteValue = "0";
this.insertStrategy = FieldStrategy.NOT_NULL;
this.updateStrategy = FieldStrategy.NOT_NULL;
this.whereStrategy = FieldStrategy.NOT_NULL;
}
在 SpringBoot
中,可以通过如下配置更改全局配置。
mybatis-plus:
global-config:
db-config:
id-type: assign_id
说明:
boolean condition
表示该条件是否加入最后生成的 sql 中,例如:query.like (StringUtils.isNotBlank (name), Entity::getName, name) .eq (age!=null && age >= 0, Entity::getAge, age)boolean
类型的入参,默认为 true
Param
均为 Wrapper
的子类实例 (均具有 AbstractWrapper
的所有方法)R
为泛型,在普通 wrapper 中是 String
, 在 LambdaWrapper
中是 函数 (例: Entity::getId
, Entity
为实体类, getId
为字段 id
的 getter Method )R column
均表示数据库字段,当 R
具体类型为 String
时则为数据库字段名 (字段名是数据库关键字的自己用转义符包裹)! 而不是实体类数据字段名!!!,另当 R
具体类型为 SFunction
时项目 runtime 不支持 eclipse 自家的编译器!!!下面看一图来简单了解一下 Wrapper
的结构
条件构造抽象类,最顶端父类,抽象类中提供 3 个方法以及其他方法
用于查询条件封装,生成 sql 的 where 条件,QueryWrapper (LambdaQueryWrapper) 和 UpdateWrapper (LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件,entity 属性也用于生成 sql 的 where 条件
这里列举一下他的重要方法
Lambda 语法使用 Wrapper 统一处理解析 lambda 获取 column。
用于 Lambda 语法使用的查询 Wrapper
Lambda 更新封装 Wrapper
实体类 对象封装操作类,不是用 lambda 语法,自身的内部属性 实体类 也用于生成 where 条件
说明: 继承自
AbstractWrapper
, 自身的内部属性entity
也用于生成 where 条件 及LambdaUpdateWrapper
, 可以通过new UpdateWrapper().lambda()
方法获取!
set(String column, Object val)
set(boolean condition, String column, Object val)
set("name", "Leo")
set("name", "")
—> 数据库字段值变为空字符串set("name", null)
—> 数据库字段值变为 null
setSql(String sql)
setSql("name = 'Leo'")
LambdaWrapper
在 QueryWrapper
中是获取 LambdaQueryWrapper
在 UpdateWrapper
中是获取 LambdaUpdateWrapper
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
UpdateWrapper<Student> updateWrapper = new UpdateWrapper<Student>();
updateWrapper.eq("name", "Leo").eq("age", 18).set("id", 100);
empolyeeMapper.update(student, updateWrapper);
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
package com.Leo.mp;
import com.Leo.mp.mapper.UserMapper;
import com.Leo.mp.pojo.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
/**
* @author : Leo
* @version 1.0
* @date 2023/8/31 13:36
* @description :
*/
@SpringBootTest
public class MpTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
@Test
void test1() {
// 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于17
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.isNotNull("name")
.ge("age",17);
userMapper.selectList(wrapper).forEach(System.out::println);
}
@Test
void test2(){
// 查询名字admin
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.eq("name","admin");
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); // 区间
Long count = userMapper.selectCount(wrapper);// 查询结果数
System.out.println(count);
}
// 模糊查询
@Test
void test4(){
QueryWrapper<User> wrapper = new QueryWrapper<User>()
// 左和右 t%
.notLike("name","ad")
.likeRight("email","@qq.com");
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 student 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);
}
}
对于单表的操作,虽然 MP 大大简化了操作,但是每次都需要我们重新创建文件,从 mapper 到 controller,那将会带来太大的工作量。
于是我们选择使用 MP 为我们带来的新版生成器,一劳永逸🥳
注意 适用版本:mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!
目前支持两套生成的方式,一套使用 SQL 查询的方式是兼容旧的代码生成器核心逻辑使用,另一套使用驱动规范来读取元数据的方式,默认的使用元数据查询方式来生成代码
导入依赖
<!--mybatis-plus-generator 生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</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>
<version>2.3.31</version>
</dependency>
<!--beetl引擎模板-->
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.8.1.RELEASE</version>
</dependency>
快速生成
这里我已经根据官网配置生成好了一个通用的 MP 工具类,大家可以直接拿来用,所见即所得。
package com.cisyam.code.util;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
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.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author : gaoziman
* @description : MP 代码生成器
* @date 2023/7/19 20:01
*/
public class CodeUtil {
public static void main(String[] args) {
//这里按着给的注解修改参数即可
//手动配置数据源
//注意修改数据库名
String url = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8";
String name = "root";
String password = "root";
//数据库表的设置
//设置需要自动代码生成的表名
List<String> listTable = Arrays.asList("employee", "user");
//设置 过滤 表的后缀
List<String> listTableSuffix = Collections.singletonList("_b");
//设置 过滤 表的后缀
List<String> listTablePrefix = Arrays.asList("t_", "c_");
//基本信息
String author = "gaoziman";
//作者
String parent = "com.cisyam";
//父包名
String module = "code";
//模块包名
//已封装好,无需更改。可按照需求进行注释
//1、配置数据源
FastAutoGenerator.create(url, name, password)
//2、全局配置
.globalConfig(builder -> {
builder.author(author) // 设置作者名
.outputDir(System.getProperty("user.dir") + "/src/main/java") //设置输出路径:项目的 java 目录下【System.getProperty("user.dir")意思是获取到项目所在的绝对路径】
.commentDate("yyyy-MM-dd hh:mm:ss") //注释日期
.dateType(DateType.ONLY_DATE) //定义生成的实体类中日期的类型 TIME_PACK=LocalDateTime;ONLY_DATE=Date;
.fileOverride() //覆盖之前的文件
.enableSwagger() //开启 swagger 模式
.disableOpenDir(); //禁止打开输出目录,默认打开
})
//3、包配置
.packageConfig(builder -> {
builder.parent(parent) // 设置父包名
.moduleName(module) //设置模块包名
.entity("pojo") //pojo 实体类包名
.service("service") //Service 包名
.serviceImpl("service.impl") // ***ServiceImpl 包名
.mapper("mapper") //Mapper 包名
.xml("mapper.xml") //Mapper XML 包名
.controller("controller") //Controller 包名
.other("config") //自定义包名(一般不在这里生成,而是后面编写的时候自己建包)
.pathInfo(Collections.singletonMap(OutputFile .xml, System.getProperty("user.dir") + "/src/main/resources/mapper")); //配置 mapper.xml 路径信息:项目的 resources 目录下
})
//4、策略配置
.strategyConfig(builder -> {
builder
.enableCapitalMode() //开启大写命名
.enableSkipView() //创建实体类的时候跳过视图
.addInclude(listTable) // 设置需要生成的数据表名
.addTableSuffix(listTableSuffix) //设置 过滤 表的后缀
.addTablePrefix(listTablePrefix) // 设置 过滤 表的前缀
//4.1、实体类策略配置
.entityBuilder()
.enableChainModel() //开启链式模型
//.disableSerialVersionUID() //默认是开启实体类序列化,可以手动disable使它不序列化。由于项目中需要使用序列化就按照默认开启了
.enableTableFieldAnnotation() // 开启生成实体时生成字段注解
.enableLombok() //开启 Lombok
.versionColumnName("version") //乐观锁字段名(数据库)
.versionPropertyName("version") //乐观锁属性名(实体)
.logicDeleteColumnName("deleted") //逻辑删除字段名(数据库)
.logicDeletePropertyName("deleteFlag") //逻辑删除属性名(实体)
.naming(NamingStrategy.underline_to_camel) //数据库表映射到实体的命名策略:默认是下划线转驼峰命。这里可以不设置
.columnNaming(NamingStrategy.underline_to_camel) //数据库表字段映射到实体的命名策略:下划线转驼峰命。(默认是和naming一致,所以也可以不设置)
.addTableFills(
new Column("create_time", FieldFill.INSERT),
new Column("modify_time", FieldFill.INSERT_UPDATE)
) //添加表字段填充,"create_time"字段自动填充为插入时间,"modify_time"字段自动填充为插入修改时间
.idType(IdType.AUTO) //设置主键自增
//4.2、Controller策略配置
.controllerBuilder()
.enableHyphenStyle() //开启驼峰连转字符
.formatFileName("%sController") //格式化 Controller 类文件名称,%s进行匹配表名,如 UserController
.enableRestStyle() //开启生成 @RestController 控制器
//4.3、service 策略配置
.serviceBuilder()
.formatServiceFileName("%sService") //格式化 service 接口文件名称,%s进行匹配表名,如 UserService
.formatServiceImplFileName("%sServiceImpl") //格式化 service 实现类文件名称,%s进行匹配表名,如 UserServiceImpl
//4.4、Mapper策略配置
.mapperBuilder()
.superClass(BaseMapper.class) //设置父类
.enableBaseResultMap() //启用 BaseResultMap 生成
.enableBaseColumnList() //启用 BaseColumnList
.formatMapperFileName("%sMapper") //格式化 mapper 文件名称
.enableMapperAnnotation() //开启 @Mapper 注解
.formatXmlFileName("%sMapper") //格式化Xml文件名称
.formatMapperFileName("%sMapper"); //格式化Mapper文件名称
})
//5、模板
.templateEngine(new VelocityTemplateEngine())
/*
模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker(以下两个引擎用哪个就保留哪个)
.templateEngine(new BeetlTemplateEngine())
.templateEngine(new FreemarkerTemplateEngine())
*/
.templateEngine(new FreemarkerTemplateEngine())
//本人选择了Freemarker
//6、执行
.execute();
}
}
配置了之后就不需要在实体类主键上配置了
mybatis-plus:
global‐config:
db‐config:
id‐type: auto
物理删除: 在删除的时候直接将数据从数据库干掉 DELTE 逻辑删除: 从逻辑层面控制删除,通常会在表里添加一个逻辑删除的字段比如 enabled 、is_deleted ,数据默认是有效的(值为 1),当用户删除时将数据修改 UPDATE 0, 在查询的时候就只查 where is_deleted =1.
在阿里开发规范中,当我们设计数据库字段表示有 ” 是 否 “ 相关概念是,应该是设置为 is_xxxx 类型。
需要添加逻辑删除的字段
局部单表逻辑删除,需要在对应的 pojo
类加入对应的逻辑删除标识字段
@TableLogic // 代表逻辑删除(单个字段的)
private Integer is_deleted;
开启全局逻辑删除配置,如果进行了全局逻辑删除配置并且指定了,就可以不用在每个实体类中配置了 @TableLogic
mybatis-plus:
global‐config:
db-config:
id‐type: auto
logic-delete-field: is_deletd # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
当执行删除, 将会把逻辑删除字段进行修改,执行的 sql 语句为
update user set is_deletd = 0 where id= ?
当执行查询时。会自动查询有效数据 where flag=1
select id,name,age,email,address from user where is_deleted=1
该类继承了 IPage
类,实现了 简单分页模型
如果你要实现自己的分页模型可以继承 Page
类或者实现 IPage
类
要使用 MP 的分页插件,必须配置一个全局配置类,才可以全局生效。下面我就直接放代码,大家可以直接拿来用。
package com.manman.common.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Loe
*/
@Configuration
@MapperScan("com.Leo.mp.mapper")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
悲观锁: 悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界 (包括本系统当前的其他事务,以及来自外部系统的事务处理) 修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。 假设功能并发量非常大,就需要使用 synchronized 来处理高并发下产生线程不安全问题, 会使其他线程进行挂起等待从而影响系统吞吐量
乐观锁: 乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。 假设功能产生并发几率极少,采用乐观锁版本机制对比, 如果有冲突 返回给用户错误的信息
在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题
丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户 1 把值从 500 改为 8000,用户 B 把值从 500 改为 200,则多人同时提交同一条记录,后提交的把之前的提交数据覆盖。 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读。例如:用户 A,B 看到的值都是 500,用户 B 把值改为 200,用户 A 读到的值仍为 500。
针对一种问题的解决方案,为解决问题而生的。解决什么问题呢?主要是解决丢失更新问题如下图理解
为了解决这些并发带来的问题。 我们需要引入并发控制机制。
在对应的实体类中加 version 字段,并设置成下面这样
@Version //这就是控制版本的
@TableField(fill = FieldFill.INSERT) //这个方便在添加的时候设置版本初始为1
private Integer version; //版本的字段
MetaObjectHandler
自动填充,像创建时间,更新时间也可以在这操作。@Component
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//这里的“version”就是指定的字段,设置初始值为1,之后每修改一次+1
this.setFieldValByName("version",1,metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
}
}
在创建一个配置类,开启一个乐观锁插件
@Configuration
@MapperScan("com.zhz.mapper")//这里就是你的mapper文件的包
public class MyBatisConfig {
//乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
接下来在做增加数据的时候,调用 insert 添加方法就可以了。修改的时候呢,我们需要先查人后再做修改,因为我们为了防止问题的发生,需要先去查询版本号比对才进行后续操作!!
以上便是关于 MP 相关的内容,本人才疏学浅,文章有什么错误的地方,欢迎大佬们批评指正!我是 Leo
,一个在互联网行业的小白,立志成为更好的自己。