springboot整合mybatis-plus看這篇文章就足夠了

目錄

 

什麼是mybatis-plus

特點

框架結構

引入依賴

代碼生成器

 使用mybatis自帶的方法進行增刪改查

條件構造

分頁插件

 邏輯刪除

通用枚舉類

自動填充功能

樂觀鎖

 總結


什麼是mybatis-plus

MyBatis-Plus(簡稱 MP)是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,爲簡化開發、提高效率而生。

特點

  • 無侵入:只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑
  • 損耗小:啓動即會自動注入基本 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 操作智能分析阻斷,也可自定義攔截規則,預防誤操作

框架結構

引入依賴

springboot

        maven:

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

      gradle:

compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.3.1.tmp'

springmvc

      maven:

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

            gradle:

compile group: 'com.baomidou', name: 'mybatis-plus', version: '3.3.1.tmp'

 

代碼生成器

代碼生成器可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率。

還沒有試用代碼生成器之前的項目結構:

數據庫設置

1.新建數據庫:test

2.新建數據表:users,數據表結構如下:

代碼開發

引入依賴,這裏我一maven爲例,gradle可以搜索相關依賴:

<?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.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ymy</groupId>
    <artifactId>springboot-mybatis-plus</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-mybatis-plus</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mysql驅動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>


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



        <!--簡化bean代碼-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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


        <!--代碼生成器 生產環境中可以不用引入-->
        <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>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </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>

 

CodeGenerator代碼生成器類:

package com.ymy.config;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;

public class CodeGenerator {
    

    public static void main(String[] args) {

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

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir("F:\\demo\\springboot-mybatis-plus\\src\\main\\java");
        gc.setAuthor("yaomaoyang");
        gc.setOpen(false);
        // gc.setSwagger2(true); 實體屬性 Swagger2 註解
        mpg.setGlobalConfig(gc);

        // 數據源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://127.0.0.1:33306/test?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        //pc.setModuleName(scanner("模塊名"));
        pc.setParent("com.ymy");
        mpg.setPackageInfo(pc);

        // 自定義配置
//        InjectionConfig cfg = new InjectionConfig() {
//            @Override
//            public void initMap() {
//                Map<String, Object> map = new HashMap<>();
//                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
//                this.setMap(map);
//            }
//        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出文件名 , 如果你 Entity 設置了前後綴、此處注意 xml 的名稱會跟着發生變化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
//        cfg.setFileCreate(new IFileCreate() {
//            @Override
//            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
//                // 判斷自定義文件夾是否需要創建
//                checkDir("調用默認方法創建的目錄");
//                return false;
//            }
//        });
//        cfg.setFileOutConfigList(focList);
//        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定義輸出模板
        //指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //strategy.setSuperEntityClass("你自己的父類實體,沒有就不用設置!");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父類
       // strategy.setSuperControllerClass("你自己的父類控制器,沒有就不用設置!");
        // 寫於父類中的公共字段
        //strategy.setSuperEntityColumns("id");
        strategy.setInclude("users");//表名
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

運行main方法:

10:01:54.232 [main] DEBUG com.baomidou.mybatisplus.generator.AutoGenerator - ==========================準備生成文件...==========================
10:01:54.719 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\entity]
10:01:54.719 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\controller]
10:01:54.720 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service]
10:01:54.721 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\mapper]
10:01:54.721 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service\impl]
10:01:54.992 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/entity.java.ftl;  文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\entity\Users.java
10:01:55.010 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/mapper.java.ftl;  文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\mapper\UsersMapper.java
10:01:55.016 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/service.java.ftl;  文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service\IUsersService.java
10:01:55.026 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/serviceImpl.java.ftl;  文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service\impl\UsersServiceImpl.java
10:01:55.034 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/controller.java.ftl;  文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\controller\UsersController.java
10:01:55.034 [main] DEBUG com.baomidou.mybatisplus.generator.AutoGenerator - ==========================文件生成完成!!!==========================

Process finished with exit code 0

 看到上面的標誌表示代碼已經生成完畢,項目目錄結構如下:

由於後續的開發我都將採用註解的形式,所以我這裏並沒有生成xml文件,如果你想生成xml文件,可以設置這個文件:

 templateConfig.setXml(null);

 使用mybatis自帶的方法進行增刪改查

數據源配置

打開application.yml配置文件

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:33306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true&allowMultiQueries=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root

注意: driver-class-name 這個參數值回會根據mysql驅動的版本變動而變動。

 <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

 如果mysql-connector-java版本大於6.0,那麼使用:com.mysql.cj.jdbc.Driver,否則:com.mysql.jdbc.Driver,這點需要特別注意。

1.新增

由於新增操作時針對對象操作,所以我對Users,請看代碼:

如果你引入了@Builder這種建造者模式,那麼Users對象將不能使用new進行實例化,如果想支持建造者和new兩種創建實例的方式,那麼需要引入兩個註解:@NoArgsConstructor(無參構造)、@AllArgsConstructor(有參構造,這個看情況)

這裏還需要注意一點,u_sex、deleted兩個字段類型爲tinyint(1),使用代碼生成器生成的實體類Users中與他們對應的類型是:boolean,所以這裏最好將他們修改爲Integer。

新增測試類:MybatisPlusTest.java

package com.ymy;

import com.ymy.entity.Users;
import com.ymy.service.IUsersService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDateTime;

@SpringBootTest
@Slf4j
public class MybatisPlusTest {

    @Autowired
    private IUsersService usersService;

    /**
     * 插入操作(使用mybatis-plus自帶方法)
     */
    @Test
    public  void add(){
        //初始化實例並賦值
        Users user = Users.builder().uName("張三").uMail("[email protected]")
                .uPassword("123456").uPhone("15000000000")
                .createTime(LocalDateTime.now()).uSex(1).deleted(0).build();
        //插入
        boolean save = usersService.save(user);

        log.info("保存返回的結果:{}",save);
    }
}

 

運行add方法:

數據庫:

 查詢

由於查詢會涉及到列表,剛剛直插入了一條,這裏我在數據庫手動添加了3條數據:

查詢代碼:

/**
     * 查詢操作(使用mybatis-plus自帶方法)
     */
    @Test
    public  void select(){

        //查詢整張表
        List<Users> list = usersService.list();
        log.info("查詢整張表的數據結果:{}",list);
        //通過用戶id查詢用戶
        Users user = usersService.getById(1);
        log.info("通過用戶id查詢數據結果:{}",user);

    }

查詢結果:

2020-03-10 10:54:39.790  INFO 56092 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-10 10:54:40.011  INFO 56092 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-03-10 10:54:40.059  INFO 56092 --- [           main] com.ymy.MybatisPlusTest                  : 查詢整張表的數據結果:[Users(id=1, uName=張三, uPassword=123456, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T02:44:15, deleted=0), Users(id=2, uName=李四, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T02:44:15, deleted=0), Users(id=3, uName=王五, uPassword=123456, [email protected], uPhone=15222222222, uSex=1, createTime=2020-03-10T10:48:07, deleted=0), Users(id=4, uName=趙六, uPassword=123456, [email protected], uPhone=15333333333, uSex=1, createTime=2020-03-10T10:48:34, deleted=0)]
2020-03-10 10:54:40.067  INFO 56092 --- [           main] com.ymy.MybatisPlusTest                  : 通過用戶id查詢數據結果:Users(id=1, uName=張三, uPassword=123456, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T02:44:15, deleted=0)

修改操作

/**
     * 修改操作(使用mybatis-plus自帶方法)
     */
    @Test
    public  void update(){

        Users user = Users.builder().id(1).uName("張三").uMail("[email protected]")
                .uPassword("123123").uPhone("15000000000")
                .createTime(LocalDateTime.now()).uSex(1).deleted(0).build();
        //通過id修改數據
        boolean flag = usersService.updateById(user);

        log.info("根據id修改數據返回的結果:{}",flag);

    }

刪除操作

/**
     * 刪除操作(使用mybatis-plus自帶方法)
     */
    @Test
    public  void remove(){
        //通過id刪除記錄
        boolean flag = usersService.removeById(4);
        log.info("根據id修改數據返回的結果:{}",flag);

    }

 

            

 

條件構造

條件構造可以讓sql變得更加的靈活,不用寫代碼也可以操作多條件的sql語句,下面我們一起來看看條件構造的使用方式:

AbstractWrapper

QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父類用於生成 sql 的 where 條件, entity 屬性也用於生成 sql 的 where 條件注意: entity 生成的 where 條件與 使用各個 api 生成的 where 條件沒有任何關聯行爲

