zoukankan      html  css  js  c++  java
  • MyBatis 各种参数传递方式

    MyBatis参数传递方式

    • 情况一:Mapper映射器接口方法参数只有一个且为基本类型

      接口方法:

      public List<UserEntity> selectUserByAge(int age);
      

      映射结果:

      <select id="selectUserByAge" resultMap="userResultMap">
              select * from tb_user where age > #{age};
      </select>
      

      其中 #{参数名} 表示参数占位符,等价于SQL语句的 号,这里的 #{age} 对应的就是接口方法 selectUserByAge 的参数。由于只有一个参数,而且是基本类型,所以写成 #{userAge} 或者 #{ageUser} 都无所谓,反正作用是一样的。

    • 情况二:Mapper映射器接口方法参数只有一个且为引用类型

      接口方法:

      public int insertUser(UserEntity user);
      

      映射结果:

      <insert id="insertUser">
         insert into tb_user (id,userName, password, name, age, sex, birthday, created, updated) values
         (null,#{userName},#{password},#{name},#{age},#{sex},#{birthday},now(),now());
      </insert>
      

      接口方法 insertUser 的参数是引用类型,其实传递给 SQL 语句的参数是引用类型的属性值,SQL 语句本身是不支持引用类型的。那引用类型有很多属性(或成员变量),是如何与 SQL 语句的参数一一对应的呢?

      答案是使用 #{引用类型的属性名} ,这里需要注意的是属性名不能写错了,否则就无法与 SQL 语句的参数对应,无法正确传递参数哈。

      public class UserEntity {
          private int id;
          private String userName;
          private String password;
          private String name;
          private int age;
          private int sex;
          private Date birthday;
          private String created;
          private String updated;
      }
      

      由于是自增主键,所以不需要传递引用类型的 id 参数,使用 null 代替,数据库会自动生成主键 id 标识。

    • 情况三:Mapper映射器接口方法参数有两个基本类型

      接口方法:

      public int updateUser(int id, String name);
      

      映射结果:

      <update id="updateUser">
      	  update tb_user set name=#{name} where id=#{id};
      </update>
      

      接口方法 updateUser 有两个参数且都是基本类型,按理说直接使用 #{参数名} 就可以了,不过一运行居然报错,如下:

      ### SQL: update tb_user set name=? where id=?;
      ### Cause: org.apache.ibatis.binding.BindingException: Parameter 'name' not found. Available parameters are [0, 1, param1, param2]
      
      

      从错误信息描述看,说是参数 name 没有发现,有效的参数是 [0, 1, param1, param2]。这是什么意思呀? 意思就是说当遇到不只一个参数时,比如两个参数,就不能用#{参数名}作为占位符,可以用MyBatis提供了两种方式之一。

      • 方式一#{0} 表示第一个参数 name,#{1} 表示第二个参数 id,#{2} 表示第三个参数...

        使用如下:

        <update id="updateUser">
        	  update tb_user set name=#{0} where id=#{1};
        </update>
        
        
      • 方式二#{param1} 表示第一个参数 name,#{param2} 表示第二个参数 id,#{param3} 表示第三个参数...

        使用如下:

      <update id="updateUser">
      	  update tb_user set name=#{param1} where id=#{param2};
      </update>
      
      

      其实,如果你非要用 #{参数名} 作为占位符,还可以用 MyBatis 提供的第三种方式,如下:

      • 方式三:给接口方法的参数取别名,只要参数别名和 #{参数名} 相同就可以了。

        使用如下:

      // @Param("id")表示给参数 int id 取别名为id,@Param("name") 表示给参数 name 取别名为name
      public int updateUser(@Param("id") int id,@Param("name") String name);
      
      <update id="updateUser">
      	  update tb_user set name=#{name} where id=#{id};
      </update>
      

      总结

      以上三种 MyBatis 参数的传递方式,哪种项目开发中比较常用呢?答案是第三种方式。理由是这种方式的代码可读性更好。想一想上面举例中,是#{name},#{id}作为参数占位符意思让人一目了然,还是#{0},#{1},#{param1},#{param2}呢?答案应该不言而喻。

    • 情况四:Mapper映射器接口方法参数有两个引用类型

      接口方法:

      public List<UserEntity> selectUserByAgeAndSex(@Param("userOne") UserEntity userOne,@Param("userTwo") UserEntity userTwo);
      

      映射结果1:

      <select id="selectUserByAgeAndSex" resultMap="userResultMap">
        	select * from tb_user where age > #{userOne.age} and sex = #{userTwo.sex};
      </select>
      

      映射结果2:

      <select id="selectUserByAgeAndSex" resultMap="userResultMap">
              select * from tb_user where age > #{param1.age} and sex = #{param2.sex};
      </select>
      

      以上两种映射方式都可以,但是如果没有为两个参数取 @Param("userOne") 和 @Param("userTwo") 别名的话,那么就只有映射结果2可以了,映射结果1将会报错。

      爱思考的你可能会问,MyBatis 不是还有一种传参的方式吗?如下映射方式是否可以?

      <select id="selectUserByAgeAndSex" resultMap="userResultMap">
              select * from tb_user where age > #{0.age} and sex = #{1.sex};
      </select>
      

      回答这个问题很简单,试一试不就知道了嘛。测试结果如下:

      ### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter '0' not found. Available parameters are [userOne, userTwo, param1, param2]
      ### Cause: org.apache.ibatis.binding.BindingException: Parameter '0' not found. Available parameters are [userOne, userTwo, param1, param2]
      

      测试结果报错,这说明 #{0}、#{1}这种参数占位符的方式只适用于参数是基本类型,不适用于参数是引用类型。

    • 情况五:Mapper映射器接口方法参数有多个(包括基本类型和引用类型)

      接口方法:

      public List<UserEntity> selectUserByNameAndAge(@Param("name") String name, @Param("user") UserEntity user);
      

      映射结果1:

      <select id="selectUserByNameAndAge" resultMap="userResultMap">
         select * from tb_user where name = #{name} and age > #{user.age};
      </select>
      

      映射结果2:

      <select id="selectUserByNameAndAge" resultMap="userResultMap">
          select * from tb_user where name = #{param1} and age > #{param2.age};
      </select>
      

      以上两种映射方式都可以。

    看到这里,相信你应该对 MyBatis 的#{ }传参方式已经胸有成竹了吧。无论接口方法的参数个数如何、类型如何,你应该都知道如何映射。

    MyBatis 参数传递${}方式

    MyBatis 除了可以使用 #{ } 方式传递参数,还有一种传参的方式,那么就是 ${ }。你可能会想,#{ }方式传递参数就已经够用了,干嘛还要搞一个 ${ } 出来,有完没完呀。我们还是先来看一下它的用法再说。

    接口方法:

     public List<UserEntity> selectUserByNameAndAge(@Param("name") String name, @Param("user") UserEntity user);
    
    

    映射结果:

    <select id="selectUserByAgeAndSex" resultMap="userResultMap">
         select * from tb_user where age > ${userOne.age} and sex = ${userTwo.sex};
    </select>
    

    原来这么简单,直接把原来的 #{ } 替换成 ${ }就可以了啦。那是不是所有 SQL 语句中使用 #{ } 的地方都可以被替换,这两种方式效果是相同的,是吗?对不起,回答错误。我们再看一个例子。

    接口方法:

     public int updateUser(@Param("id") int id,@Param("name") String name);
    

    映射结果:

    <update id="updateUser">
            update tb_user set name=${name} where id=${id};
    </update>
    

    运行测试,结果如下:

    ### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '张三三' in 'field list'
    ### The error may involve defaultParameterMap
    ### The error occurred while setting parameters
    ### SQL: update tb_user set name=张三三 where id=1;
    ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '张三三' in 'field list'
    
    

    报错了吧,这是怎么回事。从抛出的异常错误信息中,可以重点看 Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '张三三' in 'field list' 这句话。首先Cause 的中文意思就是错误原因;接着告诉我们抛出了一个错误异常 MySQLSyntaxErrorException,这个异常是 SQL 语法错误的意思;最后提示我们引起 SQL 语法错误的是 Unknown column '张三三',中文意思是无法识别的表字段名‘张三三''。

    分析了半天,还是不知道问题到底出在哪里?有点耐心好吧,你知道吗?经常有程序员前辈会告诉你,代码是调试出来的,不是写出来的,意思就是代码都是从错误中改出来的,没有人写代码从不出错。而作为一名合格的程序员,是需要具有独立解决问题的能力。那怎样才能具有独立解决问题的能力,这就需要多学一学人家是如何分析和定位问题的,逐渐积累经验哈。

    接着我们再看下面这三句,也许会给我们更多错误提示信息:

    ### The error may involve defaultParameterMap 错误可能涉及默认的参数映射
    ### The error occurred while setting parameters 当设置参数时产生错误的
    ### SQL: update tb_user set name=张三三 where id=1; 有语法错误的 SQL 语句
    

    我把这三句翻译成中文,相信你应该会看得明白一些。现在你应该感受到,要想编程好英文少不了。如果英文不过关,错误原因就在眼前,你也熟视无睹,如同盲人一般。所以,抽空补一补英文,别让它拖你的后腿,成为你通往程序员大牛的绊脚石。

    现在错误原因已经搞清楚了,传递参数导致的 SQL 语法错误,具体而言是传递 name 参数。那究竟错在哪里呢?update tb_user set name=张三三 where id=1; 这句 SQL 语句语法错误在于参数'张三三'是字符串但是 SQL 语句中并没有加单引号。

    对比以下正确和错误的 SQL 语法如下:

    update tb_user set name=张三三 where id=1;   #错误 SQL 语法
    update tb_user set name='张三三' where id=1; #正确 SQL 语法
    

    现在你应该明白了, #{ } 和 ${ }用法还是有区别的。

    ${ } 会将传递的参数直接显示在 SQL 语句中,而 #{ } 会将传递的参数自动添加单引号。

    现在可以解释为何第一个例子没有报错,第二个例子就报错了。因为第一个例子的两个参数都是 int 类型,所以加不加单引号都是一样的,也就是说,以下两条 SQL 语句执行结果相同。

    select * from tb_user where age > '20' and sex = '1';# 采用#{}方式
    select * from tb_user where age > 20 and sex = 1;    # 采用${}方式
    

    那 ${ } 在项目开发中到底有何用武之地呢?我们可以反过来想,什么情况下需要传递的参数不能自动添加单引号,否则会报错,而这些情况就是它的用武之地。

    • 情况一:order by 时,必须使用 ${ }

      什么意思呀,还是举个例子,如下:

      select * from tb_user where age > '20' order by 'age'
      select * from tb_user where age > '20' order by age
      

      以上两条 SQL 语句都可以在数据库中执行,但是只有一条 SQL 语句执行结果是正确的,请问是哪一条?还是动手试一试就知道了。

      执行第一条 SQL 语句结果如下:

      执行第二条 SQL 语句结果如下:

      我们知道 order by 是将查询结果进行排序,这里是按照年龄排序,默认是升序。这两条 SQL 语句执行结果只有第二条是正确的。

      现在你应该明白了,为何 order by 后面如果要传递参数,必须用不加单引号的 ${ },而不是自动加单引号的#{ }了吧。

    • 情况二:表名作为参数时,必须使用 ${ }

      什么时候会用表名做参数呀,那就是当数据库有两个一模一样的表,分别是历史表和当前表。历史表和当前表都可以查询表中的信息,但有时候需要从历史表中去查询数据,有时候需要从当前表中查询数据,而且希望使用1个方法来完成查询操作,如下:

      select * from ${tableName} 
      

      如果表名作为参数使用 #{ } 那么就会给表名自动添加单引号,这明显 SQL 语法不正确,这就是为何如果参数传递是表名时只能用 ${ }。

    MyBatis 参数传递 #{} 和 ${} 区别

    想必 MyBatis 的#{ } 和 ${ } 两种参数传递的方式你已经掌握了,那么我想再进一步加深你对它们的理解。你要明白 MyBatis 框架本质上是对 JDBC 的封装,所以想要深入理解 MyBatis 的原理,需要对 JDBC 有深入的认识。

    • 原理

      #{ }:为参数占位符?(即底层使用了 JDBC 的 PreparedStatement 来进行预处理)

      ${ }:为字符串替换(即底层使用了 JDBC 的 Statement 直接进行查询)

      注:如果你对 JDBC 的 PreparedStatement 和 Statement 的两种参数处理方式不了解,建议自己去补一补 JDBC 相关的内容。

    • 参数传递

      #{ }: 传递参数后 SQL 语句自动为参数加上单引号

      ${ }: 传递参数后 SQL 语句不会为参数加上单引号

    • SQL 注入

      #{ }:可以防止 sql 注入

      ${ }:不可以防止 sql 注入

      注:SQL 注入是黑客攻击服务器的一种简单手段,感兴趣的话可以去看我写的关于 SQL 注入的详细博客文章。

    MyBatis 参数传递总结

    参数传递方式

    • 参数传递 #{ } 方式:#{0} 、 #{param1} 、 @param(别名) / #{参数名}
    • 参数传递 ${ } 方式:用法同 #{ } 相同,注意与 #{ } 的区别

    项目开发建议

    • 建议接口参数一律使用 @param("参数名") 为参数取别名(可读性高)
    • 建议只要能用 #{ } 的地方尽量不使用 ${ }(安全性高)
    作者:Binge
    本文版权归作者和博客园共有,转载必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    Intellij IDEA Java web 项目搭建
    Spring的AOP原理
    为何有DAO与Service层?为何先搞Dao接口在搞DaoImpl实现?直接用不行吗?
    DAO层,Service层,Controller层、View层协同工作机制
    浅谈service、DAO层引入(转)
    java书籍推荐转
    Java点滴-List<Integer> list; 中尖括号的意思
    Composer更新慢的终极解决方案-转
    laravel门面DB返回数组配置
    laravel构建联合查询
  • 原文地址:https://www.cnblogs.com/binbingg/p/13747286.html
Copyright © 2011-2022 走看看