2019.11.01 杭州的一個面試,有問道這個問題:java
講一下Mybatis中 #{}和${}的區別?面試
<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User"> select * from USER where userName=#{userName} and userPassword =#{userPassword}; </select>
==> Preparing: select * from USER where userName=? and userPassword =?; ==> Parameters: mww(String), 123(String) <== Total: 1
#{}會在預編譯期,生成兩個 ?,做爲佔位符。sql
<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User"> select * from USER where userName=${userName} and userPassword =#{userPassword}; </select>
==> Preparing: select * from USER where userName=mww and userPassword =?; ==> Parameters: 123(String)
並且 由於 userName:mww 是字符串,因此 這種寫法顯然也是錯誤的。安全
會報出以下錯誤:app
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'mww' in 'where clause'
因此正確的寫法是這樣的:爲字符串字段加上單引號 ' 'spa
<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User"> select * from USER where userName='${userName}' and userPassword =#{userPassword}; </select>
結果:code
==> Preparing: select * from USER where userName='mww' and userPassword =?; ==> Parameters: 123(String) <== Total: 1
結論:顯然這種寫法是正確的,從這裏能夠看出,預編譯期 ${} 是直接把參數拼結到SQL中,xml
運行時,就只傳入了一個 #{} 修飾的參數。blog
${}的這種寫法還有一個安全隱患,那就是 SQL注入。字符串
<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User"> select * from USER where userName='${userName}' and userPassword =#{userPassword}; </select>
==> Preparing: select * from USER where userName='' OR 1=1 OR '' and userPassword =?; ==> Parameters: 65787682342367(String) <== Total: 2
<select id="getUserByNameAndPsw" parameterType="map" resultType="com.hotel3.model.User"> select * from USER where userName=#{userName} and userPassword =#{userPassword}; </select>
結果:
==> Preparing: select * from USER where userName=? and userPassword =?; ==> Parameters: ' OR 1=1 OR '(String), 123(String) <== Total: 0
結論:上面預編譯SQL的參數已經由佔位符 { ?} 代替,因此傳入的 ' OR 1=1 OR ' 只會做爲 userName字段的值,
而不會拼入執行的SQL。這樣就達到了防止SQL注入的目的。
在這種用法中, #{} 顯然比 ${} 用法更好。
那 ${} 爲何常常在 Mybatis 使用那?
User u=userService.getUserByNameAndPsw("userName,userType,userPassword",userName,userPassword);
SQL: select ${arg0} from USER
<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User"> select ${arg0} from USER where userName=#{userName} and userPassword =#{userPassword}; </select>
結果:
==> Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; ==> Parameters: mww(String), 123(String) <== Total: 1
結論:
生成了咱們想要SQL語句 :select userName,userType,userPassword from USER。。。。
User u=userService.getUserByNameAndPsw("userName,userType,userPassword",userName,userName,userPassword);
SQL: select ${arg0} from USER where ${arg1}=#{userName}
<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User"> select ${arg0} from USER where ${arg1}=#{userName} and userPassword =#{userPassword}; </select>
結果:
==> Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; ==> Parameters: mww(String), 123(String) <== Total: 1
結論:
按照傳參的順序匹配 SQL 中的 ${arg0},${arg1}。。。
生成咱們想要的代碼,但這個方式會使Mybatis 的 Mapper 文件可讀性變差,
若是不看其餘的代碼,很難辨別 ${arg0} ,${arg1} 表明的含義。
Map map =new HashMap(); map.put("selectValues","userName,userType,userPassword"); map.put("userNamefieId","userName"); map.put("userName",userName); map.put("userPassword",userPassword); User u=userService.getUserByNameAndPsw(map);
Mapper 文件的 xml
<select id="getUserByNameAndPsw" parameterType="map" resultType="com.hotel3.model.User"> select ${selectValues} from USER where ${userNamefieId}=#{userName} and userPassword =#{userPassword}; </select>
結果:
==> Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; ==> Parameters: mww(String), 123(String) <== Total: 1
結論:
結果和arg0、arg1的傳值方式相同,但 Mapper 文件的 xml 中的SQL語句能夠
經過對 map 中 key 值命名提升可讀性。
注:以上SQL,均爲預編期生成的SQL。
注2:${} 每次傳值不一樣 SQL 語句不一樣,能夠靈活生成 SQL,
但每次都須要進行預編譯,對效率會有影響,至於要不要使用 ${}還要具體狀況具體分析。
確定還有 ${} 其餘的用法,歡迎留言評論,共同探討,待續。。。。