1. 程式人生 > >原始碼解析之 Mybatis 對 Integer 引數做了什麼手腳?

原始碼解析之 Mybatis 對 Integer 引數做了什麼手腳?

--- title: 原始碼解析之 Mybatis 對 Integer 引數做了什麼手腳? date: 2021-03-11 updated: 2021-03-11 categories: - Mybatis - 原始碼解析 tags: - Mybatis - 原始碼解析 --- 解決方案放在第二節,急需解決問題,可直接檢視解決方案。 本文為深度長文,請耐心閱讀! --- --- ## 問題描述 在 Mybatis 中,Integer 的入參為 0 時,發現判斷條件的非空判斷沒有生效,原本應該存在的判斷條件丟失了。 那麼,Mybatis 到底對 Integer 引數做了什麼手腳呢?下面我們來舉例說明: **環境示例**: 該問題只與 Mybatis 的實現機制有關,與版本基本無關(如果說相關性,可能只與原始碼中實現程式碼所在的行數有關)。 不過,為了養成良好的習慣,還是稍微提一下,我使用的 Mybatis 版本是 3.5.2。 **介面示例**: ```java @GetMapping("/queryByAgeGroup") public HttpStatus queryByAgeGroup(@RequestParams("ageGroup") Integer ageGroup) { // ageGroup 年齡段:0 代表幼兒,1 代表青年,2 代表中年,3 代表老年,-1 代表未知 IndexTestService.queryByAgeGroup(ageGroup); return HttpStatus.HTTP_OK; } ``` 測試用例屬於引數透傳,沒有業務邏輯,故省略 Service 和 Dao 層。 **查詢 SQL 示例**: ```xml ``` **資料表結構示例**: ```sql CREATE TABLE `people_info` ( `id` varchar(64) NOT NULL COMMENT '主鍵', `name` varchar(255) DEFAULT NULL COMMENT '名稱', `age_group` int(11) DEFAULT NULL COMMENT '年齡', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` 當入參為 0 時, 發現控制檯列印 SQL 如下: ```sql select * from `people_info` where 1 = 1 -- 本該存在的 ageGroup 判斷消失了! ``` ## 解決方案 首先提供該問題的幾種解決方案。 ### 方案一 如果是資料字典型別的欄位,在定義資料字典時,避免使用 0 作為列舉值,從根源杜絕該問題。 ### 方案二 如果是非法資料,可在 Controller 層入參增加引數校驗,如果傳 0,提示“引數無效”。 ### 方案三 如果是合法資料,可在 SQL 判斷條件上增加 ` or ageGroup == 0` 判斷。 ```xml ``` ### 方案四 如果是合法資料,可將 Integer 轉為 String,按 String 引數處理。 ```java String ageGroupStr = String.valueOf(1); ``` ## 原始碼解析 下面,我們就通過分析原始碼,一起來看一下 Mybatis 不為人知的“小動作”。 ### 解析 1. 首先,讓我們來到 DefaultSqlSession#select(statement, parameter, rowBounds, handler) 方法。 ```java // 第 165 行 @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { // 拿到對映的 sql 語句 MappedStatement ms = configuration.getMappedStatement(statement); // 執行器執行查詢 sql -- 重點!!! executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } ``` 2. 此時拿到傳入 sql,那麼有“小動作”的相想必是執行器,下面進入 BaseExecutor#query(ms, parameter, rowBounds, resultHandler) 方法。 ```java // 第 132 行 @Override