• MyBatis(四)映射文件 之 参数获取详解#{} 与 ${}


    一、#{} 与${} 的取值

      相同点:

        #{}:可以获取map中的值或者pojo对象属性的值;
        ${}:可以获取map中的值或者pojo对象属性的值;

      区别:

         #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入;

        ${}:取出的值直接拼装在sql语句中;会有安全问题;

      大多情况下,我们去参数的值都应该去使用#{};

      什么情况下会使用 ${} 呢?

        在原生 jdbc 不支持占位符的地方就可以使用${} 进行取值,比如分表(表名的拼接,按照年份分表拆分)、排序等。

    select * from 2016_salary
    select * from ${year}_salary where xxx;
    select * from emp order by ${f_name} ${order}
    

      

    二、使用#{} 与 ${} 取值

      1、#{} 取值

        EmployeeMapper 接口中的方法:

    public void insertEmp(Employee employee);

        EmployeeMapper.xml 中的配置:

        <!--
            public void insertEmp(Employee employee);
        -->
        <insert id="insertEmp" parameterType="com.njf.mybatis.bean.Employee">
            insert into tbl_employee(`last_name`, `email`, `gender`)
            values(#{lastName}, #{email}, #{gender})
        </insert>

        测试:

         @Test
         public void testInsert() throws IOException {
              //1、获取 sqlSessionFactory
              SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
    
              //2、获取 sqlSession 实例,能直接执行已经映射的 SQL 语句
              SqlSession sqlSession = sqlSessionFactory.openSession();
              try {
                   EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    
                   Employee employee = new Employee(null, "张三", "男", "zhangsan@126.com");
                   mapper.insertEmp(employee);
                   
                   sqlSession.commit();
              } finally {
                   sqlSession.close();
              }
         }

        运行结果:

      2、${} 取值

        修改 mapper.xml 中的配置如下:

        <!--
            public void insertEmp(Employee employee);
        -->
        <insert id="insertEmp" parameterType="com.njf.mybatis.bean.Employee">
            insert into tbl_employee(`last_name`, `email`, `gender`)
            values(${lastName}, ${email}, ${gender})
        </insert>

        运行结果:

         可以发现这时并没有运行成功,这条 SQL 语句与数据库的类型不匹配。

        再次修改 mapper.xml,给 ${} 加上单引号

        <!--
            public void insertEmp(Employee employee);
        -->
        <insert id="insertEmp" parameterType="com.njf.mybatis.bean.Employee">
            insert into tbl_employee(`last_name`, `email`, `gender`)
            values('${lastName}', '${email}', '${gender}')
        </insert>

        运行结果:

         此时就可以运行成功了。

      3、总结

      获取参数值有两种方式:#{} 与${}
            (1)#{}
                    这种方式获取值,执行 SQL 时,是使用 PreparedStatement,使用 ?(通配符) 来赋值的
                    这种方式对于字符串类型来说,会自动加上单引号,不需要手动加单引号
                    因为在为 String 赋值时,可以自动加单引号,不需要考虑单引号
                    好处:可以防止 SQL 注入,更加安全。
     
            (2)${}
                    这种方式获取值,执行 SQL 时,使用 Statement 对象,不能使用通配符赋值,只能使用字符串拼接方式来赋值。
                    对于字符串类型,不会自动加单引号,需要手动加!!!
                    注意:这种方式使用字符串拼接的方式操作 SQL 语句,一定要注意单引号问题!!!
              使用建议:
                     建议优先使用 #{},在特殊情况,再使用 ${}。(例如:模糊查询、批量删除等操作时)
                     原生jdbc不支持占位符的地方我们就可以使用${}进行取值
                    比如分表、排序。。。;按照年份分表拆分
    select * from ${year}_salary where xxx;                //按年份表查询
    select * from tbl_employee order by ${f_name} ${order} //排序
    

      

    三、不同的参数类型,#{} 与 ${} 的不同取值方式

      1、当传输参数为单个 String 或基本数据类型和其包装类时

        (1)#{} 可以以任意的名字获取参数值

          在接口中声明方法:

    public Employee getEmployeeById(Integer id);

          在对应的映射文件中配置:

        <!--
            public Employee getEmployeeById(Integer id);
        -->
        <select id="getEmployeeById" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id = #{id}
        </select>

          运行结果:

           如果修改了 Mapper.xml 中的名字呢,还可以获取参数吗?

        <!--
            public Employee getEmployeeById(Integer id);
        -->
        <select id="getEmployeeById" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id = #{aaa}
        </select>

          运行结果:

         可以看出,修改了 SQL 语句中的参数名字,也可以获取参数并赋值成功!

        (2)${} 只能以 ${value} 或 ${_parameter} 获取

          mapper.xml 中的配置信息以 ${value} 取值

        <!--
            public Employee getEmployeeById(Integer id);
        -->
        <select id="getEmployeeById" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = ${value}
        </select>

        第一次运行结果:

        修改 mapper.xml 中的配置为:${_parameter}

        <!--
            public Employee getEmployeeById(Integer id);
        -->
        <select id="getEmployeeById" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = ${_parameter}
        </select>

        第二次运行结果:

        如果修改 mapper.xml 中的配置为 ${id}  呢?

        <!--
            public Employee getEmployeeById(Integer id);
        -->
        <select id="getEmployeeById" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = ${id}
        </select>

        运行结果:

         运行失败了!

        如果再改成随意的一个名字呢?

        <!--
            public Employee getEmployeeById(Integer id);
        -->
        <select id="getEmployeeById" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = ${aaa}
        </select>

        运行结果:

         还是运行失败!

        所以,对于${} 获取单个参数,只能使用 ${value} 或 ${_parameter}

      2、当传输参数为 JavaBean 时

        #{} 和 ${} 都可以通过属性名直接获取属性值。
        但是一定要注意 ${} 的单引号问题。
        参照上面的 insert 操作。

      3、当传输多个参数时(不用考虑类型)

        当传输多个参数时,MyBatis 会默认将这些参数放到 Map 集合中。

        有两种方式来实现:

          ① 存储时键为:0,1,2,3,... N-1,值就是参数本身

          ② 存储时键为:param1,param2,param3...paramN, 值是参数本身。

        注意:每个参数都是有顺序的,获取和存放都是有顺序的。(内部用 SortedMap 来实现)

        1、#{} 取值

          有两种方式:

            ① 使用 #{0},#{1} 来依次获取参数;

            ② 使用#{param1}, #{param2},#{paramN} 依次来获取参数

          mapper 的映射文件中以 #{N} 来获取参数

        <!--
            public Employee getEmployeeByIdAndLastName(Integer id, String name);
        -->
        <select id="getEmployeeByIdAndLastName" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  #{0} and last_name = #{1}
        </select>

          运行结果:

          mapper 的映射文件中以 #{paramN} 来获取参数

        <!--
            public Employee getEmployeeByIdAndLastName(Integer id, String name);
        -->
        <select id="getEmployeeByIdAndLastName" resultType="Employee">
            <!--select id, last_name lastName, email, gender from tbl_employee where id =  #{0} and last_name = #{1}-->
            select id, last_name lastName, email, gender from tbl_employee where id =  #{param1} and last_name = #{param2}
        </select>

          运行结果:

        2、${} 取值

          有一种方式:使用 ${param1},${param2} 依次来获取参数,但是要注意 ${} 的单引号问题!!!

          (1)Mapper.xml 中以 ${paramN} 来获取参数

            映射文件:

        <!--
            public Employee getEmployeeByIdAndLastName(Integer id, String name);
        -->
        <select id="getEmployeeByIdAndLastName" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  ${param1} and last_name = ${param2}
        </select>

            运行结果:

             并没有成功,对于String类型忘记加单引号了!!!

            给String类型加上单引号后:

        <!--
            public Employee getEmployeeByIdAndLastName(Integer id, String name);
        -->
        <select id="getEmployeeByIdAndLastName" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  ${param1} and last_name = '${param2}'
        </select>

            运行结果:

             此时就可以运行成功了。

          (2)Mapper.xml 中以 ${0} 来获取参数

            映射文件配置:

        <!--
            public Employee getEmployeeByIdAndLastName(Integer id, String name);
        -->
        <select id="getEmployeeByIdAndLastName" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  ${0} and last_name = ${1}
        </select>

            运行结果:

             可以发现这种方式并不能正确获取参数。

             那如果给String类型加上单引号,应该也是不可以的。

             加上单引号后,以 '${0}' 来获取参数

        <!--
            public Employee getEmployeeByIdAndLastName(Integer id, String name);
        -->
        <select id="getEmployeeByIdAndLastName" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  ${0} and last_name = '${1}'
        </select>

            运行结果:

           这种也不可以获取参数。

          对于${} 取值的方式,所以只能使用 ${paramN} 的方式来获取参数。

        

      4、当传输自定义的Map参数时

        #{} 和 ${} 都可以通过键的名字直接获取属性值,但是要注意对于String类型 ${} 取值的单引号问题!!!
        接口中的方法:
    public Employee getEmployeeByMap(Map<String, Object> map);
     
        映射文件中的配置以 #{key} 的方式取值:
        <!--
            public Employee getEmployeeByMap(Map<String, Object> map);
        -->
        <select id="getEmployeeByMap" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  #{id} and last_name = #{lastName}
        </select>
        测试:
         @Test
         public void test() throws IOException {
              //1、获取 sqlSessionFactory
              SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
    
              //2、获取 sqlSession 实例,能直接执行已经映射的 SQL 语句
              SqlSession sqlSession = sqlSessionFactory.openSession();
              try {
                   EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    
                   Map<String, Object> map = new HashMap<>();
                   map.put("id", "1");           // 这里的 key 需要与 xml 中的 key 一致
                   map.put("lastName", "Tom");      // 这里的 key 需要与 xml 中的 key 一致
                   Employee emp = mapper.getEmpByMap(map);
    
                   System.out.println("emp = " + emp);
              } finally {
                   sqlSession.close();
              }
         }
        运行结果:
        映射文件中的配置以 ${key} 的方式取值:
        <!--
            public Employee getEmployeeByMap(Map<String, Object> map);
        -->
        <select id="getEmployeeByMap" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  ${id} and last_name = '${lastName}'
        </select>
        运行结果:
        

      5、命名参数

        可以通过 @Param("key") 为 map 集合指定键的名字,也可以使用 #{paramN} 的方式来取值

        Mapper 接口中的方法:

    public Employee  getEmpByIdAndEnameByParam(@Param("id") int id, @Param("lastName")String lastName);

        Mapper.xml 中的配置以  #{key} 来取值:

        <!--
            public Employee  getEmpByIdAndEnameByParam(@Param("id")String id,  @Param("lastName")String lastName);
        -->
        <select id="getEmpByIdAndEnameByParam" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  #{id} and last_name = #{lastName}
        </select>

        运行结果:

         Mapper.xml 中的配置以 ${key} 来取值

        <!--
            public Employee  getEmpByIdAndEnameByParam(@Param("id")String id,  @Param("lastName")String lastName);
        -->
        <select id="getEmpByIdAndEnameByParam" resultType="Employee">
            select id, last_name lastName, email, gender from tbl_employee where id =  ${id} and last_name = '${lastName}'
        </select>

        运行结果:

        切记:String类型的 ${key} 的方式取值一定要加 单引号!!!

      6、当传输的是 Collection/Array 时

        Collection/Array 会被 MyBatis 封装成一个 map 传入,Collection 对应的 key 是 collection,Array 对应的 key 是 array,如果确定是 List 集合,key 还可以是 list。

    四、#{} 更丰富的用法

      1、参数位置支持的属性

        规定参数的一些规则:

    javaType、 jdbcType、 mode(存储过程)、 numericScale、resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);
    

      

      2、jdbcType通常需要在某种特定的条件下被设置

        在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
        JdbcType 
          OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
    由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
            1、#{email,jdbcType=NULL};
            2、jdbcTypeForNull=NULL
                <setting name="jdbcTypeForNull" value="NULL"/>
    
    实际上通常被设置的是:可能为空的列名指定 jdbcType
    insert into orcl_employee(id,last_name,email,gender)
    values(employee_seq.nextval,#{lastName,jdbcType=NULL },#{email},#{gender})
    

     

      3、参数处理

        参数也可以指定一个特殊的数据类型:

    #{property, javaType=int, jdbcType=NUMERIC}
    #{height, javaType=double, jdbcType=NUMERIC, numericScale=2}
    

        javaType 通常可以从参数对象中来去确定;

        如果 null 被当作值来传递,对于所有可能为空的列,jdbcType 需要被设置;

        对于数值类型,还可以设置小数点后保留的位数;

        mode 属性允许指定 IN, OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像在获取输出参数时所期望的那样。

  • 相关阅读:
    flask强大的三方组件flask-Migrate
    FTP和HTTP
    MD5-JS加密
    使用websocket实现单聊和多聊
    Flask上下文管理
    python中的with用法
    如何判断一个对象是可调用对象
    缓存
    websocket与http
    csrf
  • 原文地址:https://www.cnblogs.com/niujifei/p/15233397.html
Copyright © 2020-2023  润新知