zoukankan      html  css  js  c++  java
  • MyBatis(四)映射文件 之 参数处理

    一、参数传递

      1、单个参数

        可以接收基本类型,包装类型,字符串类型等。
        这种情况 MyBatis 可直接使用这个参数,不需要经过任何处理。
      通过 #{参数名/任意名}:取出参数值;
    

      

      2、多个参数 

        任意多个参数,都会被 MyBatis 重新包装成一个 Map 传入。
        Map 的 key 是 param1,param2,...paramN 或者 0,1,2...N-1 (参数的索引)值就是参数的值
        Map 的 value 就是传入的参数值
        #{ } 就是从 map 中获取指定的 key 的值;
        通过 #{param1} 或 #{0}  就可以从map中获取指定key的值
        如果不这样的话就会抛出异常:
    方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
    取值:#{id},#{lastName}
    抛出异常:
        org.apache.ibatis.binding.BindingException:
        Parameter 'id' not found.
        Available parameters are [1, 0, param1, param2]
         
        正确取值方式:
        方式一:使用 param 形式
        <!--
           public Employee getEmpByIdAndLastName(Integer id, String lastName);
        -->
        <select id="getEmpByIdAndLastName" resultType="com.njf.mybatis.bean.Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = #{0} and last_name = #{1}
        </select>
        方式二:使用参数索引的形式
        <!--
           public Employee getEmpByIdAndLastName(Integer id, String lastName);
        -->
        <select id="getEmpByIdAndLastName" resultType="com.njf.mybatis.bean.Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = #{param1} and last_name = #{param2}
        </select>
     

      3、命名参数

        为参数使用 @Param 起一个名字,Mybatis 就会将这些参数封装进 map 中,key 就是我们自己指定的名字

    key:使用@Param 注解指定的值
    value:参数值
    
    #{指定的key}取出对应的参数值
    

        示例:

        <!--
          public Employee getEmpByIdAndLastName(@Param("id")Integer id, @Param("lastName") String lastName);
        -->
        <select id="getEmpByIdAndLastName" resultType="com.njf.mybatis.bean.Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = #{id} and last_name = #{lastName}
        </select>

      4、POJO

         如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;

    #{属性名}:取出传入的pojo的属性值
    

      

      5、Map

        如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map

    #{key}:取出map中对应的值
    

         代码示例:

       <!--    public Employee getEmpByMap(Map<String, Object> map); -->
        <select id="getEmpByMap" resultType="com.njf.mybatis.bean.Employee">
            select id, last_name lastName, email, gender from tbl_employee
            where id = #{id} and last_name = #{lastName}
        </select>

        测试:

         @Test
         public void testParam() throws IOException {
              //1、获取 sqlSessionFactory
              SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
    
              //2、获取 sqlSession 实例,能直接执行已经映射的 SQL 语句
              SqlSession sqlSession = sqlSessionFactory.openSession();
              try {
                   //3、获取接口的实现类对象
                   /**
                    * 推荐使用
                    * 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
                    */
                   EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    
                   Map<String, Object> map =  new HashMap<String, Object>(){{
                        put("id", 1);
                        put("lastName", "Tom");
                   }};
    
                   Employee emp = employeeMapper.getEmpByMap(map);
    
                   System.out.println("emp = " + emp);
              } finally {
                   sqlSession.close();
              }
         }

      6、TO

        如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象

    如分页对象
    Page{
        int index;
        int size;
    }
    #{属性名}:取出传入的 TO 的属性值

      7、Collection/Array

        如果是Collection(List、Set)类型或者是数组,也会特殊处理。会被 MyBatis 封装成一个 map 传入。
        Collection(List、Set) 对应的 key 是 collection,Array 对应的 key 是array,如果确定是 List 集合,key 还可以是 list。
     
     

    二、案例

      参数传递案例:

    1、传递单个参数
         <!-- public Emp selectById(int id); -->
         <select id="selectById"  resultType="com.njf.bean.Emp">
             select id,last_name,email,gender from t_emp  where id=#{id}
         </select>
    
    2、传递多个参数
         <!-- public Emp selectByIdAndName(Integer id, String  name); -->
         <select id="selectByIdAndName"  resultType="com.njf.bean.Emp">
             select id,last_name,email,gender from  t_emp where id=#{0} and last_name=#{1}
             select id,last_name,email,gender from  t_emp where id=#{param1} and last_name=#{param2}
         </select>
    
    3、命名参数
         <!-- public Emp selectByIdAndName(@Param("id")Integer id,  @Param("name")String name); -->
         <select id="selectByIdAndName"  resultType="com.njf.bean.Emp">
             select id,last_name,email,gender from t_emp  where id=#{id} and last_name=#{name}
         </select>
    
    4、传递Map
         <!-- public Emp getEmpByMap(Map<String, Object> map);  -->
         <select id="getEmpByMap"  resultType="com.njf.bean.Emp">
             select id,last_name,email,gender from t_emp  where id=#{id} and last_name=#{name}
         </select>
    
    5、传入POJO或TO
         <!-- public void addEmp(Emp emp); -->
         <insert id="addEmp"  parameterType="com.njf.bean.Emp"
             useGeneratedKeys="true" keyProperty="id"  databaseId="mysql">
             insert into t_emp(last_name,email,gender)
             values(#{lastName}, #{email}, #{gender})
         </insert>
    
    6、传入集合/数组
         <!-- public Emp getEmpByCollection(Collection<String>  collection); -->
         <select id="getEmpByCollection"  resultType="com.njf.bean.Emp">
             select id,last_name,email,gender from  t_emp where id=#{list[0]} and last_name=#{list[1]}
         </select>
         
         <!-- public Emp getEmpByArray(String[] array); -->
         <select id="getEmpByArray"  resultType="com.njf.bean.Emp">
             select id,last_name,email,gender from t_emp  where id=#{array[0]} and last_name=#{array[1]}
         </select>
    
    案例:
    public Employee getEmp(@Param("id")Integer id,String lastName);
        取值:id==>#{id/param1}   lastName==>#{param2}
    
    
    public Employee getEmp(Integer id,@Param("e")Employee emp);
        取值:id==>#{param1}    lastName===>#{param2.lastName/e.lastName}
    
    public Employee getEmpById(List<Integer> ids);
        取值:取出第一个id的值:   #{list[0]}

    三、参数传递源码分析

      1、以命名参数为例

    public Employee getEmployeeByIdAndLastName
    (@Param("id")Integer id, @Param("lastName")String lastName);
        总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;#{key}就可以取出map中的值;

      2、Debug源码

        (1)测定代码

           可以发现在真正执行方法的时候的由 MyBatis生成的代理对象来执行。

        (2)代理对象 MapperProxy

           在 MapperProxy 代理类中执行方法,如果要是 Object 类中的方法则直接跳过,如果不是就把当前的方法包装成一个 MapperMethod 对象,然后执行其中的 execute() 方法。

        (3)MapperMethod 的 execute() 方法

           在这个方法里面会根据增删改查,不同的标签来执行的不同的方法,但都有共同的一步:converArgsToSqlCommandParam() 将参数args转换为 SQL命令行参数;

        (4)转换参数

           具体的转换参数是由 paramNameResolver 参数名称解析器来解析的。

        (5)解析参数 getNameParam 方法

           该方法就是用来解析参数的,最后参数。

      3、ParamNamesResolver 核心方法

        (1)构造器方法

    public ParamNameResolver(Configuration config, Method method) {
        final Class<?>[] paramTypes = method.getParameterTypes();
        final Annotation[][] paramAnnotations = method.getParameterAnnotations();
        final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
        int paramCount = paramAnnotations.length;
        // get names from @Param annotations
        for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
          if (isSpecialParameter(paramTypes[paramIndex])) {
            // skip special parameters
            continue;
          }
          String name = null;
          for (Annotation annotation : paramAnnotations[paramIndex]) {
            if (annotation instanceof Param) {
              hasParamAnnotation = true;
              name = ((Param) annotation).value();
              break;
            }
          }
          if (name == null) {
            // @Param was not specified.
            if (config.isUseActualParamName()) {
              name = getActualParamName(method, paramIndex);
            }
            if (name == null) {
              // use the parameter index as the name ("0", "1", ...)
              // gcode issue #71
              name = String.valueOf(map.size());
            }
          }
          map.put(paramIndex, name);
        }
        names = Collections.unmodifiableSortedMap(map);
      }

          可以看到该主要就是给成员变量 names 赋值的,该 names 是一个SortedMap

    private final SortedMap<Integer, String> names;

           流程:

            ① 获取每个标了 Param 注解的参数的 @Param 的值:id,lastName;然后赋值给 name;

            ② 每次解析一个参数就给 map中保存一个信息:(key:参数索引;value:name的值)

              name的值:

                如果标注了 @Param 注解,name 的值就是注解的值;

                没有注解:

                  a 是否开启了全局配置:useActualParamName(jdk1.8):name=参数名;

                  b 否则 name 就等于 map.size(),相当于当前元素的索引。

          例如我们传递的参数为:

    @Param("id")Integer id, @Param("lastName") String lastName
    

           封装完的 names 的值就为:

    {0=id, 1=lastName}
    

          如果此时再添加一个参数并,没有使用@Param

    @Param("id")Integer id, @Param("lastName") String lastName, Integer age
    

          封装完的 names 的值就是:

    {0=id, 1=lastName,2=2}
    

          最终的 names:

          

        (2)getNamedParams 方法

    public Object getNamedParams(Object[] args) {
        final int paramCount = names.size();
        //1、参数为null直接返回
        if (args == null || paramCount == 0) {
          return null;
         
        //2、如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回
        } else if (!hasParamAnnotation && paramCount == 1) {
          return args[names.firstKey()];
          
        //3、多个元素或者有Param标注
        } else {
          final Map<String, Object> param = new ParamMap<Object>();
          int i = 0;
          
          //4、遍历names集合;{0=id, 1=lastName,2=2}
          for (Map.Entry<Integer, String> entry : names.entrySet()) {
          
              //names集合的value作为key;  names集合的key又作为取值的参考args[0]:args【1,"Tom"】:
              //eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
            param.put(entry.getValue(), args[entry.getKey()]);
            
            
            // add generic param names (param1, param2, ...)param
            //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
            //效果:有Param注解可以#{指定的key},或者#{param1}
            final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
            // ensure not to overwrite parameter named with @Param
            if (!names.containsValue(genericParamName)) {
              param.put(genericParamName, args[entry.getKey()]);
            }
            i++;
          }
          return param;
        }
      }
    }

        其中 names 中的值在构造器方法中赋值完毕,然后就根据 names 和参数args 来进行组装解析完的参数。

        

         到最后可以看到,返回的 param中不仅有 names 中value作为key的,还有以param为前缀为 key 的。

        解析完参数后,交给 sqlSession 执行,最终把结果 result 返回。

       

  • 相关阅读:
    file is universal (3 slices) but does not contain a(n) armv7s slice error for static libraries on iOS
    WebImageButton does not change images after being enabled in Javascript
    ajax OPTION
    编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
    获取海洋天气预报
    C#线程系列教程(1):BeginInvoke和EndInvoke方法
    js控制只能输入数字和小数点
    Response.AddHeader(,)
    ManualResetEvent的理解
    Convert.ToInt32、int.Parse(Int32.Parse)、int.TryParse、(int) 区别
  • 原文地址:https://www.cnblogs.com/niujifei/p/15228035.html
Copyright © 2011-2022 走看看