今天在工作時,使用MyBatis中向sql傳遞兩個參數時,一直顯示SQL語法錯誤,仔細檢查,才發現傳入的參數被加上了引號,導致傳入的參數(要傳入的參數是表名)附近出現語法錯誤。
錯誤寫法:
1 select pro_type, name, b.info from #{0} a inner join #{1} b on a.config_id = b.config_id;
這種寫法在控制台報錯:
select pro_type, name, b.info from ? a inner join ? b on a.config_id = b.config_id;
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''dana.auto_kpi_cfg_info' a inner join 'kingnetio.meta_config_info' b on a.config' at line 1
我們發現通過占位符傳進來的參數兩個表名都被加上了引號,這就導致在執行SQL時,會報語法錯誤。
后來特地查閱相關資料,改成使用${}:
1 select pro_type, name, b.info from ${param1} a inner join ${param2} b on a.config_id = b.config_id;
控制台日志信息如下:
Preparing: select pro_type, name, b.info from dana.auto_kpi_cfg_info a inner join kingnetio.meta_config_info b on a.config_id = b.config_id;
在傳入的表名參數上沒有添加引號了。
現在特此整理這兩種用法的不同點:
(1)首先一點就是,#{}傳遞參數時,會在傳遞的參數上加上引號,在傳遞屬性比如 name=? 時,可以很方便的使用#{}。而${}則不會添加引號,傳遞的是什么就會直接放到SQL中去執行。
(2)通過上面的日志信息我們可以看到,#{}傳遞的參數實際上是通過占位符去傳入到已經預編譯好的SQL中去的,所以此時的SQL已經完成編譯,只需要傳參數就完成執行了。而${}在日志中顯示的是直接將參數拼接成完整的SQL去DBMS中編譯執行的。所以#{}方式實際上比${}方式更加安全,不會引起SQL注入。但是在傳入表名參數時,只能使用${},這時候,必須要在接受參數的時候加入邏輯判斷,判斷參數中是否存在SQL語句,以防引起注入。
