MybatisPlus中的insert操作詳解

MybatisPlus insert操作

在測試之前,我們思考一個問題,上個入門案例中,我們什麼sql語句代碼都沒寫,但也能查詢出來數據。

是誰幫我們做瞭寫基本代碼的事情?肯定是MybatisPlus。

為瞭驗證並繼續向下學習,我們開啟日志,打印在控制臺上。

1、開啟日志

隻需在yml配置文件中,寫上:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2、測試插入的代碼

    @Test
    void testInsert() {
        UserEntity userEntity = new UserEntity();
        userEntity.setName("pipizhen");
        userEntity.setAge(10);
        userEntity.setEmail("[email protected]");
        int count = userMapper.insert(userEntity);

        System.out.println(count);
        System.out.println(userEntity);
    }

控制臺輸出:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2373ad99] was not registered for synchronization because synchronization is not active
2020-11-23 14:13:12.748  INFO 7392 — [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 – Starting…
2020-11-23 14:13:13.028  INFO 7392 — [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 – Start completed.
JDBC Connection [HikariProxyConnection@1977652583 wrapping com.mysql.cj.jdbc.ConnectionImpl@2a334bac] will not be managed by Spring
==>  Preparing: INSERT INTO tbl_user ( id, name, email, age ) VALUES ( ?, ?, ?, ? ) 
==> Parameters: 1330756266048045058(Long), pipizhen(String), [email protected](String), 10(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2373ad99]
1
UserEntity(id=1330756266048045058, name=pipizhen, age=10, [email protected])

說明我們插入數據成功瞭,細心的人會發現我們並沒有指定id,但插入成功後,我們發現對象存在id。

肯定是主鍵自動生成的,沒錯,但是怎麼生成一個這麼大的數字呢?為什麼不是在原有的記錄條數id在自增1呢?

這裡數據庫插入的id的默認值為:全局唯一id。

全局唯一id可自行百度:分佈式系統唯一id生成。

3、MybatisPlus使用的是雪花算法

原理:Twitter的雪花算法SnowFlake,使用Java語言實現。

可以瞭解一下:

SnowFlake算法產生的ID是一個64位的整型,結構如下(每一部分用“-”符號分隔):

0 – 0000000000 0000000000 0000000000 0000000000 0 – 00000 – 00000 – 000000000000

  • 1位標識部分,在java中由於long的最高位是符號位,正數是0,負數是1,一般生成的ID為正數,所以為0;
  • 41位時間戳部分,這個是毫秒級的時間,一般實現上不會存儲當前的時間戳,而是時間戳的差值(當前時間-固定的開始時間),這樣可以使產生的ID從更小值開始;41位的時間戳可以使用69年,(1L << 41) / (1000L606024365) = 69年;
  • 10位節點部分,Twitter實現中使用前5位作為數據中心標識,後5位作為機器標識,可以部署1024個節點;
  • 12位序列號部分,支持同一毫秒內同一個節點可以生成4096個ID;

SnowFlake算法生成的ID大致上是按照時間遞增的,用在分佈式系統中時,需要註意數據中心標識和機器標識必須唯一,這樣就能保證每個節點生成的ID都是唯一的。或許我們不一定都需要像上面那樣使用5位作為數據中心標識,5位作為機器標識,可以根據我們業務的需要,靈活分配節點部分,如:若不需要數據中心,完全可以使用全部10位作為機器標識;若數據中心不多,也可以隻使用3位作為數據中心,7位作為機器標識。

snowflake生成的ID整體上按照時間自增排序,並且整個分佈式系統內不會產生ID碰撞(由datacenter和workerId作區分),並且效率較高。據說:snowflake每秒能夠產生26萬個ID。

4、MybatisPlus中的主鍵生成策略

我們可以在@TableId註解中發現有個屬性IdType,這是一個枚舉類。

舊版本的枚舉值有AUTO, NONE, INPUT, ID_WORKER, UUID, ID_WORKER_STR;

新版本又增加瞭兩種,ASSIGN_ID,ASSIGN_UUID。

舊版本當我們不指定主鍵生成類型時,即值為null時,默認使用ID_WORKER類型。

新版本默認為NONE,註解裡等於跟隨全局,全局裡約等於 INPUT。

(1)AUTO:數據庫ID自增。

(2)NONE:無狀態,該類型為未設置主鍵類型(註解裡等於跟隨全局,全局裡約等於 INPUT)。

(3)INPUT:insert前自行set主鍵值,即我們插入前,需要手動設置id。

(4)ASSIGN_ID:分配ID(主鍵類型為Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默認實現類為DefaultIdentifierGenerator雪花算法)。

(5)ASSIGN_UUID:分配UUID,主鍵類型為String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默認default方法)

(6)ID_WORKER:分佈式全局唯一ID 長整型類型(please use ASSIGN_ID)。

(7)UUID:32位UUID字符串(please use ASSIGN_UUID)。

(8)ID_WORKER_STR 分佈式全局唯一ID 字符串類型(please use ASSIGN_ID)

註意:最後三個在最新版本中,ID_WORKER,UUID,ID_WORKER_STR已經被遺棄瞭,不建議使用。

5、測試不同的主鍵生成策略

(1)AUTO策略

我們修改實體類中的id註解為:@TableId(value = “id”, type = IdType.AUTO)

並修改數據庫中表tbl_user的id確定為自增。不然啟動會報錯:Field ‘id’ doesn’t have a default value.

測試之前,我們還需把表中那個id好長的記錄刪掉,並重啟MySQL軟件,我的是Navicat。目的防止緩存的影響。

測試程序還是上面那幾行代碼:

@Test
    void testInsert() {
        UserEntity userEntity = new UserEntity();
        userEntity.setName("pipizhen");
        userEntity.setAge(10);
        userEntity.setEmail("[email protected]");
        int count = userMapper.insert(userEntity);
        System.out.println(count);
        System.out.println(userEntity);
    }

控制臺的部分打印為:

1
UserEntity(id=6, name=pipizhen, age=10, [email protected])

我們發現確實是我們熟悉的id自增1。

(2)INPUT策略

需要我們手動設置id的值,這樣設置時,當數據庫表的id設置瞭自增,插入時可設置id,也可不設置id。

當數據庫表id沒有設置自增,那我們插入數據時就必須設置id,不然誰來幫我們設置id的值,大傢都知道id存在主鍵約束。

(3)ASSIGN_ID策略

也是自動生成一個很長的Long型數字。

可以自己嘗試測試一下,隻需改變實體類中註解中的IdType屬性值。

MybatisPlus坑insert方法

有天早上我的一個同事,突然跑來告訴我。我們某張表的自增ID變得很大。類似1173776258468638722 這種。這個當然是不能接受的啊。

著手解決

然後就開始找問題的原因,一開始我想的是數據庫上的問題,我刪掉不合理的數據,

alter table *** AUTO_INCREMENT=20

修改自增ID從20開始。手動插入數據,居然OK。

那就說明,可能是我們代碼insert數據的時候存在的問題。我找到數據庫訪問層的insert語句處,發現使用的是mybatis-plus,網上查瞭一下關於這塊的東西,發現insert方法在配置的時候,可以指定自增ID的方式。

源碼中定義有以下幾種

public enum IdType {
    AUTO(0, "數據庫ID自增"),
    INPUT(1, "用戶輸入ID"),
    ID_WORKER(2, "全局唯一ID"),
    UUID(3, "全局唯一ID"),
    NONE(4, "該類型為未設置主鍵類型"),
    ID_WORKER_STR(5, "字符串全局唯一ID");

然後我就果斷手動配置瞭一下,

    @TableId(type = IdType.AUTO)
    private Long userId;

重啟測試,OK。

也是很奇怪為什麼之前的那部分數據的自增ID都是沒問題的,突然出現這個,也是很困惑

總結,出現這個問題的原因,還是因為自己技術不熟練啦~~

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: