MyBatisPlus

簡介

1、引入依賴

2、application.yml配置

  • spring boot 2.1及以上(内置jdbc8驅動),驅動類使用 driver-class-name: com.mysql.cj.jdbc.Driver
  • spring boot 2.0(内置jdbc5驅動),驅動類使用:driver-class-name: com.mysql.jdbc.Driver
  • MySQL5.7版本的url : jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
  • MySQL8.0版本的url:jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
  • 於MyBatisPlus中配置log-impl會於日誌中自動產生使用的SQL語法
    • mybatis-plus.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3、lombok

  • 在bean上面設置@NoArgsConstructor,會自動生成無參構造器
  • @AllArgsConstructor,會自動生成有參構造器
  • @Getter,會自動生成getter
  • @Setter,會自動生成setter
  • @EqualsAndHashCode,會自動生成equal和HashCode
  • 以上全部可以用一個 @Data替代,但是無有參構造

4、BaseMapper

  • 掃描mapper接口所在的包,在Spring Boot啟動類中添加@MapperScan(“com.twelvefish.MyBatisPlus.mapper”)
    • 或是放置於@Configuration配置類中
  • 藉由繼承BaseMapper來使用MyBatisPlus所提供的各種方法,而泛型能夠映射資料庫,將對應的表資料結構取出
  • BaseMapper是MyBatisPlus提供的模板mapper,其中包含了基本的CRUD方法,泛型為操作的實體類型
  • IDEA在 userMapper 報錯,因為找不到注入的對象,因为類是動態創建,但是編譯可以正常執行,但想避免報錯,可以在mapper接口上添加 @Repository 注解
  • 下述截圖範例為使用UserMapper去查詢資料庫中的資料,並query全部資料回來,通過條件構造器查詢一個list集合,若沒有條件,則可設置為null


5、多表查詢

  • 自定義的SQL語句預設放在resources/mapper底下的xml,或是在yml中自定義路徑
  • 在繼承BaseMapper的類下增加自定義方法
  • 自定義方法可自己撰寫SQL語法,搭配xml的id匹配對應的方法名稱

6、Service

  • MyBatis-Plus中有一個接口 IService和實現類 ServiceImpl,封裝了常見的業務層邏輯
  • UserService繼承IService模板提供的基礎功能
  • ServiceImpl實現了IService,提供了IService中基礎功能的實現
  • 若ServiceImpl無法滿足業務需求,則可以使用自定的UserService定義方法,並在實現類中實現

7、常用註解

  • @TableName
    • 在實體類類型上添加@TableName(“XXXX”),即可標識實體類對應的資料庫表名稱,預設則為第一字母小寫
    • 或是也可以直接在application.yml進行全域配置table-prefix,針對每張表都進行前缀處理,就可以不用每張表都增加@TableName
  • @TableId
    • MyBatisPlus默認將 “id” 作為主鍵,可以使用@TableId此參數,告訴MyBatisPlus此張表的主鍵字串是哪個欄位
    • 但也可以透過@TableId的value屬性,指定表中的主鍵字串,@TableId(value=”uid”)
    • @TableId(type=IdType.ASSIGN_ID)(默認),基於雪花算法的策略生成數據id,與資料庫id是否設置自增無關
    • @TableId(type=IdType.AUTO),使用資料庫的自增策略,注意,該類型請確保資料庫設置了id自增,
      否則無效
    • 設置統一(每張表)的主鍵生成策略,id-type
  • @TableField
    • mybatis會自動將資料庫裏面的欄位字串,如果有底線,當轉換成資料表時,會進行駝峰轉換,ex: 資料庫裡面user_name,轉換成程式裡面的資料表時,可以取名為userName
    • 可利用 @TableField 於程式碼裡面指定所對應的資料庫欄位
  • @TableLogic
    • 邏輯刪除,針對某個欄位設定為0、1,在select時,針對這欄位去判斷,來讓資料感覺上被刪除了,但實際上資料還存在
    • 呼叫查詢方法時,會只查詢此欄位為0的資料,呼叫刪除方法時,只會將此欄位從0變成1,不會真的將資料刪除


8、條件構造器 Wrapper

  • Wrapper : 條件構造抽像類,最頂端父類

    • Abstract Wrapper : 用於查詢條件封裝,生成 sql 的 where 條件
      • QueryWrapper : 查詢條件封裝
      • UpdateWrapper : Update 條件封裝
      • AbstractLambdaWrapper : 使用Lambda 語法
        • LambdaQueryWrapper :用於Lambda語法使用的查詢Wrapper
        • LambdaUpdateWrapper : Lambda 更新封裝Wrapper
  • 使用queryWrapper來查詢user中條件為: name字串中有”B”,age>20,age<30,email不為空

    • SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      @Test
      public void testWraper(){
      QueryWrapper<User> queryWrapper = new QueryWrapper<>();
      queryWrapper.like("name", "B")
      .between("age",20,30)
      .isNotNull("email");
      List<User> list = userMapper.selectList(queryWrapper);
      list.forEach(System.out::println);
      }
  • 藉由lambda來設定條件的優先級,將用戶名中包含有a並且(年齡大於20或郵箱為null)的用戶信息修改

    • UPDATE user SET age=?, email=? WHERE (name LIKE ? AND (age > ? OR email IS NULL))
    • lambda表達式內的邏輯優先運算
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @Test
      public void testWraper() {
      QueryWrapper<User> queryWrapper = new QueryWrapper<>();
      queryWrapper.like("name", "a")
      .and(i -> i.gt("age", 20).or().isNull("email"));
      User user = new User();
      user.setAge(18);
      user.setEmail("user@gmail.com");
      int result = userMapper.update(user, queryWrapper);
      System.out.println("受影響的行數:" + result);
      }
  • 藉由select,只查詢特定的欄位,查詢用戶信息的name和age字段

    • SELECT name,age FROM t_user
      1
      2
      3
      4
      5
      6
      7
      8
      @Test
      public void testWraper() {
      QueryWrapper<User> queryWrapper = new QueryWrapper<>();
      queryWrapper.select("name", "age");
      //selectMaps()返回Map集合列表,通常配合select()使用,避免User對像中沒有被查詢到的列值為null
      List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
      maps.forEach(System.out::println);
      }
  • 藉由inSql,實現子查詢,查詢id小於等於3的用戶信息

    • SELECT id,name,age,email,is_deleted FROM user WHERE (id IN (select id from user where id <= 3))
      1
      2
      3
      4
      5
      6
      7
      @Test
      public void testWraper() {
      QueryWrapper<User> queryWrapper = new QueryWrapper<>();
      queryWrapper.inSql("id", "select id from user where id <= 3");
      List<User> list = userMapper.selectList(queryWrapper);
      list.forEach(System.out::println);
      }
  • UpdateWrapper,可直接在updateWrapper進行值的設定

    • 將(年齡大於20或郵箱為null)並且用戶名中包含有a的用戶信息修改
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @Test
      @Test
      public void testWraper() {
      UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
      updateWrapper.set("age", 18)
      .set("email", "user@gmail.com")
      .like("name", "a")
      .and(i -> i.gt("age", 20).or().isNull("email"));
      int result = userMapper.update(null, updateWrapper);
      System.out.println(result);
      }
  • condition,在真正開發的過程中,組裝條件是常見的功能,而這些條件數據來源於用戶輸入,是可選的,因此我們在組裝這些條件時,必須先判斷用戶是否選擇了這些條件,若選擇則需要組裝該條件,若沒有選擇則一定不能組裝,以免影響SQL執行的結果

    • SELECT id,name,age,email,is_deleted FROM user WHERE (age >= ? AND age <= ?)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      @Test
      public void testWraper() {
      //定義查詢條件,有可能為null(用戶未輸入或未選擇)
      String name = null;
      Integer ageBegin = 10;
      Integer ageEnd = 24;
      QueryWrapper<User> queryWrapper = new QueryWrapper<>();
      //StringUtils.isNotBlank()判斷某字符串是否不為空且長度不為0且不由空白符(whitespace)構成
      queryWrapper.like(StringUtils.isNotBlank(name), "name", "a")
      .ge(ageBegin != null, "age", ageBegin)
      .le(ageEnd != null, "age", ageEnd);
      List<User> users = userMapper.selectList(queryWrapper);
      users.forEach(System.out::println);
      }
  • LambdaQueryWrapper,避免再填寫表的字串名時打錯,可用Lambda來替代

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Test
    public void testWraper() {
    //定義查詢條件,有可能為null(用戶未輸入)
    String name = "a";
    Integer ageBegin = 10;
    Integer ageEnd = 24;
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    //避免使用字符串表示字段,防止運行時錯誤
    queryWrapper.like(StringUtils.isNotBlank(name), User::getName, name)
    .ge(ageBegin != null, User::getAge, ageBegin)
    .le(ageEnd != null, User::getAge, ageEnd);
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
    }
  • LambdaUpdateWrapper,避免再填寫表的字串名時打錯,可用Lambda來替代

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Test
    public void test10() {
    //組裝set子句
    //lambda,表達式內的邏輯優先運算
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.set(User::getAge, 18)
    .set(User::getEmail, "user@gmail.com")
    .like(User::getName, "a")
    .and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail));
    User user = new User();
    int result = userMapper.update(user, updateWrapper);
    System.out.println("受影響的行數:" + result);
    }

9、分頁插件

  • MyBatisPlus自帶分頁插件,配置後可實現分頁功能
  • 插件的原理是藉由先將對應的資料查詢出來,再藉由攔截器去過濾資料,攔截器中可配置多個插件
  • 分頁插件就是將查詢出來的數據藉由limit去過濾每頁顯示的數據
  • Page page = new Page(1,3);
    • 第一個參數為第幾頁,第二個參數為一頁顯示幾個
    • (頁數-1)*3 => 每頁的起始index
  • 添加配置類
    • 建立一個MyBatisPlus攔截器,並設定分頁插件
    • interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test(){
Page<User> page = new Page(1,3);
userMapper.selectPage(page, null);
System.out.println("當前頁的資料: " + page.getRecords());
System.out.println("當前第幾頁: " + page.getCurrent());
System.out.println("顯示筆數: " + page.getSize());
System.out.println("總共幾頁: " + page.getPages());
System.out.println("總紀錄數: " + page.getTotal());
System.out.println("有無上一頁: " + page.hasPrevious());
System.out.println("有無下一頁: " + page.hasNext());
}
  • 自定義方法的分頁插件
    • 自定義方法的返回類型必須是Page
    • 自定義方法的第一個參數必須是Page
    • 於XML配置SQL語法
    • 在mapper.xml檔中的resultMap的type或者parameterType會用到自定義的POJO
    • 用mybatis.type-aliases-package來指定POJO掃描包來讓mybatis自動掃描到自定義的POJO