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

 結論:

    #{}會在預編譯期,生成兩個  ?,作爲佔位符。

 

情況二:一個用  ${} 一個用 #{}

  <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 是字符串,所以 這種寫法顯然也是錯誤的。

     會報出如下錯誤:

### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'mww' in 'where clause'

  所以正確的寫法是這樣的:爲字符串字段加上單引號 ' '

<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)
 <==  Total: 1

  結論:顯然這種寫法是正確的,從這裏可以看出,預編譯期   ${}  是直接把參數拼結到SQL中,

     運行時,就只傳入了一個 #{} 修飾的參數。

     ${}的這種寫法還有一個安全隱患,那就是 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  使用那?

 

${}正確用法(個人見解):

  1、同時傳入一個字段名和字段值:

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。。。。

 

   2、傳入兩個字段名和字段值:

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} 代表的含義。

   3、使用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, 

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章