請看代碼實例:

 /**
     * 條件構造
     */
    @Test
    public  void conditionalConstruct(){
        //通過用戶名和性別查詢用戶信息
        QueryWrapper<Users> usersQueryWrapper = new QueryWrapper<Users>()
                .eq("u_name","張三")//姓名
                .eq("u_sex",1);//性別
        List<Users> users = usersService.list(usersQueryWrapper);
        log.info("根據用戶名和性別查詢的結果:{}",users);


        //將用戶名爲張三並且性別爲男的用戶的密碼設置爲999999
        UpdateWrapper<Users> updateWrapper = new UpdateWrapper<Users>()
                .eq("u_name","張三")//姓名
                .eq("u_sex",1)
                .set("u_password","999999");//將密碼設置爲999999
        boolean update = usersService.update(updateWrapper);
        log.info("將用戶名爲張三並且性別爲男的用戶的密碼設置爲999999的返回結果:{}",update);


        //通過對象類的實行修改
        UpdateWrapper<Users> updateEntity = new UpdateWrapper<Users>()
                .eq("u_name","張三")//姓名
                .eq("u_sex",1);
        Users users1 = new Users();
        users1.setUPassword("999999");
        boolean update1 = usersService.update(users1, updateEntity);
        log.info("通過對象類的實行修改返回的結果:{}",update1);


        //刪除用戶名爲王五並且性別爲1的用戶

        UpdateWrapper<Users> deleteUser = new UpdateWrapper<Users>()
                .eq("u_name","王五")//姓名
                .eq("u_sex",1);
        boolean remove = usersService.remove(deleteUser);
        log.info("刪除用戶名爲王五並且性別爲1的用戶返回結果:{}",remove);


        //lambda表達式查詢     查詢用戶名爲zhangsan的用戶
        LambdaQueryWrapper<Users> lambdaWrapper = new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三");
        List<Users> lambdaUsers = usersService.list(lambdaWrapper);
        log.info("lambda表達式查詢     查詢用戶名爲zhangsan的用戶返回結果:{}",lambdaUsers);


    }
2020-03-10 11:25:36.666  INFO 78972 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-10 11:25:36.901  INFO 78972 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-03-10 11:25:36.945  INFO 78972 --- [           main] com.ymy.MybatisPlusTest                  : 根據用戶名和性別查詢的結果:[Users(id=1, uName=張三, uPassword=123123, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T02:58:19, deleted=0)]
2020-03-10 11:25:36.966  INFO 78972 --- [           main] com.ymy.MybatisPlusTest                  : 將用戶名爲張三並且性別爲男的用戶的密碼設置爲999999的返回結果:true
2020-03-10 11:25:36.982  INFO 78972 --- [           main] com.ymy.MybatisPlusTest                  : 通過對象類的實行修改返回的結果:true
2020-03-10 11:25:36.998  INFO 78972 --- [           main] com.ymy.MybatisPlusTest                  : 刪除用戶名爲王五並且性別爲1的用戶返回結果:true
2020-03-10 11:25:37.019  INFO 78972 --- [           main] com.ymy.MybatisPlusTest                  : lambda表達式查詢     查詢用戶名爲zhangsan的用戶返回結果:[Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T03:25:39, deleted=0)]

以上我只寫了一個和條件構造一起使用的條件:eq,還有很多,詳情請參https://mp.baomidou.com/guide/wrapper.html#abstractwrapper

分頁插件

mybatis-plus使用分頁不需要像mybatis一樣添加依賴,因爲mybatis-plus已經提供了分頁插件,直接拿來使用即可。

1.引入依賴:

package com.ymy.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {


    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 設置請求的頁面大於最大頁後操作, true調回到首頁,false 繼續請求  默認false
        // paginationInterceptor.setOverflow(false);
        // 設置最大單頁限制數量,默認 500 條,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 開啓 count 的 join 優化,只針對部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

打工告成,接下來就可以使用分頁了。

/**
     * 分頁測試
     */
    @Test
    public void paginationTest(){

        //分頁查詢
        Page<Users> page = new Page<Users>(1,5);// 1:當前頁    5:每頁展示多少行
        IPage<Users> usersPage = usersService.page(page);
        log.info("總數:{} 條",usersPage.getTotal());
        log.info("通過分頁查詢返回的結果條數:{}",usersPage.getRecords().size());
        log.info("通過分頁查詢返回數據:{}",usersPage);
        for(Users user: usersPage.getRecords()){
            log.info("記錄:{}",user);
        }

    }
2020-03-10 11:54:23.113  INFO 83360 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-10 11:54:23.356  INFO 83360 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-03-10 11:54:23.463  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 總數:6 條
2020-03-10 11:54:23.463  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 通過分頁查詢返回的結果條數:5
2020-03-10 11:54:23.463  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 通過分頁查詢返回數據:com.baomidou.mybatisplus.extension.plugins.pagination.Page@7da34b26
2020-03-10 11:54:23.463  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T03:25:39, deleted=0)
2020-03-10 11:54:23.463  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=2, uName=李四, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T02:44:15, deleted=0)
2020-03-10 11:54:23.463  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=5, uName=王五, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T03:41:12, deleted=0)
2020-03-10 11:54:23.464  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=6, uName=趙六, uPassword=123456, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:13, deleted=0)
2020-03-10 11:54:23.464  INFO 83360 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=7, uName=王麻子, uPassword=123123, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:31, deleted=0)

將當前頁換成2

2020-03-10 12:01:24.239  INFO 82440 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-10 12:01:24.467  INFO 82440 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-03-10 12:01:24.571  INFO 82440 --- [           main] com.ymy.MybatisPlusTest                  : 總數:6 條
2020-03-10 12:01:24.572  INFO 82440 --- [           main] com.ymy.MybatisPlusTest                  : 通過分頁查詢返回的結果條數:1
2020-03-10 12:01:24.572  INFO 82440 --- [           main] com.ymy.MybatisPlusTest                  : 通過分頁查詢返回數據:com.baomidou.mybatisplus.extension.plugins.pagination.Page@55fee662
2020-03-10 12:01:24.572  INFO 82440 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=8, uName=令狐沖, uPassword=110110, [email protected], uPhone=15888888888, uSex=0, createTime=2020-03-10T03:41:52, deleted=0)

上面的查詢沒有嵌套條件,我們也可以結合查詢條件一起使用,mybatis-plus也給出了對應的方法,請看源碼:

/**
     * 翻頁查詢
     *
     * @param page         翻頁對象
     * @param queryWrapper 實體對象封裝操作類 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
     */
    default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
        return getBaseMapper().selectPage(page, queryWrapper);
    }

分頁結合條件構造一起使用,由於前面已經講解過條件構造了,這裏就不做代碼展示了,你可能還有疑問,那就是使用mybatis-plus自帶的方法可以使用這個分頁插件,如果是我們自己手動寫的sql,也能支持分頁嗎?,當然可以,請看代碼:

/**
     * 分頁測試(手動編寫sql)
     */
    @Test
    public void paginationSqlTest(){

        //分頁查詢
        Page<Users> page = new Page<Users>(1,5);// 1:當前頁    5:每頁展示多少行
        IPage<Users> usersPage = usersService.selectList(page);
        log.info("總數:{} 條",usersPage.getTotal());
        log.info("通過分頁查詢返回的結果條數:{}",usersPage.getRecords().size());
        log.info("通過分頁查詢返回數據:{}",usersPage);
        for(Users user: usersPage.getRecords()){
            log.info("記錄:{}",user);
        }
    }




public interface IUsersService extends IService<Users> {

    /**
     * 通過手動編寫sql查詢分頁
     * @param page
     * @return
     */
    IPage<Users> selectList(Page<Users> page);
}


@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements IUsersService {


    @Autowired
    private UsersMapper usersMapper;


    @Override
    public IPage<Users> selectList(Page<Users> page) {
        return usersMapper.selectListBySql(page);
    }
}


@Mapper
public interface UsersMapper extends BaseMapper<Users> {

    @Select("select * from users ")
    IPage<Users> selectListBySql(Page<Users> page);
}

 

2020-03-10 12:08:23.945  INFO 85452 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-10 12:08:24.173  INFO 85452 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-03-10 12:08:24.282  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 總數:6 條
2020-03-10 12:08:24.282  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 通過分頁查詢返回的結果條數:5
2020-03-10 12:08:24.283  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 通過分頁查詢返回數據:com.baomidou.mybatisplus.extension.plugins.pagination.Page@73a8e994
2020-03-10 12:08:24.283  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T03:25:39, deleted=0)
2020-03-10 12:08:24.283  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=2, uName=李四, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T02:44:15, deleted=0)
2020-03-10 12:08:24.283  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=5, uName=王五, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T03:41:12, deleted=0)
2020-03-10 12:08:24.283  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=6, uName=趙六, uPassword=123456, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:13, deleted=0)
2020-03-10 12:08:24.283  INFO 85452 --- [           main] com.ymy.MybatisPlusTest                  : 記錄:Users(id=7, uName=王麻子, uPassword=123123, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:31, deleted=0)

 可以看出,手動編寫的sql,同樣是支持分頁的,查詢也很簡單,只需要將Page對象傳遞到Mapper即可,我們一起來看看返回的對象IPage到底是一個什麼東西。

public interface IPage<T> extends Serializable {

    /**
     * 降序字段數組
     *
     * @return order by desc 的字段數組
     * @see #orders()
     */
    @Deprecated
    default String[] descs() {
        return null;
    }

    /**
     * 升序字段數組
     *
     * @return order by asc 的字段數組
     * @see #orders()
     */
    @Deprecated
    default String[] ascs() {
        return null;
    }

    /**
     * 獲取排序信息,排序的字段和正反序
     *
     * @return 排序信息
     */
    List<OrderItem> orders();

    /**
     * KEY/VALUE 條件
     *
     * @return ignore
     */
    default Map<Object, Object> condition() {
        return null;
    }

    /**
     * 自動優化 COUNT SQL【 默認:true 】
     *
     * @return true 是 / false 否
     */
    default boolean optimizeCountSql() {
        return true;
    }

    /**
     * 進行 count 查詢 【 默認: true 】
     *
     * @return true 是 / false 否
     */
    default boolean isSearchCount() {
        return true;
    }

    /**
     * 計算當前分頁偏移量
     */
    default long offset() {
        return getCurrent() > 0 ? (getCurrent() - 1) * getSize() : 0;
    }

    /**
     * 當前分頁總頁數
     */
    default long getPages() {
        if (getSize() == 0) {
            return 0L;
        }
        long pages = getTotal() / getSize();
        if (getTotal() % getSize() != 0) {
            pages++;
        }
        return pages;
    }

    /**
     * 內部什麼也不幹
     * <p>只是爲了 json 反序列化時不報錯</p>
     */
    default IPage<T> setPages(long pages) {
        // to do nothing
        return this;
    }

    /**
     * 設置是否命中count緩存
     *
     * @param hit 是否命中
     * @since 3.3.1
     */
    default void hitCount(boolean hit) {

    }

    /**
     * 是否命中count緩存
     *
     * @return 是否命中count緩存
     * @since 3.3.1
     */
    default boolean isHitCount() {
        return false;
    }

    /**
     * 分頁記錄列表
     *
     * @return 分頁對象記錄列表
     */
    List<T> getRecords();

    /**
     * 設置分頁記錄列表
     */
    IPage<T> setRecords(List<T> records);

    /**
     * 當前滿足條件總行數
     *
     * @return 總條數
     */
    long getTotal();

    /**
     * 設置當前滿足條件總行數
     */
    IPage<T> setTotal(long total);

    /**
     * 獲取每頁顯示條數
     *
     * @return 每頁顯示條數
     */
    long getSize();

    /**
     * 設置每頁顯示條數
     */
    IPage<T> setSize(long size);

    /**
     * 當前頁,默認 1
     *
     * @return 當前頁
     */
    long getCurrent();

    /**
     * 設置當前頁
     */
    IPage<T> setCurrent(long current);

    /**
     * IPage 的泛型轉換
     *
     * @param mapper 轉換函數
     * @param <R>    轉換後的泛型
     * @return 轉換泛型後的 IPage
     */
    @SuppressWarnings("unchecked")
    default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
        List<R> collect = this.getRecords().stream().map(mapper).collect(toList());
        return ((IPage<R>) this).setRecords(collect);
    }
}

 邏輯刪除

什麼是邏輯刪除?在刪除中,分物理刪除和邏輯刪除,物理刪除:將數據從數據表中刪除;邏輯刪除:使用一個字段標識,未被刪除是一個狀態,刪除以後是一個狀態,說直白一點,邏輯刪除就是一個更新操作。

準備工作

在用戶表新建一個字段用來標識用戶的狀態:deleted(是否刪除 0:正常 1:刪除),由於之前新建users表中已經添加了此字段,所以我們直接開始寫代碼。

編輯配置文件application.yml

# 邏輯刪除
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  #全局邏輯刪除字段值 3.3.0開始支持,詳情看下面。
      logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
      logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)

在實體類Users中設置狀態的字段

/**
     * 是否刪除  0:正常  1:刪除
     */
    @TableLogic
    private Integer deleted;

新建測試代碼

/**
     * 邏輯刪除
     */
    @Test
    public void logicRemove(){

        //刪除用戶名爲令狐沖的用戶信息
        boolean remove = usersService.remove(new UpdateWrapper<Users>().lambda().eq(Users::getUName, "令狐沖"));
        log.info("刪除返回的結果:{}",remove);

    }

 

 

可以發現,令狐沖的deleted狀態被設置爲了1,不知道有沒有細心的靚仔發現了一個問題,那就是我已經在配置文件中設置了logic-delete-field: deleted:全局邏輯刪除字段值,爲什麼還要在實體類中加入 @TableLogic註解呢?

logic-delete-field代表全局配置,如果你設置了這個屬性,那麼系統會自動在所有實體類中匹配你設置的字段,如果沒有找到,執行物理刪除,@TableLogic可以在不同的實體類設置在不同的字段上,如果全局配置和註解都沒有,那麼將會執行物理刪除,註解的形式會相對來說比較靈活,但是代碼侵入較強,全局配置比較死板,但是代碼侵入較弱,項目使用中可以根據自己數據表的結構做出相應的選擇。

注意:邏輯刪除默認只會在MP自帶的方法中生效,比如查詢:list()方法他會自動加上deleted=0的條件,如果sql時自己編寫的時候Mp不會加上deleted=0的條件,所以在使用自定義sql的時候別忘了加上deleted條件。

通用枚舉類

通用枚舉解決了字段轉換的問題,如:性別,數據庫存放的是數字,但是在界面上卻要展示字符串,我們還需要在程序中做一次轉換,使用MP的通用枚舉類之後,就無需我們自己做轉換了。

測試方法

/**
     * 枚舉
     */
    @Test
    public void enumTest(){
        //刪除用戶名爲令狐沖的用戶信息
        Users users = usersService.getOne(new UpdateWrapper<Users>().lambda().eq(Users::getUName, "令狐沖"));
        log.info("查詢返回的結果:{}",users);
    }

這只是一個簡單的查詢列表語句,還沒有設置通用枚舉,我們先看一下沒有設置通用枚舉的結果:

2020-03-10 17:04:04.831  INFO 94084 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-10 17:04:05.091  INFO 94084 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-03-10 17:04:05.158  INFO 94084 --- [           main] com.ymy.MybatisPlusTest                  : 查詢返回的結果:Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T06:21:05, deleted=0)

你會發現這時候我們返回的uSex並不是我們需要的結果,我們需要的是男性或者女性,但是程序給我們返回了 1/2,我們需要自己處理,現在我們使用MP的通用枚舉,代碼改造開始。

 1.新建性別枚舉類

package com.ymy.entity.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.baomidou.mybatisplus.core.enums.IEnum;

/**
 * 性別枚舉類
 */
public enum SexEnum  implements IEnum<Integer> {

    MAN(1,"男"),

    WOMAN(2,"女");

    @EnumValue
    private final int code;
    private final String descp;


    SexEnum(int code, String descp) {
        this.code = code;
        this.descp = descp;
    }


    @Override
    public String toString() {
        return this.descp;
    }
    @Override
    public Integer getValue() {
        return code;
    }

}

2.改造實體類中的性別字段

@JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
    private SexEnum uSex;

@JSONField需要FastJson的依賴。

3.引入FastJson

 <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>

4.編輯配置文件:application.yml

# 邏輯刪除
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  #全局邏輯刪除字段值 3.3.0開始支持,詳情看下面。
      logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
      logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
  type-enums-package: com.ymy.entity.enums   #掃描MP的通用枚舉類所在的package

 運行測試案例,查看結果

這裏我們需要注意一點,如果我們數據庫給定字段的類型如果是tinyint(1),那麼執行上面得到的性別將會時null,原因時MP會將 tinyint(1)映射爲boolean類型,也就是說我們需要將枚舉類中的1,2,改爲true、false,但0一般標識true,非0標識false,true、false無法滿足我們的需求,納入和處理呢?其實很簡單,我們只需要將數據庫的字段類型修改爲int即可。

自動填充功能

自動填充:指定字段在沒有給定具體值時,系統將自動的填充設置好的數據,常見的例子比如數據添加的創建時間,數據修改的修改時間,這些都可以設置爲自動填充。

這個時候我們需要在在數據表增加兩個一個字段,update_time,不能給定默認值,並且不會雖數據修改發生變化。

1.自定義實現通用填充類MyMetaObjectHandler

package com.ymy.config;

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

import java.time.LocalDateTime;

@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推薦使用)
       // this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 該方法有bug請升級到之後的版本如`3.3.1.8-SNAPSHOT`)
        /* 上面選其一使用,下面的已過時(注意 strictInsertFill 有多個方法,詳細查看源碼) */
        //this.setFieldValByName("operator", "Jerry", metaObject);
        //this.setInsertFieldValByName("operator", "Jerry", metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推薦使用)
        //this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 該方法有bug請升級到之後的版本如`3.3.1.8-SNAPSHOT`)
        /* 上面選其一使用,下面的已過時(注意 strictUpdateFill 有多個方法,詳細查看源碼) */
        //this.setFieldValByName("operator", "Tom", metaObject);
        //this.setUpdateFieldValByName("operator", "Tom", metaObject);
    }
}

2.在實體類中將需要填充的字段添加註解標識

 /**
     * 創建時間
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 修改時間
     */
    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;





public enum FieldFill {
    /**
     * 默認不處理
     */
    DEFAULT,
    /**
     * 插入填充字段
     */
    INSERT,
    /**
     * 更新填充字段
     */
    UPDATE,
    /**
     * 插入和更新填充字段
     */
    INSERT_UPDATE
}

3.測試案例

/**
     * 通用字段測試(插入)
     */
    @Test
    public void GeneralFieldInsertTest(){
        Users user = Users.builder().uName("東方不敗").uMail("[email protected]")
                .uPassword("123456").uPhone("15000000000")
                .uSex(SexEnum.MAN).deleted(0)
                .build();
        //插入
        boolean save = usersService.save(user);
        log.info("保存返回的結果:{}",save);
    }

    /**
     * 通用字段測試(修改)
     */
    @Test
    public void GeneralFieldUpdateTest(){

        Users user = new Users();
        user.setUPassword("qoqoqoqoqoqoqo");
        //插入
        boolean save = usersService.update(user,new UpdateWrapper<Users>().lambda().eq(Users::getUName,"東方不敗"));
        log.info("保存返回的結果:{}",save);
    }

4.測試結果

添加:

修改

這樣就實現了代碼的自動填充,但是這裏需要注意一個問題,只有使用Mp自帶方法的時候纔會生效,並且使用還是有條件的:必須是update(實體類,Wapper條件),否者自動填充將無效。

 

樂觀鎖

主要適用場景

意圖:

當要更新一條記錄的時候,希望這條記錄沒有被別人更新

樂觀鎖實現方式:

取出記錄時,獲取當前version

更新時,帶上這個version

執行更新時, set version = newVersion where version = oldVersion

如果version不對,就更新失敗

 

 1.數據庫增加version字段

2.註冊樂觀鎖bean

/**
     * 註冊樂觀鎖
     * @return
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }

3.在Users實體類中添加version,字段,並加上@version註解,標識使用此字段實現樂觀鎖。

/**
     * 版本號
     */
    @Version
    private Long version;

樂觀鎖配置完成,接下來我們將用樂觀鎖兩個事例,一個成功、一個失敗,爲了更好的看到MP所做的操作,我先將sql日誌打印在控制檯中,我們修改配置文件

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  #全局邏輯刪除字段值 3.3.0開始支持,詳情看下面。
      logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
      logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
  type-enums-package: com.ymy.entity.enums   #掃描MP的通用枚舉類所在的package
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  #控制檯輸出sql

現在我們開始寫測試案例,我們需要將張三的密碼修改爲"yyyyyyyy"。

/**
     * 樂觀鎖測試
     */
    @Test
    public void versionTest()  {
        Users users = usersService.getOne(new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三"));
        users.setUPassword("yyyyyyyy");
        boolean update = usersService.updateById(users);
        log.info("修改了用戶信息,狀態:{}", update);
    }

由於數據庫中version字段是剛剛新增的,並且默認0,我們一起來執行一下測試代碼會發生什麼

我們會發現,MP在條件中自動給我們加上了version字段和deleted,deleted是我們之前配置的邏輯刪除,version是我們剛剛配置的樂觀鎖,可能你現在有疑問了,配置這個version字段的用意到底是什麼呢?我舉個例子,假如你的賬戶有100元,這個時候你想給你的朋友轉50元感謝他幫你搶了一個N95口罩,但是這個時候你的媽媽擔心你沒有錢生活了,他往你的賬戶上轉了1000元,按照我們的預期,現在你的賬戶的餘額應該是:1050元,這是我們預期的,但並不代表這是程序執行後的結果,爲什麼呢?因爲有cpu緩存以及多線程的存在,導致你的賬戶餘額可能是1100,也有可能是1050,也有可能是50;這種結果是不是很可怕,這裏我就不演示這種案例,你只要明白就行,而樂觀鎖就是用來保證你給你朋友轉賬/你媽媽給你轉賬只能成功一個,這樣就保證了你資金的安全,現在我們來模擬一個修改用戶信息失敗的案例,請看代碼:

/**
     * 樂觀鎖測試
     */
    @Test
    public void versionTest() throws InterruptedException {
    //線程1
    Thread t1 = new Thread(() -> {
        updatePwd1();
    });
    //線程2
    Thread t2 = new Thread(() -> {
        updatePwd2();
    });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.info("執行完畢");
    }

    /**
     * 將張三的密碼修改爲:pppppppppppp
     *
     * @return
     */
    private void updatePwd1() {
        String threadName = Thread.currentThread().getName();
        Users users = usersService.getOne(new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三"));
        log.info("線程:{} 獲取到的用戶信息:{}", threadName, users);
        try {
            //爲了能讓兩個查詢都執行完畢之後在執行修改操作,所以在這裏休眠了1秒中
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        users.setUPassword("pppppppppppp");
        boolean update = usersService.updateById(users);
        log.info("線程:{} 修改了用戶信息,狀態:{}", threadName, update);
    }

    /**
     * 將張三的密碼修改爲:ooooooooooo
     *
     * @return
     */
    private void updatePwd2() {
        String threadName = Thread.currentThread().getName();
        Users users  = usersService.getOne(new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三"));
        log.info("線程:{} 獲取到的用戶信息:{}", threadName, users);
        try {
            //爲了能讓兩個查詢都執行完畢之後在執行修改操作,所以在這裏休眠了1秒中
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        users.setUPassword("ooooooooooo");
        boolean update = usersService.updateById(users);
        log.info("線程:{} 修改了用戶信息,狀態:{}", threadName, update);
    }

第一個線程需要將張三的密碼修改爲:pppppppppppp,第二個線程需要將密碼修改爲:ooooooooooo,兩個線程同時執行,我這裏爲了保證兩個線程同時執行查詢,並且都還沒執行修改操作,我這裏就是模擬一下同時修改,你們項目開發的時候千萬別這麼寫代碼,由於我們之前已經執行了一次更新操作,我們的數據庫中version字段的值也應該+1

這時候我們再來運行多線程的測試代碼:

Creating a new SqlSession
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@709bb28d] was not registered for synchronization because synchronization is not active
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21152fe7] was not registered for synchronization because synchronization is not active
2020-03-11 10:41:53.870  INFO 115848 --- [       Thread-3] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-03-11 10:41:54.128  INFO 115848 --- [       Thread-3] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@1688741162 wrapping com.mysql.cj.jdbc.ConnectionImpl@3d6da8bf] will not be managed by Spring
==>  Preparing: SELECT id,u_name,u_password,u_mail,u_phone,u_sex,version,create_time,update_time,deleted FROM users WHERE deleted=0 AND (u_name = ?) 
==> Parameters: 張三(String)
<==    Columns: id, u_name, u_password, u_mail, u_phone, u_sex, version, create_time, update_time, deleted
<==        Row: 1, 張三, yyyyyyyy, [email protected], 15000000000, 1, 1, 2020-03-08 06:21:05, 2020-03-09 09:21:10, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21152fe7]
JDBC Connection [HikariProxyConnection@1306964427 wrapping com.mysql.cj.jdbc.ConnectionImpl@3d6da8bf] will not be managed by Spring
2020-03-11 10:41:54.209  INFO 115848 --- [       Thread-3] com.ymy.MybatisPlusTest                  : 線程:Thread-3 獲取到的用戶信息:Users(id=1, uName=張三, uPassword=yyyyyyyy, [email protected], uPhone=15000000000, uSex=男, version=1, createTime=2020-03-08T06:21:05, updateTime=2020-03-09T09:21:10, deleted=0)
==>  Preparing: SELECT id,u_name,u_password,u_mail,u_phone,u_sex,version,create_time,update_time,deleted FROM users WHERE deleted=0 AND (u_name = ?) 
==> Parameters: 張三(String)
<==    Columns: id, u_name, u_password, u_mail, u_phone, u_sex, version, create_time, update_time, deleted
<==        Row: 1, 張三, yyyyyyyy, [email protected], 15000000000, 1, 1, 2020-03-08 06:21:05, 2020-03-09 09:21:10, 0
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@709bb28d]
2020-03-11 10:41:54.221  INFO 115848 --- [       Thread-2] com.ymy.MybatisPlusTest                  : 線程:Thread-2 獲取到的用戶信息:Users(id=1, uName=張三, uPassword=yyyyyyyy, [email protected], uPhone=15000000000, uSex=男, version=1, createTime=2020-03-08T06:21:05, updateTime=2020-03-09T09:21:10, deleted=0)
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@28c0761b] was not registered for synchronization because synchronization is not active
2020-03-11 10:41:55.214  INFO 115848 --- [       Thread-3] com.ymy.config.MyMetaObjectHandler       : start update fill ....
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@420ba4aa] was not registered for synchronization because synchronization is not active
2020-03-11 10:41:55.223  INFO 115848 --- [       Thread-2] com.ymy.config.MyMetaObjectHandler       : start update fill ....
JDBC Connection [HikariProxyConnection@1719623840 wrapping com.mysql.cj.jdbc.ConnectionImpl@3d6da8bf] will not be managed by Spring
==>  Preparing: UPDATE users SET u_name=?, u_password=?, u_mail=?, u_phone=?, u_sex=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? AND deleted=0 
JDBC Connection [HikariProxyConnection@2004050803 wrapping com.mysql.cj.jdbc.ConnectionImpl@35e1ffd5] will not be managed by Spring
==>  Preparing: UPDATE users SET u_name=?, u_password=?, u_mail=?, u_phone=?, u_sex=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? AND deleted=0 
==> Parameters: 張三(String), ooooooooooo(String), [email protected](String), 15000000000(String), 1(Integer), 2(Long), 2020-03-08T06:21:05(LocalDateTime), 2020-03-09T09:21:10(LocalDateTime), 1(Integer), 1(Long)
==> Parameters: 張三(String), pppppppppppp(String), [email protected](String), 15000000000(String), 1(Integer), 2(Long), 2020-03-08T06:21:05(LocalDateTime), 2020-03-09T09:21:10(LocalDateTime), 1(Integer), 1(Long)
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@420ba4aa]
<==    Updates: 1
2020-03-11 10:41:55.257  INFO 115848 --- [       Thread-2] com.ymy.MybatisPlusTest                  : 線程:Thread-2 修改了用戶信息,狀態:false
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@28c0761b]
2020-03-11 10:41:55.258  INFO 115848 --- [       Thread-3] com.ymy.MybatisPlusTest                  : 線程:Thread-3 修改了用戶信息,狀態:true
2020-03-11 10:41:55.258  INFO 115848 --- [           main] com.ymy.MybatisPlusTest                  : 執行完畢

這時候你們會發現,線程名:Thread-2修改密碼成功了,Thread-3修改密碼失敗了,符合我們的要求,兩個線程同時執行修改操作的時候,只能成功一個,我們再來看看數據庫的結果:

 總結

mybatis-plus的介紹及使用就寫到這裏了,其實還有一些特性我沒有寫到,比如:多租戶 SQL 解析器、動態數據源、自定義ID生成器等等,這篇已經寫的很多了,如果有需要可以評論,我可以繼續加。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章