Mybatis源碼閱讀(三):結果集映射3.3 —— 主鍵生成策略

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
前言

在前面兩篇博客中,我們介紹了對于select語句的簡單映射和嵌套映射。mybatis中使用ResultHandler等一系列的類,將查詢結果封裝到實體類中,可以說是mybatis中最復雜的過程,而剩下的insert、update、delete語句的操作則顯得較為簡單,沒有復雜的映射邏輯。這里需要提的是在insert語句中,關于主鍵自增的問題。
KeyGenerator

在我們實際的開發中,自增主鍵還是使用比較多的。而mybatis在處理insert語句時,并不會將自增主鍵給返回,而是返回插入的條數。當我們有業務需求要獲取插入時產生的自增主鍵(或者其他類型不由程序生成的主鍵)時,則可以使用KeyGenerator。

KeyGenerator是個接口,定義了processBefore和processAfter兩個方法,分別在insert之前和之后執行,代碼如下。

/**

  • insert語句不會反回自動生成的主鍵

  • 該接口用于在插入記錄時獲取自增主鍵

  • @author Clinton Begin
    */
    public interface KeyGenerator {

    /**

    • 執行insert之前執行,設置屬性order=“BEFORE”
    • @param executor
    • @param ms
    • @param stmt
    • @param parameter
      */
      void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

    /**

    • 執行insert之后執行,設置屬性order=“AFTER”
    • @param executor
    • @param ms
    • @param stmt
    • @param parameter
      */
      void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

}

KeyGenerator有三個實現類

其中NoKeyGenerator的方法都是空實現,本文不再講解。
Jdbc3KeyGenerator

Jdbc3KeyGenerator用于取回數據庫生成的自增id,它對應于mybatis-config.xml配置的useGeneratedKeys,以及insert節點中配置的useGeneratedKeys屬性。

Jdbc3KeyGenerator的processBefore是空實現,只實現了processAfter方法,該方法會調用processBatch方法將SQL語句執行后生成的主鍵記錄到用戶傳遞的實參中,而不是直接返回。代碼如下。

/*** 核心方法,將sql語句生成的主鍵存放到參數中** @param ms* @param stmt* @param parameter*/
public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {// 獲取sql節點配置的keyProperties,指定主鍵的字段final String[] keyProperties = ms.getKeyProperties();if (keyProperties == null || keyProperties.length == 0) {return;}// 獲取數據庫生成的主鍵。如果沒有生成主鍵則結果集為空try (ResultSet rs = stmt.getGeneratedKeys()) {// 獲取resultSet元信息final ResultSetMetaData rsmd = rs.getMetaData();// 獲取Configurationfinal Configuration configuration = ms.getConfiguration();// 查到的列數小于主鍵的列數,說明查詢有誤。mybatis不進行處理if (rsmd.getColumnCount() < keyProperties.length) {// Error?} else {assignKeys(configuration, rs, rsmd, keyProperties, parameter);}} catch (Exception e) {throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);}
}

在assignKeys方法中,處理對主鍵賦值的邏輯。該方法會判斷參數的類型,將Map、集合、對象三種情況分別進行處理。代碼如下。

private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,Object parameter) throws SQLException {if (parameter instanceof ParamMap || parameter instanceof StrictMap) {// 參數是mapassignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);} else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()&& ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {// 參數是集合assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, ((ArrayList<ParamMap<?>>) parameter));} else {// 參數是對象assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);}
}

三個方法邏輯類似,這里只對assignKeysToParam方法進行分析,。

該方法會在進入方法時,將參數轉為集合,并遍歷集合去給主鍵進行賦值,代碼如下。

/*** 當參數是對象時的處理邏輯** @param configuration* @param rs* @param rsmd* @param keyProperties* @param parameter* @throws SQLException*/
private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,String[] keyProperties, Object parameter) throws SQLException {// 將參數轉為集合,此時這個集合最多只有一個對象Collection<?> params = collectionize(parameter);if (params.isEmpty()) {return;}// 存放主鍵屬性的信息List<KeyAssigner> assignerList = new ArrayList<>();for (int i = 0; i < keyProperties.length; i++) {assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));}Iterator<?> iterator = params.iterator();while (rs.next()) {if (!iterator.hasNext()) {throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size()));}Object param = iterator.next();assignerList.forEach(x -> x.assign(rs, param));}
}

SelectKeyGenerator

MySql、PostgreSql等數據庫支持自增主鍵,在執行insert時可以不指定主鍵,插入的過程中由數據庫去生成自增主鍵。而像Oracle、DB2等數據庫產品則需要使用sequence實現自增id,因此在insert之前必須明確指定主鍵的值。SelectKeyGenerator就用來處理不支持自增主鍵的數據庫。

SelectKeyGenerator主要用于生成主鍵,它會執行映射配置文件中定義的selectKey節點的sql,該語句會獲取insert語句所需要的主鍵。

SelectKeyGenerator中有兩個核心字段,keyStatement用于存放解析后的selectKey節點,executeBefore用于標識該節點是在insert之前還是之后執行。

/*** 標識selectKey是在insert之前還是之后執行* true為之前,false為之后*/
private final boolean executeBefore;
/*** 存放selectKey節點,用于獲取insert語句使用的主鍵*/
private final MappedStatement keyStatement;

SelectKeyGenerator的processBefore和processAfter都是執行processGeneratorKeys方法,根據executeBefore字段的值決定是在insert之前還是之后執行。

@Override
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {if (executeBefore) {processGeneratedKeys(executor, ms, parameter);}
}@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {if (!executeBefore) {processGeneratedKeys(executor, ms, parameter);}
}

processGeneratorKeys方法是處理主鍵的核心代碼。該方法會先獲取selectKey節點的keyProperties配置的屬性名稱,該屬性對應著主鍵。接著,創建Executor執行器,執行selectKey節點中的sql,查詢主鍵。Executor是mybatis的核心執行器,后面的博客會對其進行介紹。

selectKey執行完畢后,會判斷查詢結果是否合法。主鍵的查詢結果肯定只會返回一條,因此查詢結果條數不為1的全部視為非法查詢。該方法代碼如下。

private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {try {if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {// 獲取selectKey節點的keyProperties配置的屬性名稱,表示主鍵對應的屬性String[] keyProperties = keyStatement.getKeyProperties();final Configuration configuration = ms.getConfiguration();// 創建用戶傳入的實參對應的MetaObject對象final MetaObject metaParam = configuration.newMetaObject(parameter);// 創建Executor對象,執行keyStatement記錄的sql。Executor是mybatis中的執行器,后面會講Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);// 執行selectKey節點的sql,查詢主鍵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 {// 主鍵查詢出來肯定只有一條,因此values的size不等于1的情況都是錯誤的// 創建主鍵對象對應的MetaObjectMetaObject metaResult = configuration.newMetaObject(values.get(0));if (keyProperties.length == 1) {if (metaResult.hasGetter(keyProperties[0])) {// 取出主鍵的值,設置到用戶參數對應的屬性中setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));} else {// 如果主鍵對象不包含get方法,就可能是基本類型或者String,直接復制setValue(metaParam, keyProperties[0], values.get(0));}} else {// 處理主鍵有多列的情況handleMultipleProperties(keyProperties, metaParam, metaResult);}}}} catch (ExecutorException e) {throw e;} catch (Exception e) {throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);}
}

該方法中有一個handleMultipleProperties方法,用于處理一張表多個主鍵的情況。由于復合主鍵耦合性高、影響性能,并且操作起來較為繁瑣,因此不推薦數據庫中給一張表設置多個主鍵,這里也就不對該方法進行介紹。
結語

KeyGenerator的代碼較為簡單,閱讀起來也不會吃力。本周我公司已經開始了上班,因此博客也需要開始寫了,mybatis的源碼已經閱讀了一大半,爭取在7月份之前把剩下代碼的博客全部編寫完畢
*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536968.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536968.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536968.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

list最大容量_Java 基礎(四)集合源碼解析 List

List 接口前面我們學習了Iterator、Collection&#xff0c;為集合的學習打下了基礎&#xff0c;現在我們來學習集合的第一大體系 List。List 是一個接口&#xff0c;定義了一組元素是有序的、可重復的集合。List 繼承自 Collection&#xff0c;較之 Collection&#xff0c;List…

Mybatis源碼閱讀(四):核心接口4.1——StatementHandler

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Shell學習之結合正則表達式與通配符的使用(五)

Shell學習之結合正則表達式與通配符的使用 目錄 通配符 正則表達式與通配符通配符通配符的使用正則表達式 正則表達式正則表達式的使用通配符 正則表達式與通配符 正則表達式用來在文件中匹配符合條件的字符串&#xff0c;正則是包含匹配。grep、awk、sed等命令可以支持正則表達…

Mybatis源碼閱讀(四):核心接口4.2——Executor(上)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

接收xml參數_SpringBoot實戰(二):接收xml請求

強烈推薦一個大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在對接一個第三方系統&#xff0c;需要接收第三方系統的回調&#xff0c;而且格式為XML形式&#xff0c;之前自己一般接收的參數是Json形式&#xff0c;于是乎做個實驗驗證一下使用…

報錯 插入更新_window如何解決mysql數據量過大導致的報錯

window如何解決報錯“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中輸入mysql -hlocalhost -uroot -p #如果設置了密碼直接接在p 后面 show variables like %storage_engine%以下為結果可以看到InnoDB是MySQL的默認引…

148. Sort List

Sort a linked list in O(n log n) time using constant space complexity. Example 1: Input: 4->2->1->3 Output: 1->2->3->4 Example 2: Input: -1->5->3->4->0 Output: -1->0->3->4->5難度&#xff1a;medium 題目&#xff1a;排…

Mybatis源碼閱讀(四):核心接口4.2——Executor(下)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

python解橢圓方程的例題_橢圓標準方程典型例題及練習題

橢圓標準方程典型例題例1已知P 點在以坐標軸為對稱軸的橢圓上&#xff0c;點P 到兩焦點的距離分別為354和352&#xff0c;過P 點作焦點所在軸的垂線&#xff0c;它恰好過橢圓的一個焦點&#xff0c;求橢圓方程&#xff0e; 解&#xff1a;設兩焦點為1F 、2F &#xff0c;且3541…

leetcode393. UTF-8 Validation

題目要求 A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:For 1-byte character, the first bit is a 0, followed by its unicode code. For n-bytes character, the first n-bits are all ones, the n1 bit is 0, followed by n-1 by…

Mybatis源碼閱讀(五 ):接口層——SqlSession

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

插入公式_一個小工具,徹底幫你搞定在Markdown中插入公式的問題

在編輯Markdown文檔時&#xff0c;插入公式是一個挺麻煩的活兒。需要掌握LaTex語法。我自己看完語法后&#xff0c;直接放棄&#xff0c;這絕對是反人類的語法。&#xff08;好吧&#xff0c;是我不會用...&#xff09;但是&#xff0c;我相信你看了這篇文章后&#xff0c;絕對…

JavaScript數據結構與算法——字典

1.字典數據結構 在字典中&#xff0c;存儲的是【鍵&#xff0c;值】對&#xff0c;其中鍵名是用來查詢特定元素的。字典和集合很相似&#xff0c;集合以【值&#xff0c;值】的形式存儲&#xff0c;字典則是用【鍵&#xff0c;值】對的形式存儲。字典也稱作映射。 2.創建字典 f…

Mybatis源碼閱讀(一):Mybatis初始化1.2 —— 解析別名、插件、對象工廠、反射工具箱、環境

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

中西方對時間的差異_中西方時間觀念差異 英文

The concept of time(時間觀念)①Inchina&#xff0c;words and phrases about time are very general. Forexample&#xff0c;ifyoudatewithsomeone,mostofChineseusedtoanswer: in the afternoon /at night/after a while and so on.Butinwestern,peoplehaveaverystrongconc…

Google 修改 Chrome API,防止隱身模式檢測

開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f; 在使用 Chrome 瀏覽網頁時&#xff0c;某些網站會使用某種方法來確定訪問者是否處于隱身模式&#xff0c;這是一種隱私泄漏行為。Google 目前正在考慮修改 Chrome 的相關 API&#xff0c;來杜絕…

Mybatis源碼閱讀(一):Mybatis初始化1.1 解析properties、settings

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

亞馬遜推薦python_使用python查找amazon類別

我想得到amazon的類別&#xff0c;我計劃廢棄不用API。我已經取消了http://www.amazon.com&#xff0c;我已經在Shop By Department下拉列表中抓取了所有的類別和子類別&#xff0c;我創建了一個web服務來完成這項工作&#xff0c;代碼就在這里route(/hello)def hello():textli…

JavaScript異步基礎

唯一比不知道代碼為什么崩潰更可怕的事情是&#xff0c;不知道為什么一開始它是工作的&#xff01;在 ECMA 規范的最近幾次版本里不斷有新成員加入&#xff0c;尤其在處理異步的問題上&#xff0c;更是不斷推陳出新。然而&#xff0c;我們在享受便利的同時&#xff0c;也應該了…

Flutter、ReactNative、uniapp對比

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…