Mybatis中 #{}和${}的區別

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注入。字符串

 

狀況三: ${}   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

  結論:只要咱們在  ${}  輸入   ' OR 1=1 OR '   不管後面的密碼輸入什麼均可以,查詢到數據,這種狀況就是SQL注入。

 

 狀況四:#{}  防止SQL注入

  <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傳值,提升 Mapper 文件的可讀性

     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, 

     但每次都須要進行預編譯,對效率會有影響,至於要不要使用 ${}還要具體狀況具體分析。

 

  確定還有  ${} 其餘的用法,歡迎留言評論,共同探討,待續。。。。

相關文章
相關標籤/搜索