天天看點

MyBatisPlus學習

簡介

  • 是什麼? MyBatis 本來就是簡化 JDBC 操作的!
  • 官網:​​Redirect​​ MyBatis Plus,簡化 MyBatis !
MyBatisPlus學習

特性

無侵入:隻做增強不做改變,引入它不會對現有工程産生影響,如絲般順滑

損耗小:啟動即會自動注入基本 CURD,性能基本無損耗,直接面向對象操作, BaseMapper

強大的 CRUD 操作:内置通用 Mapper、通用 Service,僅僅通過少量配置即可實作單表大部分

CRUD 操作,更有強大的條件構造器,滿足各類使用需求, 以後簡單的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 操作智能分析阻斷,也可自定義攔截規則,預防誤

操作

極速入門

位址:​​https://mp.baomidou.com/guide/quick-start.html#初始化工程​​

使用第三方元件:

1、導入對應的依賴

2、研究依賴如何配置

3、代碼如何編寫

4、提高擴充技術能力!

步驟

  • 建立資料庫mybatis_plus
create database   mybatis_plus charset = "utf8" ;      
  • 建立表,并注入基本資料
use mybatis_plus;

drop table if exists user;

create table user(
    id BIGINT(20) not null comment '主鍵ID',
    name VARBINARY(30) null DEFAULT null comment '姓名' ,
    age INT(11) null  default  null comment '年齡' ,
    email VARBINARY(50) null  default null comment  '郵箱' ,
    primary key (id)

);

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]');
      
  • 配置依賴
<!--web場景啟動器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.4.1</version>
        </dependency>
         <!--test啟動器,并且排除vitage的junit-->
        <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>
        <!--資料庫連結驅動資料庫版本5.7 這裡使用6.0.6-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>
        <!--加入mybatis-plus的場景啟動器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
    </dependencies>
      

⚠️ Tips:說明:我們使用 mybatis-plus 可以節省我們大量的代碼,盡量不要同時導入 mybatis 和 mybatisplus!版本的差異!

  • 配置yaml連結資料庫,這裡分mysql版本,注意要加時區
spring:
  profiles:
    active: mysql5

---
#   mysql5.7   com.mysql.jdbc.Driver
spring:
  config:
    activate:
      on-profile: mysql5
  datasource:
    username: root
    password: 125697
    url: jdbc:mysql://47.111.237.28:3388/mybatis_plus?useSSL=false&useUnicode=true&charasetEnCoding=utf-8&severTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
#配置mybatis-plus的日志,這裡直接輸出控制台
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

---
#   mysql8.x  com.mysql.cj.jdbc.Driver   必須要加serverTimezone=Asia/Shanghai、Asia/HongKong
spring:
  config:
    activate:
      on-profile: mysql8
  datasource:
    username: root
    password: 125697
    url: jdbc:mysql://47.111.237.28:3388/mybatis_plus?useSSL=false&useUnicode=true&charasetEnCoding=utf-8&severTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
      
  • 傳統方式pojo-dao(連接配接mybatis,配置mapper.xml檔案)-service-controller
  • 使用了Mybatis—plus之後
  • pojo
/**
 * @author starrysky
 * @title: User
 * @projectName mybaits_plus_final
 * @description: pojo-user
 * @date 2021/1/3119:30
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
      
  • mapper,繼承mybatis-plus的BaseMapper
/**
 * @author starrysky
 * @title: UserMapper
 * @projectName mybaits_plus_final
 * @description: TODO
 * @date 2021/1/3119:31
 */
@Repository
public interface UserMapper extends BaseMapper<User> {
}
      
  • ⚠️ Tips:注意點,我們需要在主啟動類上去掃描我們的mapper包下的所有接口@MapperScan("icu.lookyousmileface.mapper")
  • 測試類中測試
@SpringBootTest
class MybaitsPlusFinalApplicationTests {

    @Autowired
    private UserMapper userMapper;

    /**
     * 查詢所有的使用者
     */
    @Test
    void contextLoads() {
        List<User> users = userMapper.selectList(null);
        users.stream().forEach(System.out::println);
    }

}
      
MyBatisPlus學習

配置mybatis-plus的日志

#這裡直接輸出到控制台即可
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl      

插入

  • Insert 插入
/**
     * 插入使用者
     */
    @Test
    void userInster(){
        user.setName("skystartx");
        user.setAge(88);
        user.setEmail("[email protected]");

        int insert = userMapper.insert(user);//自動生産主鍵
        System.out.println(insert);//影響行數
        System.out.println(user);//顯示插入的使用者
    }
      

​⚠️ 預設插入的id主鍵預設值(IdType.NONE):是全局唯一

主鍵生成政策

  • 預設 ID_WORKER 全局唯一id
  • 分布式系統唯一id生成:​​分布式系統唯一ID生成方案彙總 - nick hao - 部落格園​​
  • 雪花算法:

    snowflflake是Twitter開源的分布式ID生成算法,結果是一個long型的ID。其核心思想是:使用41bit作為毫秒數,10bit作為機器的ID(5個bit是資料中心,5個bit的機器ID),12bit作為毫秒内的流水号(意味着每個節點在每毫秒可以産生 4096 個 ID),最後還有一個符号位,永遠是0。可以保證幾乎全球唯一!

  • 可以通過@TableId 注解 選擇主鍵id的插入模式,可選的如下:
@Getter
public enum IdType {
        //自增id
    AUTO(0),
        //預設類型全局唯一
    NONE(1),
        //使用者自動組冊填充插件填充
    INPUT(2),
        //雪花算法實作全局唯一,主鍵類型為number或者Long
    ASSIGN_ID(3),
        //配置設定UUID,主鍵類型為string類型
    ASSIGN_UUID(4),
        //過時,預設的全局唯一id
    @Deprecated
    ID_WORKER(3),
        //過時,預設的全局唯一id string類型
    @Deprecated
    ID_WORKER_STR(3),
        //過時,UUID
    @Deprecated
    UUID(4);

}
      

⚠️ Tips:主鍵自增,需要設定資料庫内的表的字段也是自增才可以

  • 例如:設定主鍵的為雪花算法填充
  • User
/**
 * @author starrysky
 * @title: User
 * @projectName mybaits_plus_final
 * @description: pojo-user
 * @date 2021/1/3119:30
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {

    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
      
  • 測試方法:
/**
     * 插入使用者 主鍵模式為雪花算法
     */
    @Test
    void userInster2(){
        user.setName("mantianx");
        user.setAge(99);
        user.setEmail("[email protected]");

        int insert = userMapper.insert(user);
        System.out.println(insert);
        System.out.println(user);
    }
      

更新操作

  • 例子:
/**
     * 更新操作
     */
    @Test
    void userUpdate(){
        user.setId(5L);
        user.setAge(1024);
        user.setName("哈哈哈哈哈");

        int i = userMapper.updateById(user);
        System.out.println(i);
        System.out.println(user);
    }
      

​⚠️ Tips:updateById會根據目前設定的Id修改對應使用者的資訊。

自動填充

  • 阿裡巴巴開發手冊:所有的資料庫表:gmt_create、gmt_modifified幾乎所有的表都要配置上!而且需要自動化!
  • 方式一:資料庫級别(工作中不允許你修改資料庫)
  1. 在表中新增兩個字段create_time、update_time
alter table user add create_time datetime  default CURRENT_TIMESTAMP  comment '建立時間';
alter table user add update_time datetime  default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP comment '更新時間';
      
  1. 在pojo中加入這兩個屬性
private Date createTime;
private Date updateTime;
      
  1. 檢視效果

MyBatisPlus學習
MyBatisPlus學習

​​

  • 方式二:代碼級别
  1. 現将之前資料庫中的字段恢複沒有update on和預設值的兩個字段
  2. 在和資料庫中對應的字段上加上注解,也就是設定自動填充
@TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
      
  • 編寫處理器,需要實作MetaObjectHandler接口的兩個方法
/**
 * @author starrysky
 * @title: MyMataHandler
 * @projectName mybaits_plus_final
 * @description: Meta處理器,當對應pojo的字段上設定@TableField值時,
 * 觸發了inster/update時間都會更新createTime或者updateTime兩個字段
 * @date 2021/1/3123:28
 */

@Slf4j
@Component
public class MyMataHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
      log.info("有資料開始插入....開始更新建立時間戳");
      this.setFieldValByName("createTime",new Date(),metaObject);
      this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("有資料被操作...開始更新最新的時間戳");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}
      

樂觀鎖

  • 概念:

    樂觀鎖 : 故名思意十分樂觀,它總是認為不會出現問題,無論幹什麼不去上鎖!如果出現了問題,再次更新值測試

    悲觀鎖:故名思意十分悲觀,它總是認為總是出現問題,無論幹什麼都會上鎖!再去操作!

  • 樂觀鎖實作方式:
樂觀鎖:1、先查詢,獲得版本号 version = 1
 -- A update user set name = "queue", version = version + 1 where id = 2 and version = 1
 -- B 線程搶先完成,這個時候 version = 2,會導緻 A 修改失敗! update user set name = "queue", version = version + 1 where id = 2 and version = 1      
  1. 取出記錄時,擷取目前 version
  2. 更新時,帶上這個version
  3. 執行更新時, set version = newVersion where version = oldVersion
  4. 如果version不對,就更新失敗
  • 測試一下MP的樂觀鎖插件
  1. 給資料庫中增加version字段!
alter table user add version INT(11) default 1 after age;
      
  1. 我們實體類加對應的字段
@Version
    private Integer version;      
  1. 注冊元件
//開啟事務注解
@EnableTransactionManagement
@MapperScan("icu.lookyousmileface.mapper")
@Configuration
public class MybatisPlusConfig {

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

}      
  1. 測試一下!
/**
     * 樂觀鎖測試
     */
    @Test
    void IntLock(){
        User user = userMapper.selectById(1L);

        user.setName("趙子龍");
        user.setAge(9000);

        userMapper.updateById(user);
    }
    /**
     * 多線程下測試樂觀鎖
     */
    @Test
    void RunableLock(){
        User user1 = userMapper.selectById(2L);
        user1.setName("Thread3-changed=>success!");
        //假裝有線程插隊
        User user2 = userMapper.selectById(2L);
        user2.setName("Thread4-changed=>success!");
        userMapper.updateById(user2);
        //自旋鎖多次嘗試
        userMapper.updateById(user1);//沒有樂觀鎖就會被覆寫調

    }
}      

查詢操作

/**
     * 測試查詢
     */
    @Test
    void selectTest(){
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }
    /**
     * 批量查詢,根據id
     */
    @Test
    void latchBatchSelet(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
        users.stream().forEach(System.out::println);
    }
    /**
     * 按條件查詢之一使用map操作
     */
    @Test
    void selectMap(){
        Map<String,Object> map = new ConcurrentHashMap<>();
        /**
         * 這兩個條件是使用And與在一起的不是分開查詢的兩個語句
         */
        map.put("name","Sandy");
        map.put("age",21);

        List<User> users = userMapper.selectByMap(map);
        users.stream().forEach(System.out::println);
    }      

分頁查詢

  • 分頁在網站使用的十分之多!
  • 分頁:

    1、原始的 limit 進行分頁

    2、pageHelper 第三方插件

    3、MP 其實也内置了分頁插件!

  • 使用:
  1. 配置攔截器元件
/**
     * mybatis-plus的分頁插件
     * @return new PaginationInterceptor()
     */
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }      
  1. 直接使用Page對象即可!
/**
     * MP的分頁插件測試
     */
    @Test
    void limitPages(){
        Page<User> page = new Page<>(3,4);

        userMapper.selectPage(page,null);

        page.getRecords().stream().forEach(System.out::println);
        System.out.println(page.getTotal());
    }      

删除操作

/**
     * 測試删除
     */
    @Test
    void deleteTest() {
        int i = userMapper.deleteById(1L);
        System.out.println(i);
    }

    /**
     * 批量删除
     */
    @Test
    void deleteBatch() {
        int i = userMapper.deleteBatchIds(Arrays.asList(3L, 1355902835564224514L));
        System.out.println(i);
    }

    /**
     * 按條件删除map使用
     */
    @Test
    void deleteMap() {

        Map<String, Object> map = new ConcurrentHashMap<>(16, 0.75F);
        map.put("age", 1024);
        map.put("name", "哈哈哈哈哈2");

        int i = userMapper.deleteByMap(map);

        System.out.println("影響行數:"+i);
    }      

邏輯删除

  • 概念:

    實體删除 :從資料庫中直接移除

    邏輯删除 :再資料庫中沒有被移除,而是通過一個變量來讓他失效! deleted = 0 => deleted = 1

    管理者可以檢視被删除的記錄!防止資料的丢失,類似于資源回收筒!

  • 使用:
  1. 資料庫中增加deleted字段預設為0,即沒有删除
alter table user add deleted INT(11) default 0  comment '邏輯删除' after age ;      
  1. 在pojo中添加deleted字段并且标上邏輯删除的注解
@TableLogic
    private Long deleted;      
  1. 配置bean、yml
/**
     * 邏輯删除插件
     */
    @Bean
    public ISqlInjector injector(){
        return new DefaultSqlInjector();
    }
      
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0
      
  • 再測試一下删除,會發現資料庫中還存在,但是查不到了,其實也就是在之後的查詢語句加上deleted=0罷了

性能分析插件

  • P6Spy
  • 使用:
  1. 配置p6spy的依賴
<dependency>
   <groupId>p6spy</groupId>
   <artifactId>p6spy</artifactId>
   <version>3.9.1</version>
</dependency>      
  1. 替換jdbc的驅動
driver-class-name: com.p6spy.engine.spy.P6SpyDriver      
  1. 修改spring.datasource.url
url: jdbc:p6spy:mariadb://127.0.0.1:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai      
  1. 添加配置檔案 spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定義日志列印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志輸出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系統記錄 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 設定 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL字首
useprefix=true
# 配置記錄 Log 例外,可去掉的結果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 實際驅動可多個
#driverlist=org.h2.Driver
# 是否開啟慢SQL記錄
outagedetection=true
# 慢SQL記錄标準 2 秒
outagedetectioninterval=2
      

條件構造器

  • 十分重要:Wrapper
  • 我們寫一些複雜的sql就可以使用它來替代!
/**
     * 條件構造器,isNotNull非空、get大于等于
     */
    @Test
    void queryWrapper1 () {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.isNotNull("name")
                .isNotNull("age")
                .isNotNull("email")
                .ge("age", 22);

        List<User> users = userMapper.selectList(wrapper);
        users.stream().forEach(System.out::println);
    }
    /**
     * 條件構造器,eq、selectOne
     */
    @Test
    void queryWrapper2(){

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name","Sandy");

        User user = userMapper.selectOne(wrapper);// 查詢一個資料,出現多個結果使用List 或者 Map
        System.out.println(user);
    }
    /**
     * 條件構造器 ,between
     */
    @Test
    void queryWrapper3(){

        QueryWrapper<User> wrappper = new QueryWrapper<>();
        wrappper.between("age",21,99);

        List<User> users = userMapper.selectList(wrappper);
        users.stream().forEach(System.out::println);
    }
    /**
     * 條件構造器,like比對
     */
    @Test
    void queryWrapper4() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        /**
         * %{}%類型
         */
//        QueryWrapper<User> like = queryWrapper.like("name", "這");
        /**
         * %{}%類型,比對到就排出比對到到反向選擇
         */
//        QueryWrapper<User> wrapper = queryWrapper.notLike("name", "這");
        /**
         * {}%類型,從右邊比對
         */
        QueryWrapper<User> wrapper = queryWrapper.likeRight("name", "這");
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        for (Map<String, Object> value : maps) {
            System.out.println(value);
        }
    }
        /**
         * 模糊查詢,子查詢
         */
        @Test
        void queryWrapper5(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
             queryWrapper.inSql("id" , "select id from user where id>50");
//            List<User> users = userMapper.selectList(wrapper);
            /**
             * 隻傳回第一個字段的值
             */
            List<Object> objects = userMapper.selectObjs(queryWrapper);
            objects.forEach(System.out::println);
        }
      /**
      * 排序 ASC正序,DESC倒叙
      */
      @Test
    void queryWrapper6(){
          QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//          queryWrapper.orderByAsc("id");
            queryWrapper.orderByDesc("id");
          List<User> users = userMapper.selectList(queryWrapper);
          users.forEach(System.out::println);

      }