Mrchen1144 发表于 2022-6-12 08:17

MybatisPlus

文档下载:「md文档」https://www.aliyundrive.com/s/e3jnVkaKsje 提取码: lg84 点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。## ==1. `MybatisPlus`入门==

### 1.1 快速入门

#### 1.1.1 新建工程

新建模块,导入依赖坐标

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cy</groupId>
    <artifactId>mybatisplus_01_quickstart</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
      <java.version>1.8</java.version>
    </properties>

    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.5.0</version>
      <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
       <!-- mybaits plus -->
      <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
      </dependency>

      <!-- druid数据源 -->
      <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
      </dependency>

      <!-- mysql数据库驱动 -->
      <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
      </dependency>

      <!-- spring-test -->
      <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>
            <version>1.18.12</version>
      </dependency>
    </dependencies>

    <build>
      <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
      </plugins>
    </build>

</project>
```

#### 1.1.2 导入数据库

```sql
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;

DROP TABLE IF EXISTS user;

CREATE TABLE user (
            id bigint(20) primary key auto_increment,
            name varchar(32) not null,
            passwordvarchar(32) not null,
            age int(3) not null ,
            tel varchar(32) not null
);
insert into user values(null,'tom','123456',12,'12345678910');
insert into user values(null,'jack','123456',8,'12345678910');
insert into user values(null,'jerry','123456',15,'12345678910');
insert into user values(null,'tom','123456',9,'12345678910');
insert into user values(null,'snake','123456',28,'12345678910');
insert into user values(null,'张益达','123456',22,'12345678910');
insert into user values(null,'张大炮','123456',16,'12345678910');
```





#### 1.1.3 新建实体类

```java
package com.cy.domain;

import lombok.*;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}
```





#### 1.1.4 `application.yml`中配置`Jdbc`参数

```yaml
spring:
datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
    username: root
    password: root
```





#### 1.1.5 定义数据接口,继承`BaseMapper`

```java
package com.cy.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cy.domain.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserDao extends BaseMapper<User> {
}
```





#### 1.1.6 测试类中注入`dao`接口,测试功能

```java
package com.cy;

import com.cy.dao.UserDao;
import com.cy.domain.User;
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 Mybatisplus01QuickstartApplicationTests {

    @Autowired
    private UserDao userDao;

    @Test
    void testGetAll() {
      List<User> userList = userDao.selectList(null);
      System.out.println(userList);
    }

}
```



### 1.2 `MP`简介

#### 1.2.1 `MyBatisPlus`介绍

- `MyBatisPlus`(简称MP)是基于`MyBatis`框架基础上开发的增强型工具,旨在简化开发、提高效率
- 官网:(https:://mybatis-plus.com)/(https://mp.baomidou.com/)

#### 1.3.1 `MyBatisPlus`特性

- 无侵入:只做增强不做改变,不会对现有工程产生影响。导入`MP`后也可以使用原生`Mybatis`。
- 强大的 `CRUD` 操作:内置通用 `Mapper`,少量配置即可实现单表`CRUD` 操作
- 支持 `Lambda`:编写查询条件无需担心字段写错
- 支持主键自动生成
- 内置分页插件







## ==2.标准开发==

### 2.1 CRUD

- 已提供的通用`Mapper`内置的CRUD通用方法

| 功能       | `BaseMapper`中提供方法                                       | 备注                                 |
| ---------- | ------------------------------------------------------------ | -------------------------------------- |
| 新增       | `int insert(T entity)`                                       |                                        |
| 根据id删除 | `int deleteById(Serializable id)`                            | `id`类型不确定,但是都要实现序列化接口 |
| 根据id修改 | `int updateById(@Param("et") T entity)`                      | 按需修改,动态`SQL`                  |
| 根据id查询 | `T selectById(Serializable id)`                              |                                        |
| 查询所有   | `List<T> selectList(Wrapper<T> queryWrapper)`                |                                        |
| 分页查询   | `Ipage<E> selectPage(Ipage<E> page,null)`                  |                                        |
| 条件查询   | `Ipage<E> selectPage(Ipage<E> page,Wrapper<T> queryWrapper)` |                                        |



- 演示代码

```java
package com.cy;

import com.cy.dao.UserDao;
import com.cy.domain.User;
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 Mybatisplus01QuickstartApplicationTests {

      @Autowired
      private UserDao userDao;

      @Test
      void testSave() {
          User user = new User();
          user.setName("程序员");
          user.setPassword("cy");
          user.setAge(12);
          user.setTel("4006184000");
          userDao.insert(user);
      }

      @Test
      void testDelete() {
          userDao.deleteById(1401856123725713409L);
      }

      @test // 已经实现了按需更新
      void testUpdate() {
          User user = new User();
          user.setId(1L);
          user.setName("Tom888");
          user.setPassword("tom888");
          userDao.updateById(user);
      }

      @Test
      void testGetById() {
          User user = userDao.selectById(2L);
          System.out.println(user);
      }


      @Test
      void testGetAll() {
          List<User> userList = userDao.selectList(null);
          System.out.println(userList);
      }
}
```



## 3. 查询

### ==3.1 分页查询==

#### 3.1.1 实现

- 已提供的通用`Mapper`内置的分页查询方法

| 功能   | `BaseMapper`中提供方法                                       | 备注 |
| -------- | ------------------------------------------------------------ | ---- |
| 分页查询 | `Ipage<E> selectPage(Ipage<E> page,null)`                  |      |
| 条件查询 | `Ipage<E> selectPage(Ipage<E> page,Wrapper<T> queryWrapper)` |      |

​                              



- 演示代码

**装配分页拦截器作为Spring管理的bean**

```java
package com.cy.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 MybatisPlusConfig {
      
      @Bean
      public MybatisPlusInterceptor mybatisPlusInterceptor(){
          //1 创建MybatisPlusInterceptor拦截器对象
          MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
          //2 添加分页拦截器
          mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
          return mpInterceptor;
      }
}
```

**执行分页查询**

```java
//分页查询
@Test
void testSelectPage(){
      //1 创建IPage分页对象,设置分页参数
      IPage<User> page=new Page<>(1,3);
      //2 执行分页查询
      userDao.selectPage(page,null);
      //3 获取分页结果
      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());
}

```



#### 3.1.2 常见问题

- 问题描述

使用该插件过程中,常见的问题是总记录数为0。



为0是最容易发现的,其实还有其他问题

- 不是分页查询,而是查了所有记录
- `SQL`语句中没有添加分页条件

等等。



- 原因

分页拦截器,未配置或者配置错误。



- 解决方法

见3.1.1中**装配分页拦截器作为Spring管理的bean**





### 3.2 开启`MyBatisPlus`日志

#### 3.2.1 开启日志

```yaml
# 开启mp的日志(输出到控制台)
mybatis-plus:
configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

```



#### 3.2.2 简化日志(不推荐)

##### 3.2.2.1 取消初始化`spring`日志打印

- 取消做法

在`resources`下新建一个`logback.xml`文件,名称固定,内容为空,如下:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

</configuration>

```

关于logback参考播客:https://www.jianshu.com/p/75f9d11ae011



##### 3.2.3.1 取消`SpringBoot`启动`banner`图标

- 原日志显示如下:

```shell
    .   ____          _            __ _ _
   /\\ / ___'_ __ _ _(_)_ ____ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
   \\/___)| |_)| | | | | || (_| |) ) ) )
    '|____| .__|_| |_|_| |_\__, | / / / /
   =========|_|==============|___/=/_/_/_/
   :: Spring Boot ::                (v2.5.0)

```

- 取消做法

```yaml
spring:
    main:
      banner-mode: off # 关闭SpringBoot启动图标(banner)

```





##### 3.4.3 取消`MybatisPlus`启动`banner`图标

- 原日志显示如下:

```shell
   _ _   |__ _|_. ___ _ |    _
| | |\/|_)(_| | |_\|_)||_|_\
       /               |         
                        3.4.1

```

- 取消做法

```yaml
mybatis-plus:
    global-config:
      banner: off # 关闭mybatisplus启动图标

```



### ==3.3 条件构造器`QueryWrapper`==

- `MyBatisPlus`将书写复杂的`SQL`条件进行了封装,使用编程的方式完成查询条件的组合
- 所有的查询条件封装在`QueryWrapper`对象中,并且在调用通用方法时传递该对象

简单的查询,建议使用`QueryWrapper`;复杂的`SQL`语句,不推荐使用;



#### 3.3.1 条件构造器封装方式

1. **按条件查询**

   手写字段名,字段名容易写错

   ```java
   //方式一:按条件查询
   QueryWrapper<User> qw=new QueryWrapper<>();
   // 设置条件,参数分为为字段名和值
   // lt : less then 小于
   qw.lt("age", 18);
   List<User> userList = userDao.selectList(qw);
   System.out.println(userList);
   
   ```

2. **`lambda`格式按条件查询**

   可以避免字段名写错

   ```java
   //方式二:lambda格式按条件查询
   QueryWrapper<User> qw = new QueryWrapper<User>();
   User user /* = 获取一个user对象*/;
   qw.lambda().lt(User::getName,user.getName());
   List<User> userList = userDao.selectList(qw);
   System.out.println(userList);
   
   ```

3. **`lambda`格式按条件查询(推荐)**

   ```java
   //方式三:lambda格式按条件查询
   LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
   // LambdaQueryWrapper支持链式编程
   lqw.lt(User::getAge, 10);
   // qw.lambda().lt(User::getName,user.getName());
   List<User> userList = userDao.selectList(lqw);
   System.out.println(userList);
   
   ```

   

#### 3.3.2 组合条件

1. 并且关系(and)

   设置条件时默认是并且关系

   ```java
   // 并且关系,默认就是并且关系
   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);
   
   ```

   

2. 或者关系(or)

   ```java
   // 或者关系,需要通过or()方法连接
   LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
   / 小于10岁或者大于30岁
   lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
   List<User> userList = userDao.selectList(lqw);
   System.out.println(userList);
   
   ```

   

#### 3.3.3 `QueryWrapper`方法与功能

- 常用方法

| 方法      | 功能                         | 备注                        |
| ----------- | ---------------------------- | --------------------------- |
| eq          | 指定列的值相同               | equals的简写                |
| ne          | 指定列的值不相同             | not equals的简写            |
| lt          | 指定列的值小于               | less then的简写             |
| le          | 指定列的值小于等于         | less then and equals的简写|
| gt          | 指定列的值大于               | great then 的简写         |
| ge          | 指定列的值大于等于         | great then and equals的简写 |
| between   | 指定列的值在..和…之间      |                           |
| notBetween| 指定列的值不在..和…之间      |                           |
| like      | 指定列的值等值模糊查询       |                           |
| notLIke   | 指定列的值不等值模糊查询   |                           |
| orderByAsc| 按照指定的列升序排序         |                           |
| orderByDesc | 按照指定的列降序排序         |                           |
| groupBy   | 按照指定的列分组             |                           |
| having      | 按照指定的列在分组后条件过滤 |                           |
| in          | 单条件多值查询               |                           |
| isNull      | 指定列的值为空               |                           |
| isNOtNull   | 指定列的值不为空             |                           |



- 用户登录(eq匹配)

```java
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//等同于=
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);

```

- 购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)

```java
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt le gt ge eq between
lqw.between(User::getAge, 10, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

```

- 查信息,搜索新闻(非全文检索版:like匹配)

```java
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//模糊匹配 like
lqw.likeLeft(User::getName, "J");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

```

- 统计报表(分组查询聚合函数)

```java
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.select("gender","count(*) as nums");
qw.groupBy("gender");
List<Map<String, Object>> maps = userDao.selectMaps(qw);
System.out.println(maps);

```

**官方`API`文档**

