1 引言
前面的所有語句中你所見到的都是簡單參數的例子,實際上參數是 MyBatis 非常強大的元素,對於簡單的做法, 90% 的情況參數都很少,比如:
<select id="selectUsers" resultType="User"> select id, username, password from users where id = #{id} </select>
上面的這個示例說明了一個非常簡單的命名參數映射。參數類型被設置為 int,這樣這個參數就可以被設置成任何內容。
原生的類型或簡單數據類型(比如整型和字符串)因為沒有相關屬性,它會完全用參數值來替代。然而,如果傳入一個復雜的對象,行為就會有一點不同了。比如:
<insert id="insertUser" parameterType="User"> insert into users (id, username, password) values (#{id}, #{username}, #{password}) </insert>
如果 User 類型的參數對象傳遞到了語句中,id、username 和 password 屬性將會被查找,然后將它們的值傳入預處理語句的參數中。
這點對於向語句中傳參是比較好的而且又簡單,不過參數映射的功能遠不止於此。
2 Mybitis參數類型
首先,像 MyBatis 的其他部分一樣,參數也可以指定一個特殊的數據類型。
#{property,javaType=int,jdbcType=NUMERIC}
像 MyBatis 的剩余部分一樣,javaType 通常可以從參數對象中來去確定,前提是只要對象不是一個 HashMap。那么 javaType 應該被確定來保證使用正確類型處理器。
NOTE 如果 null 被當作值來傳遞,對於所有可能為空的列,需要設置參數的jdbcType。你可以自己通過閱讀預處理語句的 setNull() 方法的 JavaDocs 文檔來研究這種情況。
為了以后定制類型處理方式,你也可以指定一個特殊的類型處理器類(或別名),比如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
盡管看起來配置變得越來越繁瑣,但實際上是很少去設置它們。
對於數值類型,還有一個小數保留位數的設置,來確定小數點后保留的位數。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最后,mode 屬性允許你指定 IN,OUT 或 INOUT 參數。
如果參數為 OUT 或 INOUT,參數對象屬性的真實值將會被改變,就像你在獲取輸出參數時所期望的那樣。如果 mode 為 OUT(或 INOUT),而且 jdbcType 為 CURSOR(也就是 Oracle 的 REFCURSOR),你必須指定一個 resultMap 來映射結果集到參數類型。要注意這里的 javaType 屬性是可選的,如果左邊的空白是 jdbcType 的 CURSOR 類型,它會自動地被設置為結果集。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis 也支持很多高級的數據類型,比如結構體,但是當注冊 out 參數時你必須告訴它語句類型名稱。比如(再次提示,在實際中要像這樣不能換行):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
盡管所有這些強大的選項很多時候你只簡單指定屬性名,其他的事情 MyBatis 會自己去推斷,最多你需要為可能為空的列名指定 jdbcType。
#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}
3 字符串替換
默認情況下,使用#{}格式的語法會導致 MyBatis 創建預處理語句屬性並安全地設置值(比如?)。這樣做更安全,更迅速,通常也是首選做法,不過有時你只是想直接在 SQL 語句中插入一個不改變的字符串。比如,像 ORDER BY,你可以這樣來使用:
ORDER BY ${columnName}
這里 MyBatis 不會修改或轉義字符串。
NOTE 以這種方式接受從用戶輸出的內容並提供給語句中不變的字符串是不安全的,會導致潛在的 SQL 注入攻擊,因此要么不允許用戶輸入這些字段,要么自行轉義並檢驗。
3.1 #{}與${}
注意以下兩個符號的使用:
#{}(ibatis為:##):MyBatis創建預處理語句屬性從而設置安全的值(比如?)。常用作查詢條件的值,例如:where name=#{value}。
該參數可以指定一個確切的數據類型,例如: #{property,javaType=int,jdbcType=NUMERIC}.
${}(ibatis為:$$): MyBatis不會修改或轉義字符串,將會直接在SQL語句中插入一個不改變的字符串,常用於拼湊sql的實體部分,
例如:select * from ${tableName}
在執行SQL時MyBatis會自動通過對象中的屬性給SQL中參數賦值,它會自動將Java類型轉換成數據庫的類型。而一旦傳入的是null它就無法准確判斷這個類型應該是什么,就有可能將類型轉換錯誤,從而報錯。
要解決這個問題,需要針對這些可能為空的字段,手動指定其轉換時用到的類型。
一般情況下,我們沒有必要按個字段去識別/判斷它是否可以為空,而是將所有的字段都當做可以為空,全部手動設置轉換類型。
[java] view plain copy
-
<insert id="save"
-
parameterType="com.tarena.entity.Cost">
-
insert into cost values(
-
cost_seq.nextval,
-
#{name,jdbcType=VARCHAR},
-
#{base_duration,jdbcType=INTEGER},
-
#{base_cost,jdbcType=DOUBLE},
-
#{unit_cost,jdbcType=DOUBLE},
-
#{status,jdbcType=CHAR},
-
#{descr,jdbcType=VARCHAR},
-
#{creatime,jdbcType=TIMESTAMP},
-
#{startime,jdbcType=TIMESTAMP},
-
#{cost_type,jdbcType=CHAR}
-
)
-
</insert>
再次插入一條空數據,測試結果,能插入數據庫。
其他數據類型參照下圖表: