MyBatis-Plus用起來真的很舒服



 


一、MyBatis-Plus

1、簡介

  MyBatis-Plus 是一個 Mybatis 增強版工具,在 MyBatis 上擴充了其他功能沒有改變其基本功能,為了簡化開發提交效率而存在。

官網文檔地址:
  https://mp.baomidou.com/guide/

MyBatis-Plus 特性:
  https://mp.baomidou.com/guide/#%E7%89%B9%E6%80%A7

2、使用 SpringBoot 快速使用 MyBatis-Plus

(1)准備工作
  需要 Java 開發環境(JDK)以及相應的開發工具(IDE)。
  需要 maven(用來下載相關依賴的 jar 包)。
  需要 SpringBoot。
  可以使用 IDEA 安裝一個 mybatis-plus 插件。

 

 

 

(2)創建一個 SpringBoot 項目。
  方式一:去官網 https://start.spring.io/ 初始化一個,然后導入 IDE 工具即可。
  方式二:直接使用 IDE 工具創建一個。 Spring Initializer。

 

 

 

 

 

 

(3)添加 MyBatis-Plus 依賴(mybatis-plus-boot-starter)

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.1.tmp</version>
</dependency>

 

(4)為了測試開發,此處使用 mysql 8,需要引入 mysql 相關依賴。
  為了簡化代碼,引入 lombok 依賴(減少 getter、setter 等方法)。

復制代碼
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
</dependency>
復制代碼

 

(5)完整依賴文件(pom.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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lyh.test</groupId>
    <artifactId>test-mybatis-plus</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>test-mybatis-plus</name>
    <description>測試 -- 測試 MyBatis-Plus 功能</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

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

</project>
復制代碼

 

(6)使用一個表進行測試。

  僅供參考,可以定義 創建時間、修改時間等字段。

復制代碼
DROP DATABASE IF EXISTS testMyBatisPlus;

CREATE DATABASE testMyBatisPlus;

USE testMyBatisPlus;

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)
);

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');
復制代碼

 

 

 

(7)在 application.yml 文件中配置 mysql 數據源信息。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/testMyBatisPlus?useUnicode=true&characterEncoding=utf8

 

 

 

(8)編寫表對應的 實體類。

復制代碼
package entity;

import lombok.Data;

@Data
public class User {
    private Long id;
    private String name;
    private int age;
    private String email;
}
復制代碼

 

 

 

(9)編寫操作實體類的 Mapper 類。
  直接繼承 BaseMapper,這是 mybatis-plus 封裝好的類。

復制代碼
package mapper;

import bean.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface UserMapper extends BaseMapper<User> {
}
復制代碼

 

 

 

 

 

 

(10)實體類、Mapper 類都寫好了,就可以使用了。
  Step1:先得在啟動類里掃描 Mapper 類,即添加 @MapperScan 注解

復制代碼
package com.lyh.test.testmybatisplus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("mapper")
@SpringBootApplication
public class TestMybatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestMybatisPlusApplication.class, args);
    }

}
復制代碼

 

  Step2:寫一個測試類測試一下。

復制代碼
package com.lyh.test.testmybatisplus;

import bean.User;
import 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 TestMybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        for(User user:userList) {
            System.out.println(user);
        }
    }
}
復制代碼

 

 

 

(11)總結:
  通過以上簡單操作,就能對 user 表進行 CRUD 操作,不需要去編寫 xml 文件。
注:
  若遇到 @Autowired 標記的變量出現 紅色下划線,但是不影響 正常運行。

 

 

 

  可以進入 Settings,找到 Inspection,並選擇其中的 Spring Core -> Code -> Autowiring for Bean Class,將 Error 改為 Warning,即可。

 

 

 

二、Mybatis-Plus 常用操作

1、配置日志

【參考地址(兩種方式配置日志)】
    https://blog.csdn.net/dfBeautifulLive/article/details/100700365

 

  想要查看執行的 sql 語句,可以在 yml 文件中添加配置信息,如下。

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

 

  如下圖所示:執行時會打印出 sql 語句。

 

 

 

2、簡單認識一下常用注解

復制代碼
【@TableName 】
    @TableName               用於定義表名
注:
    常用屬性:
        value                用於定義表名

【@TableId】
    @TableId                 用於定義表的主鍵
注:
    常用屬性:
        value           用於定義主鍵字段名
        type            用於定義主鍵類型(主鍵策略 IdType)

   主鍵策略:
      IdType.AUTO          主鍵自增,系統分配,不需要手動輸入
      IdType.NONE          未設置主鍵
      IdType.INPUT         需要自己輸入 主鍵值。
      IdType.ASSIGN_ID     系統分配 ID,用於數值型數據(Long,對應 mysql 中 BIGINT 類型)。
      IdType.ASSIGN_UUID   系統分配 UUID,用於字符串型數據(String,對應 mysql 中 varchar(32) 類型)。

