【主流技術】Mybatis Plus的理解與應用

2022-06-13 12:02:56

前言

mybatis plus是一個mybatis的增強工具,在其基礎上只做增強不做改變。作為開發中常見的第三方元件,學習並應用在專案中可以節省開發時間,提高開發效率。

官方檔案地址:MyBatis-Plus (baomidou.com)

一、特性

1.1損耗小

自動注入基本CRUD,效能無失真耗,直接物件導向操作(通過BaseMaper<約定的泛型>);

1.2支援lambda表示式

通過lambda表示式的形式,方便編寫各類查詢條件,無需擔心欄位出錯;

1.3支援主鍵自動生成

內含分散式唯一ID生成器-Squence,可自行設定主鍵;

1.4支援ActiveRecord模式

實體類只需繼承Model類即可進行CRUD操作;

1.5支援分頁外掛

基於mybatis物理分頁,設定好外掛後自動將資料分頁;

二、快速入門

2.1建立資料庫

2.1.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)
);
2.1.2初始化專案
  • 匯入依賴(儘量不要與mybatis一起匯入)
  • 設定依賴
  • 編寫程式碼
  • 拓展技術能力
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
2.1.3連線資料庫
#mysql設定
#資料庫使用者名稱
spring.datasource.username=root
#登入密碼
spring.datasource.password=password123
#JDBC地址、編碼、安全連線
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
#資料庫驅動
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

2.2編寫程式碼

2.2.1實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    //使用者id
    private Long id;
    //使用者名稱
    private String name;
   //年齡
    private Integer age;
   //郵箱
    private String email;
}

2.2.2mapper檔案
@Mapper
@Repository
//該介面繼承BaseMapper類所有方法
public interface UserMapper extends BaseMapper<User> {
}
2.2.3邏輯實現層
    @Autowired
    private UserMapper userMapper;
  //建立條件構造器
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        userMapper.selectList(wrapper);
 

2.3紀錄檔設定

#mybatis-plus紀錄檔設定,預設控制檯輸出紀錄檔,properties檔案
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

2.4CRUD擴充套件

2.4.1插入操作(insert)
    void insert(){
        User user = new User();
        user.setName("Alex");
        user.setAge(42);
        user.setEmail("[email protected]");
        //受影響的行數
        int result = userMapper.insert(user);
        System.out.println(result);
        System.out.println(user);
    }

主鍵生成策略

實體類宣告資料庫表名,同時設定主鍵id為自增。

//資料庫表名
@TableName("user")
public class User implements Serializable {
    //使用者id,主鍵自增
    @TableId(value = "id",type = IdType.AUTO)
    private Long id;
}
2.4.2更新操作(update)
 //更新操作
    @Test
    void updateTest(){
        User user = new User();
        user.setId(3L);
        user.setName("docker&K8s");
        //操作結果條數
        int i = userMapper.updateById(user);
        System.out.println(i);
    }
時間自動填充策略
方式一(資料庫級別):不推薦使用

在資料庫中新建欄位並設定為操作的當前時間,且需要在實體類同步屬性:

從而達到自動填充時間的。

 private Date createTime;
 private Date updateTime;
方式二(程式碼級別):
  • 在實體類屬性加上對應註解,自動填充時間欄位:
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(fill = FieldFill.UPDATE)
    private Date updateTime;
  • 程式碼邏輯中新建時間物件獲取當前時間(推薦使用):
user.setUpdateTime(new Date());
2.4.3樂觀鎖處理

在面試中經常會提及悲觀鎖、樂觀鎖的概念,其實這兩個概念非常簡單。

樂觀鎖

樂觀鎖顧名思義十分樂觀,它總是認為不會出現問題,無論幹什麼都不會去上鎖。如果出現問題,就再更新值去測試。

悲觀鎖

悲觀鎖顧名思義十分悲觀,它總是認為會出現問題,無論幹什麼都會去上鎖,然後再去操作。

樂觀鎖機制

  • 取出記錄時,獲取當前version
  • 更新時帶上該version
  • 執行更新時,set version = newVersion where version = oldVersion
  • 若version不對,則更新失敗
樂觀鎖:1、先去查詢獲得版本號version = 1
--A執行緒
update user set name = "zhuzhiqiang",version = version + 1
where id = 2 and version = 1
若B執行緒搶先完成,這時version = 2,導致執行緒A修改失敗
--B執行緒
update user set name = "zhuzhiqiang",version = version + 1
where id = 2 and version = 1

測試mybatisPlus的樂觀鎖外掛

1、在資料庫表中新增version欄位:

2、實體類新增對應屬性,並新增@Version註解:

  //樂觀鎖欄位(註解)
  @Version
  private Integer version;

3、註冊元件:

@EnableTransactionManagement //事務管理註解
@Configuration
public class MyBatisPlusConfig { 
    //註冊樂觀鎖外掛
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor(){
        return new OptimisticLockerInterceptor();
    }
}
2.4.4分頁查詢處理(本質上還是執行limit)

使用步驟

  1. 設定攔截器元件(設定類中):

    //分頁查詢外掛
        @Bean
        public PaginationInterceptor paginationInterceptor(){
           return new PaginationInterceptor();
        }
    
  2. 直接使用Page物件即可:

     //測試分頁查詢
        @Test
        public void testPage(){
            //page物件 引數1:當前頁;引數2:頁面展示條數
            Page<User> page = new Page<>(1,5);
            userMapper.selectPage(page,null);
            page.getRecords().forEach(System.out::println);
        }
    
2.4.5刪除操作

邏輯刪除

邏輯刪除指的是在資料庫中沒有被刪除,而是通過一個變數來使其失效:deleted = 0 -> deleted = 1

  1. 資料庫表中增加欄位:

  2. 實體類中新增對應屬性:

      //邏輯刪除(註解) 
      @TableLogic
      private Integer deleted;
    
  3. properties設定:

    #mybatis-plus邏輯刪除設定,刪除為1,未刪除為0
    mybatis-plus.global-config.db-config.logic-delete-value = 1
    mybatis-plus.global-config.db-config.logic-not-delete-value = 0
    
  4. 測試刪除(本質上是一個更新操作):

    注:若執行了邏輯刪除,那麼再次查詢該條資料時,會在select語句中自動拼接deleted=0,即查詢不到該條語句。

2.4.6條件構造器Wrapper

按照複雜條件進行查詢,本質上等價於使用複雜sql進行查詢。

  1. 原始碼分析:

    //wrapper是條件構造器物件,底層繼承Wrapper類走SQL查詢
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    
  2. 測試使用:

        public void testWrapper(){
            String WxUserId = "16";
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            //相當於where語句,查詢一條wx_user_id欄位中值為WxUserId,且opinion_id欄位中值為opinionId的資訊
            wrapper
                  .eq("wx_user_id",WxUserId)
                  .eq("opinion_id", opinionId);
            userMapper.selectOne(wrapper);
        }
    
  3. 複雜條件的SQL查詢

    • like模糊查詢

       public void testLike(){
              QueryWrapper<User> wrapper = new QueryWrapper<>();
              //相當於where語句,
              wrapper
                    //表示name欄位中 不包含e的資料
                    .notLike("name","e")
                    //右查詢,以t開頭的email欄位資料
                    .likeRight("email", "t");
          }
      

三、高階用法

3.1基本概念

AutoGenerator 是 MyBatis-Plus 的程式碼生成器,通過 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模組的程式碼,極大的提升了開發效率。

3.2建立工具類

public class AutoCodeTool {
    public static void main(String[] args) {
        //構建程式碼自動生成器物件
        AutoGenerator autoCode = new AutoGenerator();
        //全域性設定生效
        autoCode.setGlobalConfig(gc);
        //資料來源生效
        autoCode.setDataSource(dsc);
        //包設定生效
        autoCode.setPackageInfo(packageConfig);
        //其它策略生效
        autoCode.setStrategy(strategyConfig);
        //執行程式碼生成器
        autoCode.execute();
    }
}

3.3全域性設定

//1、全域性設定
GlobalConfig gc = new GlobalConfig();
//輸出目錄,將自動生成的程式碼生成在以下路徑中
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
//設定作者資訊
gc.setAuthor("Created by zhuzqc");
//生成程式碼後不開啟檔案管理器
gc.setOpen(false);
//是否覆蓋原來生成的程式碼
gc.setFileOverride(false);
//主鍵型別
gc.setIdType(IdType.ID_WORKER);
//時間型別
gc.setDateType(DateType.ONLY_DATE);
//設定swagger檔案
gc.setSwagger2(true);

3.4設定資料來源

//2、設定資料來源
DataSourceConfig dsc = new DataSourceConfig();
//資料來源具體設定
dsc.setUrl("jdbc:mysql://localhost:3306/mubatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("password123");
dsc.setDbType(DbType.MYSQL);

3.5包的設定

//3、包的設定
PackageConfig packageConfig = new PackageConfig();
//生成包路徑
packageConfig.setParent("com.dcone");
//生成模組名
packageConfig.setModuleName("common");
//生成entity
packageConfig.setEntity("pojo");
//生成mapper
packageConfig.setMapper("dao");
//生成service
packageConfig.setService("service");
//生成controller
packageConfig.setController("controller");

3.6其它策略

//4、其它策略
StrategyConfig strategyConfig = new StrategyConfig();
//設定需要對映的資料庫表
strategyConfig.setInclude("user");
//駝峰命名
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
//設定Lombok
strategyConfig.setEntityLombokModel(true);
//RESTFUL風格
strategyConfig.setRestControllerStyle(true);
//邏輯刪除
strategyConfig.setLogicDeleteFieldName("deleted");
//自動填充設定(建立和修改時間)
TableFill gmtCreate = new TableFill("gmt_create_time", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified_time", FieldFill.INSERT_UPDATE);
autoCode.setStrategy(strategyConfig);
//作為列表元素新增自動填充策略
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategyConfig.setTableFillList(tableFills);
//樂觀鎖設定
strategyConfig.setVersionFieldName("version");
//存取URL下劃線風格
strategyConfig.setControllerMappingHyphenStyle(true);

四、總結

4.1優點

  • 通過少量設定即可實現單表大部分 CRUD 操作(將簡單查詢封裝),更有強大的條件構造器,滿足各類使用需求;
  • 採用程式碼或者 Maven 外掛可快速生成 Mapper 、 Model 、 Service 、 Controller 層程式碼,支援模板引擎;
  • 支援 ActiveRecord 形式呼叫,實體類只需繼承 Model 類即可進行強大的 CRUD 操作。

4.2缺點

  • 對資料存取層DAO的上層入侵太強,入侵到service、甚至controller,將層次結構耦合起來;
  • 資料查詢程式碼複雜,最終SQL過程黑盒,不利於業務性優化,不利於排查問題;
  • 一旦專案躍遷到微服務,其難以在複雜高效能大規模服務上應用。