# mybatis-plus-demo
**Repository Path**: magickdc/mybatis-plus-demo
## Basic Information
- **Project Name**: mybatis-plus-demo
- **Description**: mybatis-plus 学习 demo
- **Primary Language**: Unknown
- **License**: GPL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-05-18
- **Last Updated**: 2021-09-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Mybatis-Plus 入门
## 1. 简介
Mybatis-Plus(简称MP)是一个Mybatis增强工具,在Mybatis基础上只做增强不做改变,为简化开发、提高效率而生。
1. **润物无声**:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
2. **效率至上**:只须简单配置,即可快速进行CRUD操作,从而减少大量时间。
3. **丰富功能**:热加载、代码生成、分页、性能分析等功能一应俱全。
## 2. 快速入门
### 2.1 创建并初始化数据库
1. 创建数据库: `mybatis-plus`
2. 创建数据表:`user`
```mysql
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`)
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci;
```
3. 插入数据
```mysql
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');
```
### 2.2 创建 Spring Boot 工程
1. 版本选择:2.2.1
```xml
org.springframework.boot
spring-boot-starter-parent
2.2.1.RELEASE
```
2. 引入 Mybatis-Plus 依赖
```xml
com.baomidou
mybatis-plus-boot-starter
3.2.0
```
3. 引入 Mysql 驱动依赖
```xml
mysql
mysql-connector-java
8.0.23
```
4. 引入 Lombok(可选),IDEA中需要安装 Lombok插件
```xml
org.projectlombok
lombok
1.18.18
```
### 2.3 配置数据库信息
在 `application.properties` 配置文件中添加Mysql数据库的相关配置
Spring Boot 2.0 以下版本内置 jdbc 5 驱动
```properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
```
Spring Boot 2.1 以上版本内置 jdbc 8 驱动
```properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
```
### 2.4 创建实体类
创建 `com.example.entity.User`实体类
```java
package com.example.entity;
import lombok.Data;
/**
* @Author 孔德成
* @Slogan 致敬大师,致敬未来的你
*/
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
```
### 2.5 创建 Mapper映射文件
1. 创建 `com.example.mapper.UserMapper`
2. 继承 `BaseMapper`
- 这是 Mybatis-Plus 特性,该接口定义了很多常用方法
3. 传入实体类类型
```java
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.User;
/**
* @Author 孔德成
* @Slogan 致敬大师,致敬未来的你
*/
public interface UserMapper extends BaseMapper {
}
```
4. 在启动类上添加 `@MapperScan` 注解,配置Mapper路径
- Mapper接口实现类对象是动态生成的,启动类默认找不到,需要配置路径
- `@MapperScan("com.example.mapper")`
### 2.6. 测试
```java
package com.example;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class DemoMpApplicationTests {
// 注入 UserMapper
@Autowired
UserMapper userMapper;
@Test
void findAll() {
List users = userMapper.selectList(null);
System.out.println(users);
}
}
```
> 注意:这里 userMapper idea会报错,但是程序可以正常运行,可以给 UserMapper 添加一个 @Repository 注解解决该问题
### 2.7 查看 sql 输出日志
在 配置文件中配置 :
`mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl`
## 3. Mybatis-Plus CRUD
### 3.1 添加
```java
void testAdd() {
User user = new User();
user.setName("孔德成");
user.setAge(11);
user.setEmail("iamliuche@163.com");
// 返回值为受影响行数
int insert = userMapper.insert(user);
}
```
结果:

### 3.2 MP主键策略
| 类型 | 说明 |
| ------------- | ---------------------- |
| **ASSIGN_ID** | 长度为19位的唯一值 |
| **AUTO** | 自动增长 |
| NONE | 没有策略,需要手动设置 |
| INPUT | 需要手动设置 ID |
| ASSIGN_UUID | UUID,唯一值 |
#### 3.2.1 ASSIGN_ID
Mybatis-Plus 默认的主键策略是 ASSIGN_ID(使用了雪花算法)。
**雪花算法:分布式ID生成器**。它是由 Twitter 公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。优点:整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高。

#### 3.2.2 AUTO 自增策略
```java
@TableId(type = IdType.AUTO)
private Long id;
```
要是想影响所有实体的配置,可以设置全局主键配置
```properties
mybatis-plus.global-config.db-config.id-type=auto
```
### 3.3 更新
```java
@Test
void testUpdate() {
User user = new User();
user.setName("Decheng Kong");
user.setId(1394490488815190018L);
int count = userMapper.updateById(user);
}
```
### 3.4 自动填充
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。我们可以使用 Mybatis-Plus 的自动填充功能,完成这些字段的赋值工作。
#### 3.4.1 准备工作
1. 在 user 表中添加 datetime 类型的新字段 create_time、update_time
2. 在表对应实体类中添加对应属性
```java
private Date createTime;
private Date updateTime;
```
3. 在属性上添加自动填充注解
```java
// insert的时候自动填充该属性
@TableField(fill = FieldFill.INSERT)
private Date createTime;
// insert 和 update 的时候都自动填充该属性
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
```
4. 创建一个类实现`MetaObjectHandler`接口,实现两个方法,一个方法添加时执行,一个方法修改时执行,在方法里设置要添加什么值
```java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// MP 执行添加操作的时候执行
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
// MP 执行更新操作,这个方法执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
```
5. 测试
```java
@Test
void testInsertAndUpdate() {
// insert
// User user = new User();
// user.setName("uzi");
// user.setEmail("xxx@email.com");
// user.setAge(24);
// userMapper.insert(user);
// update
User user = new User();
user.setId(1394515905960263681L);
user.setName("uzi-RNG");
userMapper.updateById(user);
}
```
### 3.5 乐观锁
**乐观锁是解决丢失更新问题的一种解决方案。**
当多人同时操作同一条数据时,可能出现该种问题。比如**抢票**:

**应用场景:**
当要更新一条记录时,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新。
**实现方式:**
1. 取出记录时获取当前 version
2. 更新时,带上这个 version
3. 执行更新时,`set version = new Version where version=oldVersion`,如果 version 不对,就更新失败

### 3.6 MP实现乐观锁
#### 3.6.1 修改表结构和实体类
在表里添加 `version` 字段。
修改实体类,添加 `@Version`注解 在 version 属性上。
```java
@Version
private Integer version;
```
#### 3.6.2 创建配置文件
创建 `config` 包,创建文件 `MPConfig.java`
此时可以删除主类中的 `@MapperScan` 扫描注解
```java
package com.example.config;
@Configuration
@MapperScan("com.example.mapper")
public class MPConfig {
}
```
#### 3.6.3 注册乐观锁插件
```java
@Configuration
@MapperScan("com.example.mapper")
public class MPConfig {
/**
* 配置乐观锁插件
*
* @return
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
```
配置 version 属性自动填充
```java
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
```
```java
this.setFieldValByName("version", 1, metaObject);
```
#### 3.6.4 测试
先添加一条记录,初始化版本号
```java
@Test
void testAdd() {
User user = new User();
user.setName("王五");
user.setAge(11);
user.setEmail("iamliuche@163.com");
// 返回值为受影响行数
int insert = userMapper.insert(user);
}
```
执行更新操作,观察版本号是否自动增长
```java
@Test
void testOptimisticLocker() {
User user = userMapper.selectById(1394535763972243457L);
user.setName("张三");
int count = userMapper.updateById(user);
}
```
### 3.7 查询
#### 3.7.1 通过多个 id 批量查询
```java
/**
* 根据多个 id 批量查询
*/
@Test
void testSelect1() {
List users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
System.out.println(users);
}
```
#### 3.7.2 简单的条件查询
```java
/**
* 简单条件查询
*/
@Test
void testSelect2() {
HashMap columnMap = new HashMap<>();
columnMap.put("name", "Jone");
columnMap.put("age", 18);
List users = userMapper.selectByMap(columnMap);
System.out.println(users);
}
```
#### 3.7.3 分页查询
1. 在`MPConfig.java` 配置文件中配置分页插件
```java
/**
* 配置分页插件
*
* @return
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
```
2. 编写分页代码
- 创建 `Page`对象,传入两个参数`当前页`和`每页记录数`
- 调用 MP 的方法实现分页
- ```java
/**
* 测试分页查询
*/
@Test
void testSelect3() {
Page userPage = new Page<>(1, 3);
userMapper.selectPage(userPage, null);
// 总页数
long pages = userPage.getPages();
// 当前页
long current = userPage.getCurrent();
// 数据集合
List records = userPage.getRecords();
// 总记录数
long total = userPage.getTotal();
// 当前页是否有下一页
boolean hasNext = userPage.hasNext();
// 当前页是否有上一页
boolean hasPrevious = userPage.hasPrevious();
System.out.println(pages);
System.out.println(current);
System.out.println(records);
System.out.println(total);
System.out.println(hasNext);
System.out.println(hasPrevious);
}
```
####
### 3.8 删除
#### 3.8.1 根据 id 删除记录
```java
@Test
void testDelete1() {
int i = userMapper.deleteById(1L);
}
```
#### 3.8.2 根据 id 批量删除
```java
@Test
void testDelete2() {
int i = userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));
}
```
#### 3.8.3 根据简单条件删除
```java
@Test
void testDelete3() {
HashMap columnMap = new HashMap<>();
columnMap.put("name","Billie");
columnMap.put("age","24");
int i = userMapper.deleteByMap(columnMap);
}
```
### 3.9 逻辑删除
#### 3.9.1 物理删除和逻辑删除
1. **物理删除:真实删除**,将对应数据从数据库中删除,之后查询不到此条被删除数据
2. **逻辑删除:假删除**,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录。
#### 3.9.2 逻辑删除实现流程
1. 数据库修改:添加 `deleted` 字段,设置默认值为 `false`
```mysql
ALTER TABLE `user` ADD COLUMN `deleted` boolean DEFAULT FALSE;
```
2. 实体类修改:添加 `deleted` 属性,并加上 `@TableLogic` 注解
```java
@TableLogic
private Integer deleted;
```
3. 配置(可选)
```properties
# 删除用1表示,不删除用0表示,此为默认值
mybatis-plus.global-config.db-config.logic-delete-field=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
```
4. 测试
```java
@Test
void testDelete4() {
int i = userMapper.deleteById(1394490488815190018L);
}
```
> 可以发现,数据并没有被删除,只是 deleted 字段更新为了1,只有 deleted 字段为0的记录才能执行删除方法。查询的时候也会过滤掉 deleted=0的记录
### 4. 条件构造器和常用接口
#### 4.1 Wrapper
用来构造复杂查询条件,传入查询语句中,可完成复杂查询
```java
QueryWrapper