- 更多查询条件设置参看 [`https://mybatis.plus/guide/wrapper.html#abstractwrapper`](https://mybatis.plus/guide/wrapper.html#abstractwrapper)





#### 3.3.4 NULL值处理



##### 3.3.4.1常规处理方式

手动判空

```java
Integer minAge=10;//将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null;//将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
if(minAge!=null){
    lqw.gt(User::getAge, minAge);
}
if(maxAge!=null){
    lqw.lt(User::getAge, maxAge);
}
List<User> userList = userDao.selectList(lqw);
userList.forEach(System.out::println);

```





##### 3.3.4.2 `QueryWrapper`判空

- 准备查询参数封装对象

```java
@Data
public class User {
      private Long id;
      private String name;
      private String password;
      private Integer age;
      private String tel;
      private Integer online;
}

@Data
public class UserQuery extends User {
      private Integer age2;
}

```

- 测试类中,使用`QueryWrapper`判空

```java
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());
// 链式编程
lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2())
   .gt(null != uq.getAge(),User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

```



### 3.4查询投影

查询投影,就是要查询的列的问题。可能存在

- 只查询表中存在的部分字段,
- 或者查询表中没有的字段(聚合查询)

复杂查询不推荐使用`QueryWrapper`,使用`Mybatis`原生方式,自己编写SQL。



#### ==3.4.1 查询结果包含模型类中部分属性==

```java
/*LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId, User::getName, User::getAge);*/
//或者
QueryWrapper<User> lqw = new QueryWrapper<User>();
// 只指定部分类
lqw.select("id", "name", "age", "tel");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

```

#### 3.4.2 查询结果包含模型类中未定义的属性

```java
QueryWrapper<User> lqw = new QueryWrapper<User>();
// count(*)不存在于表结构中国的字段
lqw.select("count(*) as count, tel");
lqw.groupBy("tel");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);

```



### ==3.5 字段映射与表名映射==

#### 3.5.1 问题一:表字段与实体属性名不一致

- 在模型类属性上方,使用`@TableField`属性注解,通过==**value**==属性,设置当前属性对应的数据库表中的字段关系。



#### 3.5.2 问题二:实体类中添加了数据库中不存在的字段

- 在模型类属性上方,使用`@TableField`注解,通过**==exist==**属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。

查询和增删改的时候,都不会参与数据库的操作。



#### 3.5.3 问题三:不参与查询

- 在模型类属性上方,使用`@TableField`注解,通过**==select==**属性:设置该属性是否参与默认查询(`select *` )。此属性与select()映射配置不冲突。





#### 4.4 问题四:表名与实体类名不同步

- 在<u>**模型类**</u>上方,使用**@TableName**注解,通过**==value==**属性,设置当前类对应的数据库表名称。



```java
@Data
@TableName("tbl_user")
public class User {
    /*
      id为Long类型,因为数据库中id为bigint类型,
      并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型
   */
    private Long id;
    private String name;
    @TableField(value = "pwd",select = false) // 仅仅是不参与查询
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist = false) //表示online字段不参与CRUD操作
    private Boolean online;
}

```



## 4. 增删改`DML`

### 4.1 增:主键生成策略

#### 4.1.1 不同的表的id生成策略不同

- 日志:自增(1,2,3,4,……)
- 购物订单:特殊规则(FQ23948AK3843)
- 外卖单:关联地区日期等信息(10 04 20200314 34 91)
- 关系表:可省略id
- ……



#### 4.1.2 `MybatisPlus`支持的生成策略

##### 4.1.2.1`MybatisPlus`支持8种生成策略

- 五种可用,三种已过期。详情如下:

```java
public enum IdType {
      /**
       * 数据库ID自增
       * <p>该类型请确保数据库设置了 ID自增 否则无效</p>
       */
      AUTO(0),
      /**
       * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
       */
      NONE(1),
      /**
       * 用户输入ID
       * <p>该类型可以通过自己注册自动填充插件进行填充</p>
       */
      INPUT(2),

      /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
      /**
       * 分配ID (主键类型为number或string),
       * 默认使用com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator(雪花算法)
       *
       * @since 3.3.0
       */
      ASSIGN_ID(3),
      /**
       * 分配UUID (主键类型为 string)
       * 默认使用 com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
       */
      ASSIGN_UUID(4),
      /**
       * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
       */
      @Deprecated
      ID_WORKER(3),
      /**
       * @deprecated 3.3.0 please use {@link #ASSIGN_ID}
       */
      @Deprecated
      ID_WORKER_STR(3),
      /**
       * @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
       */
      @Deprecated
      UUID(4);
}

```





##### 4.1.2.2 用法1:逐个主键指定

- 使用注解`@TableId`

- 名称:`@TableId`

- 类型:**属性注解**

- 位置:模型类中用于表示主键的属性定义上方

- 作用:设置当前类中主键属性的生成策略

- 相关属性

    ​        **type**:设置主键属性的生成策略,值参照`IdType`枚举值

   

##### 4.1.2.3 全局配置策略

- `application.yaml`

```yaml
mybatis-plus:
    global-config:
      db-config:
      id-type: assign_id # 全局使用雪花算法生成主键
      table-prefix: tbl_ # 全局指定表名前缀,实体类上就不需要添加@TableName()注解了

```



### 4.2删

#### 4.2.1 按照主键删除批量删除

```java
//删除指定多条数据
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);

userDao.deleteBatchIds(list);

```

#### 4.2.2 根据主键查询多条记录

```java
//查询指定多条数据
List<Long> list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
userDao.selectBatchIds(list);

```

#### 4.2.3 逻辑删除(Delete/Update)

##### 4.2.3.1 概念

在实际环境中,如果想删除一条数据,是否会真的从数据库中删除该条数据?

- 删除操作业务问题:业务数据从数据库中丢弃
- 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中



##### 4.2.3.2 实现

1. **数据库表中添加逻辑删除标记字段**

   

2. **实体类中添加对应成员变量,并(逐个指定)标记对应属性**

   ```java
   package com.cy.domain;
   
   import com.baomidou.mybatisplus.annotation.*;
   
   import lombok.Data;
   
   @Data
   public class User {
   
       private Long id;
      
       //逻辑删除字段,标记当前记录是否被删除
       // value表示未删除,delval表示已删除
       @TableLogic(value="0", delval="1")
       private Integer deleted;
      
   }
   
   ```

   



3. **实体类中添加成员变量后,也可以全局配置**

   ```yaml
   mybatis-plus:
   global-config:
       db-config:
         table-prefix: tbl_
         # 逻辑删除字段名
         logic-delete-field: deleted
         # 逻辑删除字面值:未删除为0
         logic-not-delete-value: 0
         # 逻辑删除字面值:删除为1
         logic-delete-value: 1
   
   ```

   

##### 4.2.3.3 本质及查询

**逻辑删除本质:逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。**



### 4.3 改:乐观锁

#### 4.3.1 概念

- 概念

**乐观锁**:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不用上锁。只在更新的时候会判断一下在此期间别人有没有去更新这个数据。



- 实现思路

**使用数据版本(Version)记录**:

- 这最常用的一种实现方式。为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 `version`字段来实现。
- 当读取数据时,将`version`字段的值一同读出,数据每更新一次,对此`version`值加一。
- 当提交更新的时候,判断数据库表对应记录的**当前版本信息**与**取出来的version值**进行比对,如果数据库表当前版本号与之前取出来的`version`值相等,则予以更新,否则认为是过期数据。

**`CAS:Compare And Swap`**

**使用时间戳记录**



#### 4.3.2 实现

1. **数据库表中添加锁标记字段**

   

2. **实体类中添加对应字段,并设定当前字段为版本字段**

   ```java
   package com.cy.domain;
   
   import com.baomidou.mybatisplus.annotation.TableField;
   import com.baomidou.mybatisplus.annotation.TableLogic;
   import com.baomidou.mybatisplus.annotation.Version;
   import lombok.Data;
   
   @Data
   public class User {
   
           private Long id;
          
       @Version
       private Integer version;
   }
   
   
   ```

   



3. **配置乐观锁拦截器实现锁机制对应的动态`SQL`语句拼装**

   ```java
   package com.cy.config;
   
   import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
   import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
   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 OptimisticLockerInnerInterceptor());
         
         return mpInterceptor;
       }
   }
   
   
   ```

   



4. **使用乐观锁机制在修改前必须先获取到对应数据的`verion`方可正常进行**

   ```java
   @Test
   public void testUpdate() {
       /*User user = new User();
       user.setId(3L);
       user.setName("Jock666");
       user.setVersion(1);
       userDao.updateById(user);*/
      
       //1.先通过要修改的数据id将当前数据查询出来
       //User user = userDao.selectById(3L);
       //2.将要修改的属性逐一设置进去
       //user.setName("Jock888");
       //userDao.updateById(user);
      
       //1.先通过要修改的数据id将当前数据查询出来
       User user = userDao.selectById(3L);   //version=3
       User user2 = userDao.selectById(3L);    //version=3
       user2.setName("Jock aaa");
       userDao.updateById(user2);            //version=>4
       user.setName("Jock bbb");
       userDao.updateById(user);               //verion=3?条件还成立吗?
   }
   
   ```

ysjd22 发表于 2022-6-12 09:39

非常感谢,终于能用了

woai52lll 发表于 2022-6-12 13:05

谢谢,非常详细

xiadongming 发表于 2022-6-12 15:46

wencun 发表于 2022-6-12 19:35

点赞,整理详细

atai 发表于 2022-6-12 22:15

666 ,一起学习JAVA

JamesBond1 发表于 2022-6-13 21:02

感谢感谢!!
页: [1]
查看完整版本: MybatisPlus