【@TableField】  
    @TableField            用於定義表的非主鍵字段。
注:
    常用屬性:
        value                用於定義非主鍵字段名
        exist                用於指明是否為數據表的字段, true 表示是,false 為不是。
        fill                 用於指定字段填充策略(FieldFill)。
        
    字段填充策略:(一般用於填充 創建時間、修改時間等字段)
        FieldFill.DEFAULT         默認不填充
        FieldFill.INSERT          插入時填充
        FieldFill.UPDATE          更新時填充
        FieldFill.INSERT_UPDATE   插入、更新時填充。

【@TableLogic】
    @TableLogic           用於定義表的字段進行邏輯刪除(非物理刪除)
注:
    常用屬性:
        value            用於定義未刪除時字段的值
        delval           用於定義刪除時字段的值
        
【@Version】
    @Version             用於字段實現樂觀鎖
復制代碼

 

3、代碼生成器

(1)AutoGenerator 簡介
  AutoGenerator 是 MyBatis-Plus 的代碼生成器,通過 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率。
  與 mybatis 中的 mybatis-generator-core 類似。

(2)添加依賴

復制代碼
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.3.1.tmp</version>
</dependency>
<!-- 添加 模板引擎 依賴 -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.2</version>
</dependency>
復制代碼

 

(3)代碼分析
Step1:
  創建一個 代碼生成器。用於生成代碼。
  此處不用修改。

// Step1:代碼生成器
AutoGenerator mpg = new AutoGenerator();

 

Step2:
  配置全局信息。指定代碼輸出路徑,以及包名、作者等信息。
  此處按需添加,projectPath 需要修改,setAuthor 需要修改。

復制代碼
// Step2:全局配置
GlobalConfig gc = new GlobalConfig();
// 填寫代碼生成的目錄(需要修改)
String projectPath = "E:\\myProject\\test\\test_mybatis_plus";
// 拼接出代碼最終輸出的目錄
gc.setOutputDir(projectPath + "/src/main/java");
// 配置開發者信息(可選)(需要修改)
gc.setAuthor("lyh");
// 配置是否打開目錄,false 為不打開(可選)
gc.setOpen(false);
// 實體屬性 Swagger2 注解,添加 Swagger 依賴,開啟 Swagger2 模式(可選)
//gc.setSwagger2(true);
// 重新生成文件時是否覆蓋,false 表示不覆蓋(可選)
gc.setFileOverride(false);
// 配置主鍵生成策略,此處為 ASSIGN_ID(可選)
gc.setIdType(IdType.ASSIGN_ID);
// 配置日期類型,此處為 ONLY_DATE(可選)
gc.setDateType(DateType.ONLY_DATE);
// 默認生成的 service 會有 I 前綴
gc.setServiceName("%sService");
mpg.setGlobalConfig(gc);
復制代碼

 

Step3:
  配置數據源信息。用於指定 需要生成代碼的 數據倉庫、數據表。
  setUrl、setDriverName、setUsername、setPassword 均需修改。

復制代碼
// Step3:數據源配置(需要修改)
DataSourceConfig dsc = new DataSourceConfig();
// 配置數據庫 url 地址
dsc.setUrl("jdbc:mysql://localhost:3306/testMyBatisPlus?useUnicode=true&characterEncoding=utf8");
// dsc.setSchemaName("testMyBatisPlus"); // 可以直接在 url 中指定數據庫名
// 配置數據庫驅動
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
// 配置數據庫連接用戶名
dsc.setUsername("root");
// 配置數據庫連接密碼
dsc.setPassword("123456");
mpg.setDataSource(dsc);
復制代碼

 

Step4:
  配置包信息。
  setParent、setModuleName 均需修改。其余按需求修改.

復制代碼
// Step:4:包配置
PackageConfig pc = new PackageConfig();
// 配置父包名(需要修改)
pc.setParent("com.lyh.test");
// 配置模塊名(需要修改)
pc.setModuleName("test_mybatis_plus");
// 配置 entity 包名
pc.setEntity("entity");
// 配置 mapper 包名
pc.setMapper("mapper");
// 配置 service 包名
pc.setService("service");
// 配置 controller 包名
pc.setController("controller");
mpg.setPackageInfo(pc);
復制代碼

 

Step5:
  配置數據表映射信息。
  setInclude 需要修改,其余按實際開發修改。

復制代碼
// Step5:策略配置(數據庫表配置)
StrategyConfig strategy = new StrategyConfig();
// 指定表名(可以同時操作多個表,使用 , 隔開)(需要修改)
strategy.setInclude("test_mybatis_plus_user");
// 配置數據表與實體類名之間映射的策略
strategy.setNaming(NamingStrategy.underline_to_camel);
// 配置數據表的字段與實體類的屬性名之間映射的策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// 配置 lombok 模式
strategy.setEntityLombokModel(true);
// 配置 rest 風格的控制器(@RestController)
strategy.setRestControllerStyle(true);
// 配置駝峰轉連字符
strategy.setControllerMappingHyphenStyle(true);
// 配置表前綴,生成實體時去除表前綴
// 此處的表名為 test_mybatis_plus_user,模塊名為 test_mybatis_plus,去除前綴后剩下為 user。
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
復制代碼

 

Step6:
  執行代碼生成操作。

  此處不用修改。

// Step6:執行代碼生成操作
mpg.execute();

 

完整配置如下:

復制代碼
package com.lyh.test.test_mybatis_plus;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.jupiter.api.Test;

public class TestAutoGenerate {
    @Test
    public void autoGenerate() {
        // Step1:代碼生成器
        AutoGenerator mpg = new AutoGenerator();

        // Step2:全局配置
        GlobalConfig gc = new GlobalConfig();
        // 填寫代碼生成的目錄(需要修改)
        String projectPath = "E:\\myProject\\test\\test_mybatis_plus";
        // 拼接出代碼最終輸出的目錄
        gc.setOutputDir(projectPath + "/src/main/java");
        // 配置開發者信息(可選)(需要修改)
        gc.setAuthor("lyh");
        // 配置是否打開目錄,false 為不打開(可選)
        gc.setOpen(false);
        // 實體屬性 Swagger2 注解,添加 Swagger 依賴,開啟 Swagger2 模式(可選)
        //gc.setSwagger2(true);
        // 重新生成文件時是否覆蓋,false 表示不覆蓋(可選)
        gc.setFileOverride(false);
        // 配置主鍵生成策略,此處為 ASSIGN_ID(可選)
        gc.setIdType(IdType.ASSIGN_ID);
        // 配置日期類型,此處為 ONLY_DATE(可選)
        gc.setDateType(DateType.ONLY_DATE);
        // 默認生成的 service 會有 I 前綴
        gc.setServiceName("%sService");
        mpg.setGlobalConfig(gc);

        // Step3:數據源配置(需要修改)
        DataSourceConfig dsc = new DataSourceConfig();
        // 配置數據庫 url 地址
        dsc.setUrl("jdbc:mysql://localhost:3306/testMyBatisPlus?useUnicode=true&characterEncoding=utf8");
        // dsc.setSchemaName("testMyBatisPlus"); // 可以直接在 url 中指定數據庫名
        // 配置數據庫驅動
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        // 配置數據庫連接用戶名
        dsc.setUsername("root");
        // 配置數據庫連接密碼
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);

        // Step:4:包配置
        PackageConfig pc = new PackageConfig();
        // 配置父包名(需要修改)
        pc.setParent("com.lyh.test");
        // 配置模塊名(需要修改)
        pc.setModuleName("test_mybatis_plus");
        // 配置 entity 包名
        pc.setEntity("entity");
        // 配置 mapper 包名
        pc.setMapper("mapper");
        // 配置 service 包名
        pc.setService("service");
        // 配置 controller 包名
        pc.setController("controller");
        mpg.setPackageInfo(pc);

        // Step5:策略配置(數據庫表配置)
        StrategyConfig strategy = new StrategyConfig();
        // 指定表名(可以同時操作多個表,使用 , 隔開)(需要修改)
        strategy.setInclude("test_mybatis_plus_user");
        // 配置數據表與實體類名之間映射的策略
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 配置數據表的字段與實體類的屬性名之間映射的策略
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 配置 lombok 模式
        strategy.setEntityLombokModel(true);
        // 配置 rest 風格的控制器(@RestController)
        strategy.setRestControllerStyle(true);
        // 配置駝峰轉連字符
        strategy.setControllerMappingHyphenStyle(true);
        // 配置表前綴,生成實體時去除表前綴
        // 此處的表名為 test_mybatis_plus_user,模塊名為 test_mybatis_plus,去除前綴后剩下為 user。
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);

        // Step6:執行代碼生成操作
        mpg.execute();
    }
}
復制代碼

 

 

 

4、自動填充數據功能

(1)簡介
  添加、修改數據時,每次都會使用相同的方式進行填充。比如 數據的創建時間、修改時間等。
  Mybatis-plus 支持自動填充這些字段的數據。

  給之前的數據表新增兩個字段:創建時間、修改時間。

復制代碼
CREATE TABLE test_mybatis_plus_user
(
    id BIGINT 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 '郵箱',
    create_time timestamp NULL DEFAULT NULL COMMENT '創建時間',
    update_time timestamp NULL DEFAULT NULL COMMENT '最后修改時間', 
    PRIMARY KEY (id)
);
復制代碼

並使用 代碼生成器生成代碼。

 

 

 

(2)未使用自動填充時
  未使用 自動填充時,每次添加、修改數據都可以手動對其進行添加。

復制代碼
@SpringBootTest
class TestMybatisPlusApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    public void testUpdate() {
        User user = new User();
        user.setName("tom").setAge(20).setEmail("tom@163.com");
        // 手動添加數據
        user.setCreateTime(new Date()).setUpdateTime(new Date());
        if (userService.save(user)) {
            userService.list().forEach(System.out::println);
        } else {
            System.out.println("添加數據失敗");
        }
    }
}
復制代碼

 

 

 

(3)使用自動填充功能。
Step1:
  使用 @TableField 注解,標注需要進行填充的字段。

復制代碼
/**
 * 創建時間
 */
@TableField(fill = FieldFill.INSERT)
private Date createTime;

/**
 * 最后修改時間
 */
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
復制代碼

 

 

 

Step2:
  自定義一個類,實現 MetaObjectHandler 接口,並重寫方法。
  添加 @Component 注解,交給 Spring 去管理。

復制代碼
package com.lyh.test.test_mybatis_plus.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}
復制代碼

 

 

 

Step3:
  簡單測試一下。

復制代碼
@Test
public void testAutoFill() {
    User user = new User();
    user.setName("tom").setAge(20).setEmail("tom@163.com");
    if (userService.save(user)) {
        userService.list().forEach(System.out::println);
    } else {
        System.out.println("添加數據失敗");
    }
}
復制代碼

 

 

 

5、邏輯刪除

(1)簡介
  刪除數據,可以通過物理刪除,也可以通過邏輯刪除。
  物理刪除指的是直接將數據從數據庫中刪除,不保留。
  邏輯刪除指的是修改數據的某個字段,使其表示為已刪除狀態,而非刪除數據,保留該數據在數據庫中,但是查詢時不顯示該數據(查詢時過濾掉該數據)。

  給數據表增加一個字段:delete_flag,用於表示該數據是否被邏輯刪除。

復制代碼
CREATE TABLE test_mybatis_plus_user
(
    id BIGINT 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 '郵箱',
    create_time timestamp NULL DEFAULT NULL COMMENT '創建時間',
    update_time timestamp NULL DEFAULT NULL COMMENT '最后修改時間', 
    delete_flag tinyint(1) NULL DEFAULT NULL COMMENT '邏輯刪除(0 未刪除、1 刪除)',
    PRIMARY KEY (id)
);
復制代碼

 

(2)使用邏輯刪除。
  可以定義一個自動填充規則,初始值為 0。0 表示未刪除, 1 表示刪除。

復制代碼
/**
 * 邏輯刪除(0 未刪除、1 刪除)
 */
@TableLogic(value = "0", delval = "1")
@TableField(fill = FieldFill.INSERT)
private Integer deleteFlag;


@Override
public void insertFill(MetaObject metaObject) {
    this.strictInsertFill(metaObject, "deleteFlag", Integer.class, 0);
}
復制代碼

 

 

 

(3)簡單測試
  使用 mybatis-plus 封裝好的方法時,會自動添加邏輯刪除的功能。
  若是自定義的 sql 語句,需要手動添加邏輯。

復制代碼
@Test
public void testDelete() {
    if (userService.removeById(1258924257048547329L)) {
        System.out.println("刪除數據成功");
        userService.list().forEach(System.out::println);
    } else {
        System.out.println("刪除數據失敗");
    }
}
復制代碼

 

現有數據 

 

 

 

執行 testDelete 進行邏輯刪除。

 

 

 

 

 

 

若去除 TableLogic 注解,再執行 testDelete 時進行物理刪除,直接刪除這條數據。

 

 

 

 

 

 

6、分頁插件的使用

(1)簡介
  與 mybatis 的插件 pagehelper 用法類似。
  通過簡單的配置即可使用。

(2)使用
Step1:
  配置分頁插件。
  編寫一個 配置類,內部使用 @Bean 注解將 PaginationInterceptor 交給 Spring 容器管理。

復制代碼
package com.lyh.test.test_mybatis_plus.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自定義一個配置類,mapper 掃描也可在此寫上
 */
@Configuration
@MapperScan("com.lyh.test.test_mybatis_plus.mapper")
public class Myconfig {
    /**
     * 分頁插件
     * @return 分頁插件的實例
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}
復制代碼

 

 

 

Step2:
  編寫分頁代碼。
  直接 new 一個 Page 對象,對象需要傳遞兩個參數(當前頁,每頁顯示的條數)。
  調用 mybatis-plus 提供的分頁查詢方法,其會將 分頁查詢的數據封裝到 Page 對象中。

復制代碼
@Test
public void testPage() {
    // Step1:創建一個 Page 對象
    Page<User> page = new Page<>();
    // Page<User> page = new Page<>(2, 5);
    // Step2:調用 mybatis-plus 提供的分頁查詢方法
    userService.page(page, null);
    // Step3:獲取分頁數據
    System.out.println(page.getCurrent()); // 獲取當前頁
    System.out.println(page.getTotal()); // 獲取總記錄數
    System.out.println(page.getSize()); // 獲取每頁的條數
    System.out.println(page.getRecords()); // 獲取每頁數據的集合
    System.out.println(page.getPages()); // 獲取總頁數
    System.out.println(page.hasNext()); // 是否存在下一頁
    System.out.println(page.hasPrevious()); // 是否存在上一頁
}
復制代碼

 

 

 

7、樂觀鎖的實現

(1)首先認識一下 讀問題、寫問題?
  操作數據庫數據時,遇到的最基本問題就是 讀問題與寫問題。
  讀問題 指的是從數據庫中讀取數據時遇到的問題,比如:臟讀、幻讀、不可重復讀。

【臟讀、幻讀、不可重復讀 參考地址:】
    https://www.cnblogs.com/l-y-h/p/12458777.html#_label0_3

  寫問題 指的是數據寫入數據庫時遇到的問題,比如:丟失更新(多個線程同時對某條數據更新,無論執行順序如何,都會丟失其他線程更新的數據)

(2)如何解決寫問題?
  樂觀鎖、悲觀鎖就是為了解決 寫問題而存在的。
    樂觀鎖:總是假設最好的情況,每次讀取數據時認為數據不會被修改(即不加鎖),當進行更新操作時,會判斷這條數據是否被修改,未被修改,則進行更新操作。若被修改,則數據更新失敗,可以對數據進行重試(重新嘗試修改數據)。
    悲觀鎖:總是假設最壞的情況,每次讀取數據時認為數據會被修改(即加鎖),當進行更新操作時,直接更新數據,結束操作后釋放鎖(此處才可以被其他線程讀取)。

(3)樂觀鎖、悲觀鎖使用場景?
  樂觀鎖一般用於讀比較多的場合,盡量減少加鎖的開銷。
  悲觀鎖一般用於寫比較多的場合,盡量減少 類似 樂觀鎖重試更新引起的性能開銷。

(4)樂觀鎖兩種實現方式
方式一:通過版本號機制實現。
  在數據表中增加一個 version 字段。
  取數據時,獲取該字段,更新時以該字段為條件進行處理(即set version = newVersion where version = oldVersion),若 version 相同,則更新成功(給新 version 賦一個值,一般加 1)。若 version 不同,則更新失敗,可以重新嘗試更新操作。

方式二:通過 CAS 算法實現。
  CAS 為 Compare And Swap 的縮寫,即比較交換,是一種無鎖算法(即在不加鎖的情況實現多線程之間的變量同步)。
  CAS 操作包含三個操作數 —— 內存值(V)、預期原值(A)和新值(B)。如果內存地址里面的值 V 和 A 的值是一樣的,那么就將內存里面的值更新成B。若 V 與 A 不一致,則不執行任何操作(可以通過自旋操作,不斷嘗試修改數據直至成功修改)。即 V == A ? V = B : V = V。
  CAS 可能導致 ABA 問題(兩次讀取數據時值相同,但不確定值是否被修改過),比如兩個線程操作同一個變量,線程 A、線程B 初始讀取數據均為 A,后來 線程B 將數據修改為 B,然后又修改為 A,此時線程 A 再次讀取到的數據依舊是 A,雖然值相同但是中間被修改過,這就是 ABA 問題。可以加一個額外的標志位 C,用於表示數據是否被修改。當標志位 C 與預期標志位相同、且 V == A 時,則更新值 B。

(5)mybatis-plus 實現樂觀鎖(通過 version 機制)
實現思路:
  Step1:取出記錄時,獲取當前version
  Step2:更新時,帶上這個version
  Step3:執行更新時, set version = newVersion where version = oldVersion
  Step4:如果version不對,就更新失敗

(6)mybatis-plus 代碼實現樂觀鎖
Step1:
  配置樂觀鎖插件。
  編寫一個配置類(可以與上例的分頁插件共用一個配置類),將 OptimisticLockerInterceptor 通過 @Bean 交給 Spring 管理。

復制代碼
/**
 * 樂觀鎖插件
 * @return 樂觀鎖插件的實例
 */
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
}
復制代碼

 

 

 

Step2:
  定義一個數據庫字段 version。

復制代碼
CREATE TABLE test_mybatis_plus_user
(
    id BIGINT 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 '郵箱',
    create_time timestamp NULL DEFAULT NULL COMMENT '創建時間',
    update_time timestamp NULL DEFAULT NULL COMMENT '最后修改時間', 
    delete_flag tinyint(1) NULL DEFAULT NULL COMMENT '邏輯刪除(0 未刪除、1 刪除)',
    version int NULL DEFAULT NULL COMMENT '版本號(用於樂觀鎖, 默認為 1)',
    PRIMARY KEY (id)
);
復制代碼

 

Step3:
  使用 @Version 注解標注對應的實體類。
  可以通過 @TableField 進行數據自動填充。

復制代碼
/**
 * 版本號(用於樂觀鎖, 默認為 1)
 */
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;


@Override
public void insertFill(MetaObject metaObject) {
    this.strictInsertFill(metaObject, "version", Integer.class, 1);
}
復制代碼

 

 

 

Step4:
  簡單測試一下,可以看一下 控制台 打印的 sql 語句。

復制代碼
@Test
public void testVersion() {
    User user = new User();
    user.setName("tom").setAge(20).setEmail("tom@163.com");
    userService.save(user);
    userService.list().forEach(System.out::println);
    user.setName("jarry");
    userService.update(user, null);
    userService.list().forEach(System.out::println);
}
復制代碼

 

 

 

三、Mybatis-Plus CRUD 操作簡單了解一下

1、Mapper 接口方法(CRUD)簡單了解一下

(1)簡介
  使用代碼生成器生成的 mapper 接口中,其繼承了 BaseMapper 接口。
  而 BaseMapper 接口中封裝了一系列 CRUD 常用操作,可以直接使用,而不用自定義 xml 與 sql 語句進行 CRUD 操作(當然根據實際開發需要,自定義 sql 還是有必要的)。

此處簡單介紹一下 BaseMapper 接口中的常用方法。

 

 

 

(2)方法介紹
  混個眼熟,用多了就記得了。

復制代碼
【添加數據:(增)】
    int insert(T entity);              // 插入一條記錄
注:
    T         表示任意實體類型
    entity    表示實體對象

【刪除數據:(刪)】
    int deleteById(Serializable id);    // 根據主鍵 ID 刪除
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);  // 根據 map 定義字段的條件刪除
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 根據實體類定義的 條件刪除對象
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 進行批量刪除
注:
    id        表示 主鍵 ID
    columnMap 表示表字段的 map 對象
    wrapper   表示實體對象封裝操作類,可以為 null。
    idList    表示 主鍵 ID 集合(列表、數組),不能為 null 或 empty

【修改數據:(改)】
    int updateById(@Param(Constants.ENTITY) T entity); // 根據 ID 修改實體對象。
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper); // 根據 updateWrapper 條件修改實體對象
注:
    update 中的 entity 為 set 條件,可以為 null。
    updateWrapper 表示實體對象封裝操作類(可以為 null,里面的 entity 用於生成 where 語句)

【查詢數據:(查)】
    T selectById(Serializable id); // 根據 主鍵 ID 查詢數據
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 進行批量查詢
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根據表字段條件查詢
    T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據實體類封裝對象 查詢一條記錄
    Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢記錄的總條數
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢所有記錄(返回 entity 集合)
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢所有記錄(返回 map 集合)
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢所有記錄(但只保存第一個字段的值)
    <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢所有記錄(返回 entity 集合),分頁
    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢所有記錄(返回 map 集合),分頁
注:
    queryWrapper 表示實體對象封裝操作類(可以為 null)
    page 表示分頁查詢條件
復制代碼


2、Service 接口方法(CRUD)簡單了解一下

(1)簡介
  使用 代碼生成器 生成的 service 接口中,其繼承了 IService 接口。
  IService 內部進一步封裝了 BaseMapper 接口的方法(當然也提供了更詳細的方法)。
  使用時,可以通過 生成的 mapper 類進行 CRUD 操作,也可以通過 生成的 service 的實現類進行 CRUD 操作。(當然,自定義代碼執行也可)
此處簡單介紹一下 IService 中封裝的常用方法。

 

 

 

 

(2)方法介紹
  混個眼熟,用多了就記得了。
  內部封裝了 BaseMapper 的方法,也提供了新的方法。
比如:
  添加了 批量更新 方法、更新或修改方法等。
  對 查詢方法 做了細化,使用 get 命名的方法查詢一條數據,使用 list 命名的方法查詢多條數據等。
  增加了鏈式調用的方法。

復制代碼
【添加數據:(增)】
    default boolean save(T entity); // 調用 BaseMapper 的 insert 方法,用於添加一條數據。
    boolean saveBatch(Collection<T> entityList, int batchSize); // 批量插入數據
注:
    entityList 表示實體對象集合 
    batchSize 表示一次批量插入的數據量,默認為 1000

【添加或修改數據:(增或改)】
    boolean saveOrUpdate(T entity); // id 若存在,則修改, id 不存在則新增數據
   default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper); // 先根據條件嘗試更新,然后再執行 saveOrUpdate 操作
   boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize); // 批量插入並修改數據 

【刪除數據:(刪)】
    default boolean removeById(Serializable id); // 調用 BaseMapper 的 deleteById 方法,根據 id 刪除數據。
    default boolean removeByMap(Map<String, Object> columnMap); // 調用 BaseMapper 的 deleteByMap 方法,根據 map 定義字段的條件刪除
    default boolean remove(Wrapper<T> queryWrapper); // 調用 BaseMapper 的 delete 方法,根據實體類定義的 條件刪除對象。
    default boolean removeByIds(Collection<? extends Serializable> idList); // 用 BaseMapper 的 deleteBatchIds 方法, 進行批量刪除。
    
【修改數據:(改)】
    default boolean updateById(T entity); // 調用 BaseMapper 的 updateById 方法,根據 ID 選擇修改。
    default boolean update(T entity, Wrapper<T> updateWrapper); // 調用 BaseMapper 的 update 方法,根據 updateWrapper 條件修改實體對象。
    boolean updateBatchById(Collection<T> entityList, int batchSize); // 批量更新數據

【查找數據:(查)】
    default T getById(Serializable id); // 調用 BaseMapper 的 selectById 方法,根據 主鍵 ID 返回數據。
    default List<T> listByIds(Collection<? extends Serializable> idList); // 調用 BaseMapper 的 selectBatchIds 方法,批量查詢數據。
    default List<T> listByMap(Map<String, Object> columnMap); // 調用 BaseMapper 的 selectByMap 方法,根據表字段條件查詢
    default T getOne(Wrapper<T> queryWrapper); // 返回一條記錄(實體類保存)。
    Map<String, Object> getMap(Wrapper<T> queryWrapper); // 返回一條記錄(map 保存)。
    default int count(Wrapper<T> queryWrapper); // 根據條件返回 記錄數。
    default List<T> list(); // 返回所有數據。
    default List<T> list(Wrapper<T> queryWrapper); // 調用 BaseMapper 的 selectList 方法,查詢所有記錄(返回 entity 集合)。
    default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper); // 調用 BaseMapper 的 selectMaps 方法,查詢所有記錄(返回 map 集合)。
    default List<Object> listObjs(); // 返回全部記錄,但只返回第一個字段的值。
    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper); // 調用 BaseMapper 的 selectPage 方法,分頁查詢
    default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper); // 調用 BaseMapper 的 selectMapsPage 方法,分頁查詢
注:
    get 用於返回一條記錄。
    list 用於返回多條記錄。
    count 用於返回記錄總數。
    page 用於分頁查詢。
    
【鏈式調用:】
    default QueryChainWrapper<T> query(); // 普通鏈式查詢
    default LambdaQueryChainWrapper<T> lambdaQuery(); // 支持 Lambda 表達式的修改
    default UpdateChainWrapper<T> update(); // 普通鏈式修改
    default LambdaUpdateChainWrapper<T> lambdaUpdate(); // 支持 Lambda 表達式的修改
注:
    query 表示查詢
    update 表示修改
    Lambda 表示內部支持 Lambda 寫法。
形如:
    query().eq("column", value).one();
    lambdaQuery().eq(Entity::getId, value).list();
    update().eq("column", value).remove();
    lambdaUpdate().eq(Entity::getId, value).update(entity);
復制代碼

 

3、條件構造器(Wrapper,定義 where 條件)

(1)簡介
  上面介紹的 接口方法的參數中,會出現各種 wrapper,比如 queryWrapper、updateWrapper 等。wrapper 的作用就是用於定義各種各樣的查詢條件(where)。

復制代碼
Wrapper  條件構造抽象類
    -- AbstractWrapper 查詢條件封裝,用於生成 sql 中的 where 語句。
        -- QueryWrapper Entity 對象封裝操作類,用於查詢。
        -- UpdateWrapper Update 條件封裝操作類,用於更新。
    -- AbstractLambdaWrapper 使用 Lambda 表達式封裝 wrapper
        -- LambdaQueryWrapper 使用 Lambda 語法封裝條件,用於查詢。
        -- LambdaUpdateWrapper 使用 Lambda 語法封裝條件,用於更新。
復制代碼

 

 

(2)常用條件
  參考源碼以及官方文檔總結的,還是一句話,混個眼熟,多用用就熟練了。

復制代碼
【通用條件:】
【比較大小: ( =, <>, >, >=, <, <= )】
    eq(R column, Object val); // 等價於 =,例: eq("name", "老王") ---> name = '老王'
    ne(R column, Object val); // 等價於 <>,例: ne("name", "老王") ---> name <> '老王'
    gt(R column, Object val); // 等價於 >,例: gt("name", "老王") ---> name > '老王'
    ge(R column, Object val); // 等價於 >=,例: ge("name", "老王") ---> name >= '老王'
    lt(R column, Object val); // 等價於 <,例: lt("name", "老王") ---> name < '老王'
    le(R column, Object val); // 等價於 <=,例: le("name", "老王") ---> name <= '老王'
    
