Mybatis XML檔案中,對java.util.Date物件的值到轉換為執行SQL進行比較所做的隱式行為分析。

星小梦發表於2024-06-21

本次分析了mybatis的mapper XML檔案,sql的where子句中使用java.util.Date進行比較進行分析。

假設使用的是以下sql語句。

select * from xxx where create_time > '2024-06-20 20:38:38'

在mybatis中,會將java.util.Date物件的值轉為java.sql.Timestamp,之後在mybatis取值的時候,會呼叫java.sql.Timestamp.toString()方法獲取字串值(也就是'2024-06-20 20:38:38')。
帶有日期對比字串的sql透過jdbc傳到MySQL伺服器端,此時,mysql會怎麼處理這種對比呢?答案就是**字串會隱式的轉換create_time 值所代表的MySQL型別,之後進行對比。


分析只關注核心位置,不對整個流程進行分析(你斷點此位置一般就能看到)。

下面就著重分析**Mybatis的XML檔案,對java.util.Date型別的處理方式是怎樣的?

首先進入到org.apache.ibatis.type.DateTypeHandler#setNonNullParameter方法,這裡沒什麼說明的。

org.apache.ibatis.logging.jdbc.PreparedStatementLogger#invoke方法中,
我們會看到SET_METHODS方法是個Set集合。
SET_METHODS方法的值列表
核心位置

經過debug,我們會進到org.apache.ibatis.logging.jdbc.BaseJdbcLogger類的位置。

getParameterValueString方法用於列印輸出值和值的型別。


這一行是關鍵語句return method.invoke(statement, params);
接著進入這行程式碼,會執行到com.alibaba.druid.pool.DruidPooledPreparedStatement#setTimestamp(int, java.sql.Timestamp)

在debug進入 stmt.setTimestamp(parameterIndex, x);


會到com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl#setTimestamp(int, java.sql.Timestamp)方法位置處,這裡的選中這一行chain.preparedStatement_setTimestamp(this, parameterIndex, x);是核心,


之後會到com.mysql.cj.jdbc.ClientPreparedStatement#setTimestamp(int, java.sql.Timestamp)方法位置處。


最後會到com.mysql.cj.NativeQueryBindings#setTimestamp,這裡就是給PreparedStatement物件的sql佔位符繫結值得位置了。
可以看到,到最後這個值還是java.sql.Timestamp型別的。

至於PreparedStatement之後怎麼處理我覺的就可以不用關注了,因為PreparedStatement配套的提供了setTimestamp方法,就說明內部已有支援的轉換方式,而且MySQL驅動本身自身也支援setTimestamp。

MySQL官方文件的對於DATETIME和TIMESTAMP型別進行對比的資料。

https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html

https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html

如果其中一個引數是 TIMESTAMP 或 DATETIME 列,而另一個引數是常量,那麼在進行比較之前,會將常量轉換為時間戳。這樣做是為了對 ODBC 更友好。但 IN() 的引數不會這樣做。為了安全起見,在進行比較時應始終使用完整的日期時間、日期或時間字串。例如,在使用帶有日期或時間值的 BETWEEN 時,為了獲得最佳結果,應使用 CAST() 將值明確轉換為所需的資料型別。

來自一個或多個表的單行子查詢不視為常量。例如,如果子查詢返回一個要與 DATETIME 值比較的整數,那麼比較將以兩個整數的形式進行。整數不會轉換為時間值。要將運算元作為 DATETIME 值進行比較,請使用 CAST() 將子查詢值顯式轉換為 DATETIME 值。

其他資料

MySQL關於日期格式化的相關函式。
CAST轉換型別
The DATE, DATETIME, and TIMESTAMP Types

相關文章