zoukankan      html  css  js  c++  java
  • Mybatis中 #{}和${}的区别

    2019.11.01 杭州的一个面试,有问道这个问题:

     讲一下Mybatis中 #{}和${}的区别?

     部分内容涉及到 MySQL的预编译

    情况一:只用  #{}

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

         但每次都需要进行预编译,对效率会有影响,至于要不要使用 ${}还要具体情况具体分析。

      肯定还有  ${} 其他的用法,欢迎留言评论,共同探讨,待续。。。。

  • 相关阅读:
    合唱队形2 洛谷U5874
    AC日记——石子归并 codevs 1048
    AC日记——舒适的路线 codevs 1001 (并查集+乱搞)
    AC日记——导弹拦截 洛谷 P1020 (dp+模拟)
    常见模板(欧拉筛素数,最小生成树,快排,并查集,单源最短路)
    AC日记——信息传递 洛谷 P2661 (tarjan求环)
    AC日记——逃出克隆岛 (bfs)
    数字对 (长乐一中模拟赛day2T2)
    改造二叉树 (长乐一中模拟赛day2T1)
    AC日记——热浪 codevs 1557 (最短路模板题)
  • 原文地址:https://www.cnblogs.com/mww-NOTCOPY/p/11777187.html
Copyright © 2011-2022 走看看