【范圍:(between、not between、in、not in)】
   between(R column, Object val1, Object val2); // 等價於 between a and b, 例: between("age", 18, 30) ---> age between 18 and 30
   notBetween(R column, Object val1, Object val2); // 等價於 not between a and b, 例: notBetween("age", 18, 30) ---> age not between 18 and 30
   in(R column, Object... values); // 等價於 字段 IN (v0, v1, ...),例: in("age",{1,2,3}) ---> age in (1,2,3)
   notIn(R column, Object... values); // 等價於 字段 NOT IN (v0, v1, ...), 例: notIn("age",{1,2,3}) ---> age not in (1,2,3)
   inSql(R column, Object... values); // 等價於 字段 IN (sql 語句), 例: inSql("id", "select id from table where id < 3") ---> id in (select id from table where id < 3)
   notInSql(R column, Object... values); // 等價於 字段 NOT IN (sql 語句)
   
【模糊匹配:(like)】
    like(R column, Object val); // 等價於 LIKE '%值%',例: like("name", "王") ---> name like '%王%'
    notLike(R column, Object val); // 等價於 NOT LIKE '%值%',例: notLike("name", "王") ---> name not like '%王%'
    likeLeft(R column, Object val); // 等價於 LIKE '%值',例: likeLeft("name", "王") ---> name like '%王'
    likeRight(R column, Object val); // 等價於 LIKE '值%',例: likeRight("name", "王") ---> name like '王%'
    
【空值比較:(isNull、isNotNull)】
    isNull(R column); // 等價於 IS NULL,例: isNull("name") ---> name is null
    isNotNull(R column); // 等價於 IS NOT NULL,例: isNotNull("name") ---> name is not null

【分組、排序:(group、having、order)】
    groupBy(R... columns); // 等價於 GROUP BY 字段, ..., 例: groupBy("id", "name") ---> group by id,name
    orderByAsc(R... columns); // 等價於 ORDER BY 字段, ... ASC, 例: orderByAsc("id", "name") ---> order by id ASC,name ASC
    orderByDesc(R... columns); // 等價於 ORDER BY 字段, ... DESC, 例: orderByDesc("id", "name") ---> order by id DESC,name DESC
    having(String sqlHaving, Object... params); // 等價於 HAVING ( sql語句 ), 例: having("sum(age) > {0}", 11) ---> having sum(age) > 11

【拼接、嵌套 sql:(or、and、nested、apply)】
   or(); // 等價於 a or b, 例:eq("id",1).or().eq("name","老王") ---> id = 1 or name = '老王'
   or(Consumer<Param> consumer); // 等價於 or(a or/and b),or 嵌套。例: or(i -> i.eq("name", "李白").ne("status", "活着")) ---> or (name = '李白' and status <> '活着')
   and(Consumer<Param> consumer); // 等價於 and(a or/and b),and 嵌套。例: and(i -> i.eq("name", "李白").ne("status", "活着")) ---> and (name = '李白' and status <> '活着')
   nested(Consumer<Param> consumer); // 等價於 (a or/and b),普通嵌套。例: nested(i -> i.eq("name", "李白").ne("status", "活着")) ---> (name = '李白' and status <> '活着')
   apply(String applySql, Object... params); // 拼接sql(若不使用 params 參數,可能存在 sql 注入),例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08") ---> date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
   last(String lastSql); // 無視優化規則直接拼接到 sql 的最后,可能存若在 sql 注入。
   exists(String existsSql); // 拼接 exists 語句。例: exists("select id from table where age = 1") ---> exists (select id from table where age = 1)
   
【QueryWrapper 條件:】
    select(String... sqlSelect); // 用於定義需要返回的字段。例: select("id", "name", "age") ---> select id, name, age
    select(Predicate<TableFieldInfo> predicate); // Lambda 表達式,過濾需要的字段。
    lambda(); // 返回一個 LambdaQueryWrapper
    
【UpdateWrapper 條件:】
    set(String column, Object val); // 用於設置 set 字段值。例: set("name", null) ---> set name = null
    etSql(String sql); // 用於設置 set 字段值。例: setSql("name = '老李頭'") ---> set name = '老李頭'
    lambda(); // 返回一個 LambdaUpdateWrapper 
復制代碼

 

(3)簡單使用,測試一下 QueryWrapper 

復制代碼
@Test
public void testQueryWrapper() {
    // Step1:創建一個 QueryWrapper 對象
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();

    // Step2: 構造查詢條件
    queryWrapper
            .select("id", "name", "age")
            .eq("age", 20)
            .like("name", "j");

    // Step3:執行查詢
    userService
            .list(queryWrapper)
            .forEach(System.out::println);
}
復制代碼

當前數據庫數據如下:

 

 

執行測試方法輸出如下:

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM