Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置

|舊市拾荒|發表於2022-12-03

在前面有一篇部落格:Java開發學習(四十一)----MyBatisPlus標準資料層(增刪查改分頁)開發我們在新增的時候留了一個問題,就是新增成功後,主鍵ID是一個很長串的內容。Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置

我們更想要的是按照資料庫表欄位進行自增長,在解決這個問題之前,我們先來分析下ID該如何選擇:

  • 不同的表應用不同的id生成策略

    • 日誌:自增(1,2,3,4,……)

    • 購物訂單:特殊規則(FQ23948AK3843)

    • 外賣單:關聯地區日期等資訊(10 04 20200314 34 91)

    • 關係表:可省略id

    • ……

不同的業務採用的ID生成方式應該是不一樣的,那麼在MyBatisPlus中都提供了哪些主鍵生成策略,以及我們該如何進行選擇?

在這裡我們又需要用到MyBatisPlus的一個註解叫@TableId

知識點1:@TableId

名稱 @TableId
型別 屬性註解
位置 模型類中用於表示主鍵的屬性定義上方
作用 設定當前類中主鍵屬性的生成策略
相關屬性 value(預設):設定資料庫表主鍵名稱 type:設定主鍵屬性的生成策略,值查照IdType的列舉值

1、環境構建

在構建條件查詢之前,我們先來準備下環境

  • 建立一個SpringBoot專案

    參考Java開發學習(三十五)----SpringBoot快速入門及起步依賴解析

  • pom.xml中新增對應的依賴

    <?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.5.0</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.itheima</groupId>
        <artifactId>mybatisplus_03_dml</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
    ​
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.1</version>
            </dependency>
    ​
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    ​
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
    ​
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
    ​
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    ​
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
    ​
        </dependencies>
    ​
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    ​
    </project>
    ​
  • 編寫UserDao介面

    @Mapper
    public interface UserDao extends BaseMapper<User> {
    }
  • 編寫模型類

    @Data
    @TableName("tbl_user")
    public class User {
        private Long id;
        private String name;
        @TableField(value="pwd",select=false)
        private String password;
        private Integer age;
        private String tel;
        @TableField(exist=false)
        private Integer online;
    }
  • 編寫引導類

    @SpringBootApplication
    public class Mybatisplus03DqlApplication {
    ​
        public static void main(String[] args) {
            SpringApplication.run(Mybatisplus03DqlApplication.class, args);
        }
    ​
    }
  • 編寫配置檔案

    # dataSource
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
        username: root
        password: root
    # mp日誌
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  • 編寫測試類

    @SpringBootTest
    class Mybatisplus02DqlApplicationTests {
    ​
        @Autowired
        private UserDao userDao;
        
        @Test
        void testGetAll(){
            List<User> userList = userDao.selectList(null);
            System.out.println(userList);
        }
    }
  • 測試

    @SpringBootTest
    class Mybatisplus03DqlApplicationTests {
    ​
        @Autowired
        private UserDao userDao;
        
    }
  • 最終建立的專案結構為:

    Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置

2、程式碼演示

AUTO策略
@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

會發現,新增成功,並且主鍵id自動增長

我們會發現AUTO的作用是使用資料庫ID自增,在使用該策略的時候一定要確保對應的資料庫表設定了ID主鍵自增,否則無效。

接下來,我們可以進入原始碼檢視下ID的生成策略有哪些?開啟原始碼後,你會發現並沒有看到中文註釋,這就需要我們點選右上角的Download Sources,會自動幫你把這個類的java檔案下載下來,我們就能看到具體的註釋內容。因為這個技術是國人制作的,所以他程式碼中的註釋還是比較容易看懂的。

當把原始碼下載完後,就可以看到如下內容:

Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置

從原始碼中可以看到,除了AUTO這個策略以外,還有如下幾種生成策略:

  • NONE: 不設定id生成策略

  • INPUT:使用者手工輸入id

  • ASSIGN_ID:雪花演算法生成id(可相容數值型與字串型)

  • ASSIGN_UUID:以UUID生成演算法作為id生成策略

  • 其他的幾個策略均已過時,都將被ASSIGN_ID和ASSIGN_UUID代替掉。

擴充:

分散式ID是什麼?

  • 當資料量足夠大的時候,一臺資料庫伺服器儲存不下,這個時候就需要多臺資料庫伺服器進行儲存

  • 比如訂單表就有可能被儲存在不同的伺服器上

  • 如果用資料庫表的自增主鍵,因為在兩臺伺服器上所以會出現衝突

  • 這個時候就需要一個全域性唯一ID,這個ID就是分散式ID。

INPUT策略
步驟1:設定生成策略為INPUT
@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}

注意:這種ID生成策略,需要將表的自增策略刪除掉

步驟2:新增資料手動設定ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testSave(){
        User user = new User();
        //設定主鍵ID的值
        user.setId(666L);
        user.setName("黑馬程式設計師");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
    }
}
ASSIGN_ID策略
步驟1:設定生成策略為ASSIGN_ID
@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
步驟2:新增資料不設定ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testSave(){
        User user = new User();
        user.setName("黑馬程式設計師");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
    }
}

注意:這種生成策略,不需要手動設定ID,如果手動設定ID,則會使用自己設定的值。

生成的ID就是一個Long型別的資料。

ASSIGN_UUID策略
步驟1:設定生成策略為ASSIGN_UUID

使用uuid需要注意的是,主鍵的型別不能是Long,而應該改成String型別

@Data
@TableName("tbl_user")
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
}
步驟2:修改表的主鍵型別

主鍵型別設定為varchar,長度要大於32,因為UUID生成的主鍵為32位,如果長度小的話就會導致插入失敗。

步驟3:新增資料不設定ID
@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testSave(){
        User user = new User();
        user.setName("黑馬程式設計師");
        user.setPassword("itheima");
        user.setAge(12);
        user.setTel("4006184000");
        userDao.insert(user);
    }
}

接下來我們來聊一聊雪花演算法:

雪花演算法(SnowFlake),是Twitter官方給出的演算法實現 是用Scala寫的。其生成的結果是一個64bit大小整數,它的結構如下圖:

Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置

  1. 1bit,不用,因為二進位制中最高位是符號位,1表示負數,0表示正數。生成的id一般都是用整數,所以最高位固定為0。

  2. 41bit-時間戳,用來記錄時間戳,毫秒級

  3. 10bit-工作機器id,用來記錄工作機器id,其中高位5bit是資料中心ID其取值範圍0-31,低位5bit是工作節點ID其取值範圍0-31,兩個組合起來最多可以容納1024個節點

  4. 序列號佔用12bit,每個節點每毫秒0開始不斷累加,最多可以累加到4095,一共可以產生4096個ID

3、ID生成策略對比

介紹了這些主鍵ID的生成策略,我們以後該用哪個呢?

  • NONE: 不設定id生成策略,MyBatisPlus不自動生成,約等於INPUT,所以這兩種方式都需要使用者手動設定,但是手動設定第一個問題是容易出現相同的ID造成主鍵衝突,為了保證主鍵不衝突就需要做很多判定,實現起來比較複雜

  • AUTO:資料庫ID自增,這種策略適合在資料庫伺服器只有1臺的情況下使用,不可作為分散式ID使用

  • ASSIGN_UUID:可以在分散式的情況下使用,而且能夠保證唯一,但是生成的主鍵是32位的字串,長度過長佔用空間而且還不能排序,查詢效能也慢

  • ASSIGN_ID:可以在分散式的情況下使用,生成的是Long型別的數字,可以排序效能也高,但是生成的策略和伺服器時間有關,如果修改了系統時間就有可能導致出現重複主鍵

  • 綜上所述,每一種主鍵策略都有自己的優缺點,根據自己專案業務的實際情況來選擇使用才是最明智的選擇。

4、簡化配置

模型類主鍵策略設定

對於主鍵ID的策略已經介紹完,但是如果要在專案中的每一個模型類上都需要使用相同的生成策略,如:

Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置

確實是稍微有點繁瑣,我們能不能在某一處進行配置,就能讓所有的模型類都可以使用該主鍵ID策略呢?

答案是肯定有,我們只需要在配置檔案中新增如下內容:

mybatis-plus:
  global-config:
    db-config:
        id-type: assign_id

配置完成後,每個模型類的主鍵ID策略都將成為assign_id.

資料庫表與模型類的對映關係

MyBatisPlus會預設將模型類的類名名首字母小寫作為表名使用,假如資料庫表的名稱都以tbl_開頭,那麼我們就需要將所有的模型類上新增@TableName,如:

Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置

配置起來還是比較繁瑣,簡化方式為在配置檔案中配置如下內容:

mybatis-plus:
  global-config:
    db-config:
        table-prefix: tbl_

設定表的字首內容,這樣MyBatisPlus就會拿 tbl_加上模型類的首字母小寫,就剛好組裝成資料庫的表名。

 

 

相關文章