欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis使用useGeneratedKeys獲取自增主鍵的方法

 更新時間:2019年09月07日 15:53:22   作者:stoneFang  
這篇文章主要給大家介紹了關于Mybatis使用useGeneratedKeys獲取自增主鍵的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Mybatis具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧

摘要

我們經(jīng)常使用useGenerateKeys來返回自增主鍵,避免多一次查詢。也會經(jīng)常使用on duplicate key update,來進行insertOrUpdate,來避免先query 在insert/update。用起來很爽,但是經(jīng)常踩坑,還不知為何。本篇就是深入分析獲取自增主鍵的原理。

問題

首先摘兩段我司一些老代碼的bug

批量插入用戶收藏

for (tries = 0; tries < MAX_RETRY; tries++) {
 final int result = collectionMapper.insertCollections(collections);
 if (result == collections.size()) {
  break;
 }
}
if (tries == MAX_RETRY) {
 throw new RuntimeSqlException("Insert collections error");
}
// 依賴數(shù)據(jù)庫生成的collectionid
return collections;

collectionMapper.insertCollections 方法

<insert id="insertCollections" parameterType="list" useGeneratedKeys="true"
  keyProperty="collectionId">
 INSERT INTO collection(
 userid, item
 )
 VALUES
 <foreach collection="list" item="collection" separator=",">
  (#{collection.userId}, #{collection.item})
 </foreach>
 ON DUPLICATE KEY UPDATE
 status = 0
</insert>

不知道大家能不能發(fā)現(xiàn)其中的問題

分析

問題有兩個

返回值result的判斷錯誤

使用on duplicate key 批量update返回影響的行數(shù)是和插入的數(shù)不一樣的。犯這種錯主要在于想當然,不看文檔

看下官網(wǎng)文檔

寫的很清楚

With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values. If you specify the CLIENT_FOUND_ROWS flag to the mysql_real_connect() C API function when connecting to mysqld, the affected-rows value is 1 (not 0) if an existing row is set to its current values.

返回值有三種

0: 沒有更新 1 :insert 2. update

還有一個特殊情況,update 一個相同值到原來的值,這個根據(jù)客戶端配置,可能為0,可能為1。

所以這個判斷明顯錯誤

利用批量InsertOrUpdate的userGeneratedKey來返回自增主鍵

這個問題批量插入時有update語句時,就會發(fā)現(xiàn)有問題。返回的自增主鍵都是錯的,這是為什么呢?

1. 首先我們看下mybatis對于useGeneratedKey的描述

>This tells MyBatis to use the JDBC getGeneratedKeys method to retrieve keys generated internally by the database (e.g. auto increment fields in RDBMS like MySQL or SQL Server). Default: false.

就是使用JDBC的getGeneratedKeys的方法來獲取的。

2. 我們再找下JDBC的規(guī)范

Before version 3.0 of the JDBC API, there was no standard way of retrieving key values from databases that supported auto increment or identity columns. With older JDBC drivers for MySQL, you could always use a MySQL-specific method on the Statement interface, or issue the query SELECT LAST_INSERT_ID() after issuing an INSERT to a table that had an AUTO_INCREMENT key. Using the MySQL-specific method call isn't portable, and issuing a SELECT to get the AUTO_INCREMENT key's value requires another round-trip to the database, which isn't as efficient as possible. The following code snippets demonstrate the three different ways to retrieve AUTO_INCREMENT values. First, we demonstrate the use of the new JDBC 3.0 method getGeneratedKeys() which is now the preferred method to use if you need to retrieve AUTO_INCREMENT keys and have access to JDBC 3.0. The second example shows how you can retrieve the same value using a standard SELECT LAST_INSERT_ID() query. The final example shows how updatable result sets can retrieve the AUTO_INCREMENT value when using the insertRow() method.

意思就是JDBC3.0以前,有些亂七八糟的定義的,沒有統(tǒng)一,之后統(tǒng)一成了getGeneratedKeys()方法。兩邊是一致的。實現(xiàn)的原理主要就是數(shù)據(jù)庫端返回一個LAST_INSERT_ID。這個跟auto_increment_id強相關。

我們看下auto_increment_id的定義。重點關注批量插入

For a multiple-row insert, LAST_INSERT_ID() and mysql_insert_id() actually return the AUTO_INCREMENT key from the first of the inserted rows. This enables multiple-row inserts to be reproduced correctly on other servers in a replication setup.

批量插入的時候只會返回一個id,這個id值是第一個插入行的AUTO_INCREMENT值。至于為什么這么干,能夠使得mysql-server在master-slave架構下也能保證id值統(tǒng)一的原因可以看下這篇。本篇文章就不展開了。

那么mysql server只返回一個id,客戶端批量插入的時候怎么能實現(xiàn)獲取全部的id呢

3. 客戶端的實現(xiàn)

我們看下客戶端getGeneratedKeys的實現(xiàn)。

JDBC com.mysql.jdbc.StatementImpl

public synchronized ResultSet getGeneratedKeys() throws SQLException {
  if (!this.retrieveGeneratedKeys) {
   throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), "S1009", this.getExceptionInterceptor());
  } else if (this.batchedGeneratedKeys == null) {
   // 批量走這邊的邏輯
   return this.lastQueryIsOnDupKeyUpdate ? this.getGeneratedKeysInternal(1) : this.getGeneratedKeysInternal();
  } else {
   Field[] fields = new Field[]{new Field("", "GENERATED_KEY", -5, 17)};
   fields[0].setConnection(this.connection);
   return ResultSetImpl.getInstance(this.currentCatalog, fields, new RowDataStatic(this.batchedGeneratedKeys), this.connection, this, false);
  }
 }

看下調用的方法 this.getGeneratedKeysInternal()

protected ResultSet getGeneratedKeysInternal() throws SQLException {
    // 獲取影響的行數(shù)
    int numKeys = this.getUpdateCount();
    return this.getGeneratedKeysInternal(numKeys);
  }

這里有個重要知識點了,首先獲取本次批量插入的影響行數(shù),然后再執(zhí)行具體的獲取id操作。

getGeneratedKeysInternal方法

protected synchronized ResultSet getGeneratedKeysInternal(int numKeys) throws SQLException {
    Field[] fields = new Field[]{new Field("", "GENERATED_KEY", -5, 17)};
    fields[0].setConnection(this.connection);
    fields[0].setUseOldNameMetadata(true);
    ArrayList rowSet = new ArrayList();
    long beginAt = this.getLastInsertID();
    // 按照受影響的范圍+遞增步長
    for(int i = 0; i < numKeys; ++i) {
       if (beginAt > 0L) {
            // 值塞進去
            row[0] = StringUtils.getBytes(Long.toString(beginAt));
          }
      beginAt += (long)this.connection.getAutoIncrementIncrement();
    }
}

迭代影響的行數(shù),然后依次獲取id。

所以批量insert是正確可以返回的。

但是批量insertOrUpdate就有問題了,批量insertOrUpdate的影響行數(shù)不是插入的數(shù)據(jù)行數(shù),可能是0,1,2這樣就導致了自增id有問題了。

比如插入3條數(shù)據(jù),2條會update,1條會insert,這時候updateCount就是5,generateid就會5個了,mybatis然后取前3個塞到數(shù)據(jù)里,顯然是錯的。

以上是原理分析,如果想了解更詳細的實驗結果,可以看下實驗

總結

批量insert

<insert id="insertAuthor" useGeneratedKeys="true"
  keyProperty="id">
 insert into Author (username, password, email, bio) values
 <foreach item="item" collection="list" separator=",">
  (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
 </foreach>
</insert>

來自官網(wǎng)的例子,mapper中不能指定@Param參數(shù),否則會有問題

批量insertOrUpdate

不能依賴useGeneratedKey返回主鍵。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。

相關文章

  • 利用Java反射機制實現(xiàn)對象相同字段的復制操作

    利用Java反射機制實現(xiàn)對象相同字段的復制操作

    這篇文章主要介紹了利用Java反射機制實現(xiàn)對象相同字段的復制操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • SpringBoot整合SpringTask實現(xiàn)定時任務的流程

    SpringBoot整合SpringTask實現(xiàn)定時任務的流程

    這篇文章主要介紹了SpringBoot整合SpringTask實現(xiàn)定時任務的流程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • mybatis使用foreach遍歷list集合或者array數(shù)組方式

    mybatis使用foreach遍歷list集合或者array數(shù)組方式

    這篇文章主要介紹了mybatis使用foreach遍歷list集合或者array數(shù)組方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java實現(xiàn)網(wǎng)站微信掃碼支付

    java實現(xiàn)網(wǎng)站微信掃碼支付

    這篇文章主要為大家詳細介紹了java實現(xiàn)網(wǎng)站微信掃碼支付,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • Java的MyBatis框架中XML映射緩存的使用教程

    Java的MyBatis框架中XML映射緩存的使用教程

    MyBatis程序在做好XML映射后能夠有緩存的功能,這樣映射過SQL語句的配置以后就可以拿過來直接用了,這里我們來一起總結一下Java的MyBatis框架中XML映射緩存的使用教程
    2016-06-06
  • Java WeakHashMap案例詳解

    Java WeakHashMap案例詳解

    這篇文章主要介紹了Java WeakHashMap案例詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-08-08
  • 深入了解java中的逃逸分析

    深入了解java中的逃逸分析

    這篇文章主要介紹了深入了解java中的逃逸分析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • MyBatis映射文件中的動態(tài)SQL實例詳解

    MyBatis映射文件中的動態(tài)SQL實例詳解

    在本文中,我們深入探討了動態(tài)SQL的各種標簽,包括<if>、<choose>、<trim>、<foreach>等,通過實際的例子演示了它們的用法,感興趣的朋友一起揭開動態(tài)SQL的神秘面紗,帶你領略它的魅力
    2024-01-01
  • 還在用if(obj!=null)做非空判斷,帶你快速上手Optional

    還在用if(obj!=null)做非空判斷,帶你快速上手Optional

    這篇文章主要介紹了還在用if(obj!=null)做非空判斷,帶你快速上手Optional,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • Java 關鍵字static詳解及實例代碼

    Java 關鍵字static詳解及實例代碼

    這篇文章主要介紹了Java 關鍵字static詳解及實例代碼的相關資料,需要的朋友可以參考下
    2017-04-04

最新評論