在前面有一篇部落格: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; }
-
最終建立的專案結構為:
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檔案下載下來,我們就能看到具體的註釋內容。因為這個技術是國人制作的,所以他程式碼中的註釋還是比較容易看懂的。
當把原始碼下載完後,就可以看到如下內容:
從原始碼中可以看到,除了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大小整數,它的結構如下圖:
-
1bit,不用,因為二進位制中最高位是符號位,1表示負數,0表示正數。生成的id一般都是用整數,所以最高位固定為0。
-
41bit-時間戳,用來記錄時間戳,毫秒級
-
10bit-工作機器id,用來記錄工作機器id,其中高位5bit是資料中心ID其取值範圍0-31,低位5bit是工作節點ID其取值範圍0-31,兩個組合起來最多可以容納1024個節點
-
序列號佔用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的策略已經介紹完,但是如果要在專案中的每一個模型類上都需要使用相同的生成策略,如:
確實是稍微有點繁瑣,我們能不能在某一處進行配置,就能讓所有的模型類都可以使用該主鍵ID策略呢?
答案是肯定有,我們只需要在配置檔案中新增如下內容:
mybatis-plus:
global-config:
db-config:
id-type: assign_id
配置完成後,每個模型類的主鍵ID策略都將成為assign_id.
資料庫表與模型類的對映關係
MyBatisPlus會預設將模型類的類名名首字母小寫作為表名使用,假如資料庫表的名稱都以tbl_
開頭,那麼我們就需要將所有的模型類上新增@TableName
,如:
配置起來還是比較繁瑣,簡化方式為在配置檔案中配置如下內容:
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
設定表的字首內容,這樣MyBatisPlus就會拿 tbl_
加上模型類的首字母小寫,就剛好組裝成資料庫的表名。