MyBatis-Plus(概述、快速入門、日誌配置、主鍵生成策略、自動填充、CRUD、性能分析插件、條件構造器、代碼生成器)

MyBatis-Plus

在這裏插入圖片描述

(一)概述

  1. MyBatis-Plus(簡稱 MP)是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,爲簡化開發、提高效率而生;是 MyBatis 最好的搭檔,就像魂鬥羅中的 1P、2P,基友搭配,效率翻倍!
    在這裏插入圖片描述
  2. 官網地址:https://mp.baomidou.com/
  3. 特性:
    無侵入、損耗小、強大的 CRUD 操作、支持 Lambda 形式調用、支持主鍵自動生成、支持 ActiveRecord 模式、支持自定義全局通用操作、內置代碼生成器、內置分頁插件、分頁插件支持多種數據庫、內置性能分析插件、內置全局攔截插件。

(二)快速入門

1. 創建數據庫和表

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)
);
-- 插入數據
DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');

2. 創建SpringBoot項目導入項目依賴

<dependencies>
    <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>
    </dependency>
    <!-- mysql -->
    <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
   </dependency>
    <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.0.5</version>
    </dependency>
</dependencies>

3. 配置 application.properties

注意 mysql 5 和 mysql 8 的配置區別:mysql 8 需要時區配置 serverTimezone

spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/數據庫名?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

4. 主體測試代碼

  1. pojo層
@Data
public class User {

    private Long id;

    private String name;

    private Integer age;

    private String email;

}
  1. mapper層
@Repository
public interface UserMapper extends BaseMapper<User> {

}

到這裏基本的CRUD代碼已經編寫完成!!

  1. 啓動類
@SpringBootApplication
@MapperScan("com.chen.mapper")
public class MybatisPlusApplication {

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

}
  1. 測試
@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
}
  1. 結果
    在這裏插入圖片描述

MyBatis-Plus幫我們寫了sql、還有一些方法!!

(三)日誌輸出

1. 配置

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

2. 結果

在這裏插入圖片描述

(四)基本操作測試

1. 數據插入測試&雪花算法

  • 插入數據:
@Test
public void testInsert(){
   User user = new User();
   user.setName("zhangsan");
   user.setAge(10);
   user.setEmail("[email protected]");
   int result = userMapper.insert(user);
   System.out.println(result);
   System.out.println(user);
}
  • 結果:我們並沒有定義id,卻生成了一長串數字!
    在這裏插入圖片描述
  • 爲什麼會自動生成id?
    原因:MyBatis-Plus 會採用主鍵生成策略,這裏採用了 Twitter 的 snowflake 算法(雪花算法

雪花算法:結果是一個 long 型的ID,其核心思想是:使用41bit作爲毫秒數,10bit作爲機器的ID(5個bit是數據中心,5個bit的機器ID),12bit作爲毫秒內的流水號(意味着每個節點在每毫秒可以產生 4096 個 ID),最後還有一個符號位,永遠是0。
具體實現的代碼可以參看:https://github.com/twitter/snowflake

2. 主鍵生成策略

2.1 主鍵自增
  • 在主鍵上增加註解
@Data
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    
}
  • 保證數據庫的主鍵爲自增
    在這裏插入圖片描述
    我麼可以發現自動增量爲: 1256210312843653122
    在這裏插入圖片描述
  • 測試
@Test
public void testInsert(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(10);
    user.setEmail("[email protected]");
    int result = userMapper.insert(user);
    System.out.println(result);
    System.out.println(user);
}
  • 結果
    在這裏插入圖片描述
2.2 註解TableId

我們進入 TableId 源碼查看:發現IdType

public @interface TableId {
    String value() default "";

    IdType type() default IdType.NONE;
}

進入 IdType源碼:

public enum IdType {
    AUTO(0),              // 主鍵自增
    NONE(1),			  // 未設置主鍵
    INPUT(2),			  // 手動輸入:必須自己手動設置id,不然id=null
    ID_WORKER(3),         // 默認的全局唯一id
    UUID(4),              // uuid 全局唯一
    ID_WORKER_STR(5);     // ID_WORKER 字符串表示
}

