亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Mybatis獲取自增主鍵的兩種實現原理小結

 更新時間:2025年08月29日 10:39:38   作者:靈魂獵手  
MyBatis通過useGeneratedKeys和兩種方式適配不同數據庫的自增主鍵生成,利用JDBC接口和KeyGenerator實現高效獲取與回寫,下面就來介紹一下

一、自增主鍵的獲取方式

自增主鍵是數據庫常見的主鍵生成策略(如MySQL的AUTO_INCREMENT、Oracle 的序列等)。MyBatis針對不同數據庫的特性,提供了靈活的自增主鍵獲取方案,核心分為兩類:依賴數據庫原生自增機制的 useGeneratedKeys方式,以及通過顯式SQL查詢獲取的 <selectKey> 方式。

1. MySQL 的兩種獲取方式

MySQL支持AUTO_INCREMENT自增列,MyBatis 提供兩種方式獲取其生成的主鍵:

(1)useGeneratedKeys方式(推薦)

這是最簡單的方式,利用JDBC的 Statement.getGeneratedKeys() 接口直接獲取數據庫生成的主鍵,無需手動編寫查詢邏輯。

配置示例(XML 映射)

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
  INSERT INTO user (username, age) VALUES (#{username}, #{age})
</insert>
  • useGeneratedKeys="true":開啟自增主鍵獲取功能。
  • keyProperty="id":指定 Java 實體類中接收主鍵的屬性(如 User 類的 id 字段)。

(2)<selectKey>方式

通過顯式執(zhí)行查詢SQL來獲取主鍵,MySql也支持插入后通過LAST_INSERT_ID函數獲取剛生成的主鍵:

配置示例

<insert id="insertUser">
  <!-- order="AFTER" 表示插入后執(zhí)行查詢 -->
  <selectKey keyProperty="id" resultType="java.lang.Long" order="AFTER">
    SELECT LAST_INSERT_ID AS id -- MySQL 專用函數,返回當前會話最后生成的自增主鍵
  </selectKey>
  INSERT INTO user (username, age) VALUES (#{username}, #{age})
</insert>
  • order="AFTER":由于 MySQL 自增主鍵在插入后生成,因此需設置為 AFTER。
  • SELECT LAST_INSERT_ID:MySQL 提供的函數,用于獲取當前會話中最近一次插入生成的自增主鍵。

PS:LAST_INSERT_ID是個函數,后面要有()的,但是似乎觸發(fā)了掘金的什么feature,加上括號后會被轉成0

2. Oracle 的獲取方式

Oracle 沒有內置的自增列機制,通常通過序列(Sequence) 生成主鍵。MyBatis 需通過 <selectKey>標簽先查詢序列值,再將其作為主鍵插入。

配置示例

    <insert id="insertUser">
      <!-- order="BEFORE" 表示插入前先查詢序列 -->
      <selectKey keyProperty="id" resultType="java.lang.Long" order="BEFORE">
        SELECT USER_SEQ.NEXTVAL FROM DUAL -- 查詢序列的下一個值
      </selectKey>
      INSERT INTO user (id, username, age) VALUES (#{id}, #{username}, #{age})
    </insert>
  • order="BEFORE":由于 Oracle 需先獲取序列值作為主鍵,再執(zhí)行插入,因此需設置為 BEFORE
  • USER_SEQ.NEXTVAL:Oracle 序列的下一個值,作為主鍵傳入 INSERT 語句。

二、源碼解析:自增主鍵的實現原理

MyBatis自增主鍵的核心邏輯由 KeyGenerator 接口及其實現類完成,結合MappedStatementStatementHandler等組件,實現主鍵的生成、獲取與回寫。

1. 核心接口:KeyGenerator

KeyGenerator 是自增主鍵處理的核心接口,定義了主鍵生成的兩個關鍵時機(插入前 / 后):

public interface KeyGenerator {
  // 插入操作執(zhí)行前調用(如 Oracle 序列查詢)
  void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
  
  // 插入操作執(zhí)行后調用(如 MySQL 自增主鍵獲?。?
  void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}

MyBatis 提供兩個核心實現類:Jdbc3KeyGenerator(處理 useGeneratedKeys 方式)和 SelectKeyGenerator(處理 <selectKey> 方式)。

2.useGeneratedKeys方式的實現(Jdbc3KeyGenerator)

當解析 Mapper 配置時,useGeneratedKeys 等屬性會被封裝到 MappedStatement 中,并初始化 Jdbc3KeyGenerator

(1)創(chuàng)建PreparedStatement時設置主鍵返回標志

執(zhí)行插入時,PreparedStatementHandler 會根據 MappedStatement 的配置,創(chuàng)建帶有 RETURN_GENERATED_KEYS 標志的 PreparedStatement,告知數據庫返回自增主鍵:

// PreparedStatementHandler.java
@Override
protected Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  PreparedStatement ps;
  String sql = boundSql.getSql();
  
  // 若使用 Jdbc3KeyGenerator,設置 RETURN_GENERATED_KEYS 標志
  if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
    ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
  } else {
    ps = connection.prepareStatement(sql);
  }
  // 設置超時時間等
  return ps;
}

(2)插入后獲取并回寫主鍵(processAfter方法)

插入執(zhí)行后,Jdbc3KeyGenerator 的 processAfter 方法被調用,通過 Statement.getGeneratedKeys() 獲取主鍵,并通過反射回寫到實體類:

@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  processBatch(ms, stmt, getParameters(parameter));
}

public void processBatch(MappedStatement ms, Statement stmt, Collection<Object> parameters) {
  ResultSet rs = null;
  try {
    //核心:使用getGeneratedKeys方法獲取主鍵
    rs = stmt.getGeneratedKeys();
    final Configuration configuration = ms.getConfiguration();
    final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    //獲取參數對象主鍵屬性
    final String[] keyProperties = ms.getKeyProperties();
    final ResultSetMetaData rsmd = rs.getMetaData();
    TypeHandler<?>[] typeHandlers = null;
    if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
      for (Object parameter : parameters) {
        // there should be one row for each statement (also one for each parameter)
        if (!rs.next()) {
          break;
        }
        final MetaObject metaParam = configuration.newMetaObject(parameter);
        if (typeHandlers == null) {
          //獲取主鍵對應的typeHandlers
          typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
        }
        //反射設置到參數中
        populateKeys(rs, metaParam, keyProperties, typeHandlers);
      }
    }
  }
  //省略一些異常處理的代碼和關閉ResultSet的代碼
}

3.<selectKey>方式的實現(SelectKeyGenerator)

SelectKeyGenerator實現了<selectKey>,需要根據order屬性,判斷該在processBefore還是processAfter 中執(zhí)行:

// SelectKeyGenerator.java
@Override
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  if (executeBefore) { // order="BEFORE" 時執(zhí)行
    processGeneratedKeys(executor, ms, parameter);
  }
}

@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
  if (!executeBefore) { // order="AFTER" 時執(zhí)行
    processGeneratedKeys(executor, ms, parameter);
  }
}

private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
        if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
            String[] keyProperties = keyStatement.getKeyProperties();
            final Configuration configuration = ms.getConfiguration();
            final MetaObject metaParam = configuration.newMetaObject(parameter);
            // 創(chuàng)建執(zhí)行器,執(zhí)行主鍵查詢操作
            Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
            // 執(zhí)行查詢主鍵的操作
            List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
            if (values.size() == 0) {
                throw new ExecutorException("SelectKey returned no data.");
            } else if (values.size() > 1) {
                throw new ExecutorException("SelectKey returned more than one value.");
            } else {
                // 創(chuàng)建 MetaObject 對象,訪問查詢主鍵的結果
                MetaObject metaResult = configuration.newMetaObject(values.get(0));
                // 單個主鍵
                if (keyProperties.length == 1) {
                    // 設置屬性到 metaParam 中,相當于設置到 parameter 中
                    if (metaResult.hasGetter(keyProperties[0])) {
                        setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
                    } else {
                        setValue(metaParam, keyProperties[0], values.get(0));
                    }
                // 多個主鍵
                } else {
                    // 遍歷,進行賦值
                    handleMultipleProperties(keyProperties, metaParam, metaResult);
                }
            }
        }
    }
    //省略一些異常處理
}

4. 調用鏈路總結

自增主鍵的處理貫穿 MyBatis 插入操作的全流程,核心鏈路如下:

  1. 用戶調用:sqlSession.insert("insertUser", user)
  2. 進入執(zhí)行器:Executor.update(ms, parameter)(insert 本質是 update 操作)
  3. 創(chuàng)建 StatementHandler:PreparedStatementHandler 根據 MappedStatement 的 keyGenerator 類型,決定是否設置 RETURN_GENERATED_KEYS 標志。
  4. 執(zhí)行插入:Statement.execute() 執(zhí)行 INSERT 語句。
  5. 主鍵處理:
    • 若為 Jdbc3KeyGenerator:調用 processAfter,通過 stmt.getGeneratedKeys() 獲取主鍵并回寫。
    • 若為 SelectKeyGenerator:根據 order 調用 processBefore 或 processAfter,執(zhí)行 <selectKey> 中的 SQL 獲取主鍵并回寫。

三、總結

MyBatis 自增主鍵的獲取本質是適配數據庫特性 + 封裝 JDBC 接口:

  • 對于支持 getGeneratedKeys() 的數據庫(如 MySQL),優(yōu)先使用 useGeneratedKeys 方式,通過 Jdbc3KeyGenerator 直接獲取主鍵,簡潔高效。
  • 對于依賴序列的數據庫(如Oracle),使用 <selectKey> 方式,通過 SelectKeyGenerator 顯式查詢主鍵,靈活兼容。 。

到此這篇關于Mybatis獲取自增主鍵的實現原理小結的文章就介紹到這了,更多相關Mybatis獲取自增主鍵內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • 解決@MapperScan和@Mapper共存之坑XxxMapper?that?could?not?be?found.

    解決@MapperScan和@Mapper共存之坑XxxMapper?that?could?not?be?fo

    這篇文章主要介紹了解決@MapperScan和@Mapper共存之坑XxxMapper?that?could?not?be?found問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java實現的計時器【秒表】功能示例

    Java實現的計時器【秒表】功能示例

    這篇文章主要介紹了Java實現的計時器【秒表】功能,結合實例形式分析了Java結合JFrame框架的計時器功能相關操作技巧,需要的朋友可以參考下
    2019-02-02
  • JPA如何使用findBy方法自定義查詢

    JPA如何使用findBy方法自定義查詢

    這篇文章主要介紹了JPA如何使用findBy方法自定義查詢,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • javaWeb連接數據庫實現簡單登陸注冊功能的全過程

    javaWeb連接數據庫實現簡單登陸注冊功能的全過程

    初學javaWeb,老師留下一小作業(yè),用JAVA實現與服務器端交互,實現登錄和注冊功能,下面這篇文章主要給大家介紹了關于javaWeb連接數據庫實現簡單登陸注冊功能的相關資料,需要的朋友可以參考下
    2022-06-06
  • 掌握Java拼音轉換:pinyin4j庫使用方法及應用價值

    掌握Java拼音轉換:pinyin4j庫使用方法及應用價值

    pinyin4j是一個開源的Java庫,用于將漢字轉換為拼音,它支持將中文字符轉換為標準的全拼形式,并能夠處理多音字和聲調,本文詳細解析pinyin4j庫的核心特性、使用方法及其應用價值,感興趣的朋友一起看看吧
    2025-08-08
  • MyBatis Oracle 自增序列的實現方法

    MyBatis Oracle 自增序列的實現方法

    這篇文章給大家分享MyBatis Oracle 自增序列的實現方法及mybatis配置oracle的主鍵自增長的方法,非常不錯具有一定的參考借鑒價值,感興趣的朋友一起看看吧
    2016-11-11
  • Go Java算法之簡化路徑實例詳解

    Go Java算法之簡化路徑實例詳解

    這篇文章主要為大家介紹了Go Java算法之簡化路徑實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • 安全漏洞修復導致SpringBoot2.7與Springfox不兼容的偽命題解決

    安全漏洞修復導致SpringBoot2.7與Springfox不兼容的偽命題解決

    項目基于Spring Boot 2.5.2使用Springfox Swagger2生成API文檔,因安全漏洞需升級至2.7.8,導致兼容問題,下面就來介紹一下問題解決,感興趣的可以了解一下
    2025-06-06
  • Java實現系統限流的示例代碼

    Java實現系統限流的示例代碼

    限流是保障系統高可用的方式之一,也是大廠高頻面試題,它在微服務系統中,緩存、限流、熔斷是保證系統高可用的三板斧,所以本文我們就來聊聊如何實現系統限流吧
    2023-09-09
  • Mybatis新增數據并返回主鍵id的兩種方法實現

    Mybatis新增數據并返回主鍵id的兩種方法實現

    本文主要介紹了Mybatis新增數據并返回主鍵id的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-02-02

最新評論