一直在使用Mybatis這個ORM框架,都是使用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語句已經不包含變數了。
綜上所得, ${ } 的變數的替換階段是在動態 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},
String name = "sprite"; name = "'" + name + "'";其次,sql裡的變數取值是${xxx},不是#xxx},不是# {xxx}。