3. 更新數據操作

將 id 屬性的註解設置爲 @TableId(type = IdType.INPUT),在插入一條數據用來更改。

// 插入數據
@Test
public void testInsert2(){
    User user = new User();
    user.setId(6L);
    user.setName("zhangsan");
    user.setAge(20);
    user.setEmail("[email protected]");
    int result = userMapper.insert(user);
}

在這裏插入圖片描述
更新數據:

@Test
public void updateTest(){
    User user = new User();
    user.setId(6L);
    user.setName("lisi");
    int result = userMapper.updateById(user);
}

在這裏插入圖片描述
發現:updateById方法需要的參數類型是一個對象;並且自動拼接了動態sql:
在這裏插入圖片描述

4. 自動填充

我們新增兩個屬性:create_timeupdate-time,但不設置默認值和更新屬性!
在這裏插入圖片描述

在pojo層:標記爲填充字段

@TableField(fill = FieldFill.INSERT)
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;

FieldFill 屬性源碼:

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

新建一個 Handler 包,創建實現類:填充處理器MyHandler在 Spring Boot 中需要聲明@Component

@Slf4j
@Component
public class MyHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

測試:

// 插入操作
@Test
public void testInsert(){
    User user = new User();
    user.setName("test1");
    user.setAge(10);
    user.setEmail("[email protected]");
    int result = userMapper.insert(user);
    System.out.println(result);
    System.out.println(user);
}
// 更新操作
@Test
public void updateTest(){
    User user = new User();
    user.setId(6L);
    user.setName("lisi");
    int result = userMapper.updateById(user);
}

結果:mybatis-plus 會自動幫我們填充!
在這裏插入圖片描述

5. 查詢操作

  1. 查詢測試:查詢 id=1 的user
@Test
public void selectTest1(){
    User user = userMapper.selectById(1L);
    System.out.println(user);
}

結果:
在這裏插入圖片描述

  1. 批量查詢:插敘 id = 1、2、3 的user
@Test
public void selectTest2(){
    List<User> users= userMapper.selectBatchIds(Arrays.asList(1,2,3));
    users.forEach(System.out::println);
}

結果:
在這裏插入圖片描述

  1. 條件查詢:查詢 name=zhangsan AND age=10 的user
@Test
public void selectTest3(){
    HashMap<String, Object> map = new HashMap<>();
    // 通過map自定義條件
    map.put("name","zhangsan");
    map.put("age",10);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

結果:
在這裏插入圖片描述

6. 分頁查詢

  1. 配置分頁插件
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}
  1. 測試:每頁三條數據查詢
@Test
public void testPage(){
    // 參數:當前頁,頁數大小
    Page<User> page = new Page<>(1,3);
    userMapper.selectPage(page,null);
    page.getRecords().forEach(System.out::println);
}
  1. 結果:sql語句還是使用了 Limit
    在這裏插入圖片描述

7. 刪除操作

@Test
public void deleteTest(){
    // 通過id刪除:刪除id=1的user
    userMapper.deleteById(1L);
    // 批量刪除:刪除1、2、3號user
    userMapper.deleteBatchIds(Arrays.asList(1,2,3));
    // 條件刪除:刪除name=zhangsan的對象
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","zhangsan");
    userMapper.deleteByMap(map);
}

8. 邏輯刪除

  1. 物理刪除:從數據庫中直接移除數據;
    邏輯刪除:數據庫中並沒有移除數據,而是通過一個變量使數據失效(deleted=0、deleted=1);管理員可以查看刪除記錄,類似於回收站!
  2. 測試
  • 在表新增 deleted 字段,默認值爲0;
    在這裏插入圖片描述

  • 在實體類中新增屬性:

@TableLogic 
private Integer deleted;
  • 配置類
// 邏輯刪除
@Bean
public ISqlInjector iSqlInjector(){
    return new LogicSqlInjector();
}
  • 配置
# 邏輯已刪除值(默認爲1)
mybatis-plus.global-config.db-config.logic-delete-value=1 
mybatis-plus.global-config.db-config.logic-not-delete-value=0
  • 測試:刪除1號user
@Test
public void logicalDeleteTest() {
    userMapper.deleteById(1L);
}

結果:數據庫1號user還存在,sql語句是update,將deleted字段更新爲1
在這裏插入圖片描述
在這裏插入圖片描述
我們再來查詢一下1號user:

@Test
public void selectTest1(){
   User user = userMapper.selectById(1L);
   System.out.println(user);
}

結果:已經查詢不到1號user!
在這裏插入圖片描述

9. 樂觀鎖操作

  1. 適用場景:更新的數據是最新的,即這條數據沒有被別人更新。
  2. 實現方式:
  • 取出記錄時,獲取當前 version;
  • 更新時,帶上這個 version;
  • 執行更新時, set version = newVersion where version = oldVersion
  • 如果 version 不對,就更新失敗。
  1. 測試:
  • 在表中添加 version 字段,默認值爲1;
  • 實體類:
@Version
private Integer version;
  • 新增一個配置類:
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
	// 註冊樂觀鎖插件配置
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
    
}
  • 測試樂觀鎖:

測試前的表:
在這裏插入圖片描述
樂觀鎖成功:執行一次updateById

@Test
public void OptimisticLockerTest1(){
    User user = userMapper.selectById(1L);
    user.setName("aaa");
    userMapper.updateById(user);
}

結果:數據更新成功,version 由 1 更新爲 2
在這裏插入圖片描述
樂觀鎖失敗:模擬兩條線程去更新同一條數據

@Test
public void OptimisticLockerTest2(){
    // 線程 1
    User user1 = userMapper.selectById(1L);
    user1.setName("bbb");
    // 線程 2
    User user2 = userMapper.selectById(1L);
    user2.setName("ccc");
    userMapper.updateById(user2);
    userMapper.updateById(user1);
}

結果:由於樂觀鎖的存在,數據更新爲user2,user1更新失敗,version更新爲 3 ,如果沒有樂觀鎖,會覆蓋user2的值!

在這裏插入圖片描述

(五)性能分析插件

用於輸出每條 SQL 語句及其執行時間,比如超過某個時間就會停止操作!使用此插件可以幫助我們提升效率!

1. 配置

@Bean
// 設置 dev test 環境開啓
@Profile({"dev","test"})
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(100);   // 設置sql語句執行的最大時間,超過這個時間就不執行
    performanceInterceptor.setFormat(true);   // 設置格式化
    return performanceInterceptor;
}

在application.properties中,設置開發環境:

spring.profiles.active=dev

2. 測試

查詢user結果:

@Test
void contextLoads() {
    List<User> list = userMapper.selectList(null);
    list.forEach(System.out::println);
}

結果:
在這裏插入圖片描述

(六)條件構造器

用來操作一些複雜查詢的構造器!

1. isNotNull、ge、gt、lt、le

isNotNull:不爲空
ge:大於等於
gt:大於
lt:小於
le:小於等於

如查詢user,條件:name不爲空 AND email不爲空 AND age大於等於20

@Test
void test1() {
 QueryWrapper<User> wrapper = new QueryWrapper<>();
 wrapper
         .isNotNull("name")
         .isNotNull("email")
         .ge("age",20);
 List<User> list = userMapper.selectList(wrapper);
 list.forEach(System.out::println);
}

結果:
在這裏插入圖片描述

2. eq

eq:等於
如:查詢user姓名爲 Jack 的用戶

@Test
void test2(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name","Jack");
    User user = userMapper.selectOne(wrapper);
    System.out.println(user);
}

結果:
在這裏插入圖片描述

3. between

between:值1 AND 值2
如:查詢20~30歲之間的用戶數量

@Test
void test3(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age",20,30);
    Integer count = userMapper.selectCount(wrapper);
    System.out.println(count);
}

結果:
在這裏插入圖片描述

4. 模糊查詢

  • like
    查詢name中有王字的name:like("name", "王")—>name like '%王%'
  • notLike
    查詢name中沒有王字的name:notLike("name", "王")—>name not like '%王%'
  • likeLeft
    從name的左邊開始匹配含有王字的name,likeLeft("name", "王")—>name like '%王'
  • likeRight
    從name的右邊開始匹配含有王字的name,likeRight("name", "王")—>name like '王%'

5. inSql

子查詢,字段 IN ( sql語句 )

@Test
void test4(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.inSql("id","select id from user where id<3");
    List<Object> list = userMapper.selectObjs(wrapper);
    list.forEach(System.out::println);
}

結果:
在這裏插入圖片描述

6. orderByAsc、orderByDesc

orderByAsc:降序查詢
orderByDesc:升序查詢

@Test
void test5(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.orderByAsc("id");
    List<Object> list = userMapper.selectObjs(wrapper);
    list.forEach(System.out::println);
}

結果:
在這裏插入圖片描述

(七)代碼生成器

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

1. 新建一個SpringBoot項目

我們並沒有創建entity、mapper、service…包。
在這裏插入圖片描述

2. 編寫代碼生成器

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
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.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;

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(projectPath + "/src/main/java");
        // 設置作者
        gc.setAuthor("Mr.Chen");
        // 是否覆蓋
        gc.setOpen(false);
        // 去除Service的I前綴
        gc.setServiceName("%sService");
        // 設置id的類型
        gc.setIdType(IdType.ID_WORKER);
        // 設置時間類型
        gc.setDateType(DateType.ONLY_DATE);
        // 設置Swagger文檔生成工具:當然需要導入Swagger的依賴
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        /**
         * 數據源配置
         */
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        /**
         * 包配置
         * */
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("study");
        pc.setParent("com.chen");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setController("controller");
        pc.setService("service");
        mpg.setPackageInfo(pc);

        /**
         *  策略配置
         * */
        StrategyConfig strategy = new StrategyConfig();
        // 設置映射的表
        strategy.setInclude("user");
        // 下劃線轉駝峯命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 開啓lombok
        strategy.setEntityLombokModel(true);
        strategy.setLogicDeleteFieldName("deleted");
        // 自動填充配置
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT);
        ArrayList<TableFill> list = new ArrayList<>();
        list.add(createTime);
        list.add(updateTime);
        strategy.setTableFillList(list);
        // 樂觀鎖配置
        strategy.setVersionFieldName("version");
        // controller層配置
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);// 跳轉地址變爲下劃線連接格式
        mpg.setStrategy(strategy);
        mpg.execute();
    }
}

3. 測試結果

包和代碼全部自動生成!!後面要生成其他對應的代碼,只需要改數據庫名即可!!
在這裏插入圖片描述
我們查看實體類:這全是mybatis-plus的代碼生成器幫我們實現的!!!

import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 *  用戶類
 * </p>
 *
 * @author Mr.Chen
 * @since 2020-05-05
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="User對象", description="")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

        @ApiModelProperty(value = "主鍵ID")
        @TableId(value = "id", type = IdType.AUTO)
    private Long id;

        @ApiModelProperty(value = "姓名")
        private String name;

        @ApiModelProperty(value = "年齡")
        private Integer age;

        @ApiModelProperty(value = "郵箱")
        private String email;

        @ApiModelProperty(value = "創建時間")
        @TableField(fill = FieldFill.INSERT)
    private Date createTime;

        @ApiModelProperty(value = "更新時間")
        @TableField(fill = FieldFill.INSERT)
    private Date updateTime;

        @ApiModelProperty(value = "樂觀鎖")
        @Version
    private Integer version;

    @TableLogic
    private Integer deleted;
    
}

//下篇再見…謝謝
在這裏插入圖片描述

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