mybatis中的字符串拼接問題

mybatis字符串拼接

MyBatis中拼接字符串有兩種方式。

使用CONCAT 函數

SELECT * FROM user WHERE name LIKE CONCAT(CONCAT(‘%', #{name}), ‘%')

使用${ } 代替 #{ }

因為${ }直接傳入SQL,而#{ }傳入的是字符串帶有引號

SELECT * FROM user WHERE name LIKE ‘%${name}%'

原因:

#{}能夠有效防止SQL註入,但是也有它的缺點,它會把傳入的數據自動加上一個雙引號,所以如果要的是數字的話,就會比較尷尬。

而${}可以直接解析出原本的數據,所以需要數值比較的話,還是要加${}。

mybatis 拼接動態表名、字段名

在項目中遇到個需求是要動態的根據前臺傳入的字段名稱和升降序條件在mybatis裡動態拼接sql語句進行查詢。現在對解決方法進行下總結,希望對遇到同樣問題的夥伴有些幫助。

動態SQL是mybatis的強大特性之一,mybatis在對sql語句進行預編譯之前,會對sql進行動態解析,解析為一個BoundSql對象,也是在此處對動態sql進行處理。

這裡我們詳細說下動態表名和字段名。下面讓我們先來熟悉下mybatis裡#{}與${}的用法:

在動態sql解析過程,#{}與${}的效果是不一樣的:

#{ } 解析為一個 JDBC 預編譯語句(prepared statement)的參數標記符。

如以下sql語句

select * from user where name = #{name};

會被解析為:

select * from user where name = ?;

可以看到#{}被解析為一個參數占位符?。

${ } 僅僅為一個純碎的 string 替換,在動態 SQL 解析階段將會進行變量替換

如以下sql語句:

select * from user where name = ${name};

當我們傳遞參數“sprite”時,sql會解析為:

select * from user where name = "sprite";

可以看到預編譯之前的sql語句已經不包含變量name瞭。

綜上所得, ${ } 的變量的替換階段是在動態 SQL 解析階段,而 #{ }的變量的替換是在 DBMS 中。

#{}與${}的區別可以簡單總結如下:

  • #{}將傳入的參數當成一個字符串,會給傳入的參數加一個雙引號
  • ${}將傳入的參數直接顯示生成在sql中,不會添加引號
  • #{}能夠很大程度上防止sql註入,${}無法防止sql註入

${}在預編譯之前已經被變量替換瞭,這會存在sql註入的風險。如下sql

select * from ${tableName} where name = ${name}

如果傳入的參數tableName為user; delete user; –,那麼sql動態解析之後,預編譯之前的sql將變為:

select * from user; delete user; -- where name = ?;
--之後的語句將作為註釋不起作用,頓時我和我的小夥伴驚呆瞭!!!看到沒,本來的查詢語句,竟然偷偷的包含瞭一個刪除表數據的sql,是刪除,刪除,刪除!!!重要的事情說三遍,可想而知,這個風險是有多大。
  • ${}一般用於傳輸數據庫的表名、字段名等
  • 能用#{}的地方盡量別用${}

進入正題,通過上面的分析,相信大傢可能已經對如何動態調用表名和字段名有些思路瞭。示例如下:

  <select id="getUser" resultType="java.util.Map" parameterType="java.lang.String" statementType="STATEMENT">
    select 
        ${columns}
    from ${tableName}
        where COMPANY_REMARK = ${company}
  </select>

要實現動態調用表名和字段名,就不能使用預編譯瞭,需添加statementType="STATEMENT"" 。

statementType:STATEMENT(非預編譯),PREPARED(預編譯)或CALLABLE中的任意一個,這就告訴 MyBatis 分別使用Statement,PreparedStatement或者CallableStatement。默認:PREPARED。這裡顯然不能使用預編譯,要改成非預編譯。

其次,sql裡的變量取值是${xxx},不是#{xxx}。

因為${}是將傳入的參數直接顯示生成sql,如${xxx}傳入的參數為字符串數據,需在參數傳入前加上引號,如:

String name = "sprite";
name = "'" + name + "'";

這樣,sql就變成:

select * from user where name = 'sprite';

總結

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: