Mybatis复杂参数传参取参方式总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Weixiaohuai/article/details/90605569

一、简介

使用Mybatis最大的特性就是sql需要自己写,而写sql难免需要传递多个参数。相信大家在使用Mybatis的时候都遇到过 "Parameter 'array' not found. Available parameters are [xsids, param1]" 类似这样的错误,本文将总结一下Mybatis复杂参数传参取参的方式。

二、不使用@Param注解

【a】传递List<xx>类型参数:当不使用@Param注解时,在xml中通过foreach循环,此时必须指定collection=”list”或者collection=”collection”才能取出参数;
(1). Mapper.java:

/**
     * mybatis传复杂参数(不使用@Param("xxx")指定参数名称)
     */
    List<Map<String, Object>> getStudentsByListNoParamName(List<Integer> pkids);

(2). Mapper.xml:

<!--
        List<Map<String, Object>> getStudentsByListNoParamName(List<Integer> pkids);
        注意: 因为没有通过@Param指定参数名称,mybatis规定如果是Collection并且没有指定参数名称,那么collection="list"必须为list或collection
        否则报错:Parameter 'xxx' not found. Available parameters are [collection, list]
    -->
    <select id="getStudentsByListNoParamName" resultType="map" parameterType="list">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="collection" open="(" close=")" separator="," index="index" item="pkid">
            #{pkid}
        </foreach>
    </select>

或者:

<!--
        List<Map<String, Object>> getStudentsByListNoParamName(List<Integer> pkids);
        注意: 因为没有通过@Param指定参数名称,mybatis规定如果是Collection并且没有指定参数名称,那么collection="list"必须为list或collection
        否则报错:Parameter 'xxx' not found. Available parameters are [collection, list]
    -->
    <select id="getStudentsByListNoParamName" resultType="map" parameterType="list">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="list" open="(" close=")" separator="," index="index" item="pkid">
            #{pkid}
        </foreach>
    </select>

(3). 单元测试代码:

    @Test
    public void testNoParams() {
        List<Integer> pkids = new ArrayList<>();
        pkids.add(1);
        pkids.add(2);
        List<Map<String, Object>> students1 = studentMapper.getStudentsByListNoParamName(pkids);
        for (Map<String, Object> map : students1) {
            System.out.println(map);
        }
    }

(4).运行结果:

如果xml写错为下面,则会报错:

   <select id="getStudentsByListNoParamName" resultType="map" parameterType="list">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="xsids" open="(" close=")" separator="," index="index" item="pkid">
            #{pkid}
        </foreach>
    </select>


【b】传递Array数组类型参数:当不使用@Param注解时,在xml中通过foreach循环,此时必须指定collection=”array”才能取出参数;
(1). Mapper.java:

List<Map<String, Object>> getStudentsByArrayNoParamName(Integer[] pkids);

(2). Mapper.xml:

<!--
        List<Map<String, Object>> getStudentsByArrayNoParamName(Integer[] pkids);
        注意: 因为没有通过@Param指定参数名称,mybatis规定如果是Array数组类型并且没有指定参数名称,那么collection="array"必须为array
        否则报错:假如我们指定collection="array1",那么报错如下: Parameter 'array1' not found. Available parameters are [array]
    -->
    <select id="getStudentsByArrayNoParamName" resultType="map">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="array" open="(" close=")" separator="," index="index" item="pkid">
            #{pkid}
        </foreach>
    </select>

(3).单元测试代码:

    @Test
    public void testNoParams() {
        List<Map<String, Object>> students2 = studentMapper.getStudentsByArrayNoParamName(new Integer[]{1, 2, 3});
        for (Map<String, Object> map : students2) {
            System.out.println(map);
        }
    }

(4). 运行结果:

如果xml文件错写成,则会报错。

<select id="getStudentsByArrayNoParamName" resultType="map">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="array12" open="(" close=")" separator="," index="index" item="pkid">
            #{pkid}
        </foreach>
    </select>


【c】传递Map类型参数:当不使用@Param注解时,在xml文件可以通过#{map的key}取出参数值;
(1). Mapper.java:

 List<Map<String, Object>> getStudentsByMapNoParamName(Map<String, Object> param);

(2). Mapper.xml:

<!--
        List<Map<String, Object>> getStudentsByMapNoParamName(Map<String, Object> param);
        注意: 因为没有通过@Param指定参数名称,mybatis规定如果是map类型并且没有指定参数名称,那么在xml中取值的时候:
            对于普通属性:对应map.put('aaa',bbb) map中的key键
            对于集合类型属性:也是对应map.put('aaa',bbb) map中的key键
            对于这个示例,如下:
            paramMap.put("sname", "zhangsan2");  取值方式  ->  #{sname}
            paramMap.put("pkids", pkids2);       取值方式  -> collection="pkids"
    -->
    <select id="getStudentsByMapNoParamName" parameterType="map" resultType="map">
        select * from tbl_student t
        where 1 = 1
        <if test="sname!=null and sname!=''">
            and t.s_name = #{sname}
        </if>
        <if test="pkids != null and pkids.size() > 0">
            and t.s_id in
            <foreach collection="pkids" open="(" close=")" separator="," index="index" item="pkid">
                #{pkid}
            </foreach>
        </if>
    </select>

(3).单元测试代码:

 @Test
    public void testNoParams() {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("sname", "zhangsan2");
        List<Integer> pkids2 = new ArrayList<>();
        pkids2.add(1);
        pkids2.add(2);
        paramMap.put("pkids", pkids2);
        List<Map<String, Object>> students3 = studentMapper.getStudentsByMapNoParamName(paramMap);
        for (Map<String, Object> map : students3) {
            System.out.println(map);
        }
    }

(4). 运行结果:

三、使用@Param注解

【a】传递List<xx>类型参数:假如指定了参数名称为xsids,那么取值方式:collection="xsids", 此时默认的collection、list会失效;
(1).Mapper.java:

/**
     * mybatis传复杂参数(使用@Param("xxx")指定参数名称)
     */
    List<Map<String, Object>> getStudentsByListWithParamName(@Param("xsids") List<Integer> pkids);

(2).Mapper.xml:

  <!--
        List<Map<String, Object>> getStudentsByListWithParamName(@Param("xsids") List<Integer> pkids);
        因为我们指定了参数名称为xsids,所以取值方式:collection="xsids", 此时默认的collection、list会失效
        如果我们使用collection="list", 会报错:Parameter 'list' not found. Available parameters are [xsids, param1]
    -->
    <select id="getStudentsByListWithParamName" resultType="map" parameterType="list">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="xsids" open="(" close=")" separator="," index="index" item="xsid">
            #{xsid}
        </foreach>
    </select>

(3).单元测试代码:

    @Test
    public void testWithParams() {
        List<Integer> pkids = new ArrayList<>();
        pkids.add(1);
        pkids.add(2);
        List<Map<String, Object>> students1 = studentMapper.getStudentsByListWithParamName(pkids);
        for (Map<String, Object> map : students1) {
            System.out.println(map);
        }
    }

(4).运行结果:

如果xml中collection不是“xsid”,那么会报错

<select id="getStudentsByListWithParamName" resultType="map" parameterType="list">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="list" open="(" close=")" separator="," index="index" item="xsid">
            #{xsid}
        </foreach>
    </select>


【b】传递Array类型参数:假如我们指定了参数名称为xsids,那么取值方式:collection="xsids",此时默认的array会失效;

(1).Mapper.java:

 List<Map<String, Object>> getStudentsByArrayWithParamName(@Param("xsids") Integer[] pkids);

(2).Mapper.xml:

    <!--
        List<Map<String, Object>> getStudentsByArrayWithParamName(@Param("xsids") Integer[] pkids);
        因为我们指定了参数名称为xsids,所以取值方式:collection="xsids",此时默认的array会失效.
        如果我们使用collection="array", 会报错:Parameter 'array' not found. Available parameters are [xsids, param1]
    -->
    <select id="getStudentsByArrayWithParamName" resultType="map">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="xsids" open="(" close=")" separator="," index="index" item="pkid">
            #{pkid}
        </foreach>
    </select>

(3).单元测试代码:

@Test
    public void testWithParams() {
        List<Map<String, Object>> students2 = studentMapper.getStudentsByArrayWithParamName(new Integer[]{1, 2, 3});
        for (Map<String, Object> map : students2) {
            System.out.println(map);
        }
    }

(4).运行结果:

如果xml中的collection不是@Param指定的名字的话,同样会报错:

<select id="getStudentsByArrayWithParamName" resultType="map">
        select * from tbl_student t
        where 1 = 1
        and t.s_id in
        <foreach collection="xsids123" open="(" close=")" separator="," index="index" item="pkid">
            #{pkid}
        </foreach>
    </select>


【c】传递Map类型参数:假如我们使用@Param("paramMap")指定了参数名称为paramMap,那么我们在xml中取值的时候,就需要通过paramMap.xxx,xxx就是对应map中的键;

(1).Mapper.java:

 List<Map<String, Object>> getStudentsByMapWithParamName(@Param("paramMap") Map<String, Object> param);

(2).Mapper.xml:

<!--
        List<Map<String, Object>> getStudentsByMapWithParamName(@Param("paramMap") Map<String, Object> param);
        由于我们使用@Param("paramMap")指定了参数名称为paramMap,所以我们在xml中取值的时候,就需要通过paramMap.xxx,
        xxx就是对应map中的键,如下
        paramMap.put("sname", "zhangsan2");  取值方式 -> #{paramMap.sname}
        paramMap.put("pkids", pkids2);       取值方式 -> collection="paramMap.pkids"
    -->
    <select id="getStudentsByMapWithParamName" parameterType="java.util.Map" resultType="map">
        select * from tbl_student t
        where 1 = 1
        <if test="paramMap.sname!=null and paramMap.sname!=''">
            and t.s_name = #{paramMap.sname}
        </if>
        <if test="paramMap.pkids != null and paramMap.pkids.size() > 0">
            and t.s_id in
            <foreach collection="paramMap.pkids" open="(" close=")" separator="," index="index" item="pkid">
                #{pkid}
            </foreach>
        </if>
    </select>

(3).单元测试代码:

@Test
    public void testWithParams() {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("sname", "zhangsan2");
        List<Integer> pkids2 = new ArrayList<>();
        pkids2.add(1);
        pkids2.add(2);
        paramMap.put("pkids", pkids2);
        List<Map<String, Object>> students3 = studentMapper.getStudentsByMapWithParamName(paramMap);
        for (Map<String, Object> map : students3) {
            System.out.println(map);
        }
    }

(4).运行结果:

注意,取出参数的时候需要与Map中的key值对应上,否则取不出参数值。

四、mybatis参数封装成map源码解析

  • 下面通过查看mybatis源码了解一下里面是怎么封装参数的,同时可以加深我们对传递参数和取出参数的理解。Mybatis中参数解析过程主要是在这个类 -> ParamNameResolver.java: 下面对源码做了一些注释说明,大家也可以debug一步一步查看其中怎么封装参数的。
    
    // 假设args=[2,学生2],names={0=sid,1=sname}
    public Object getNamedParams(Object[] args) {
        int paramCount = this.names.size();
        //参数不为空时,封装成map
        if (args != null && paramCount != 0) {
            //如果没有@Param注解并且参数个数只有一个,直接返回args[0]
            if (!this.hasParamAnnotation && paramCount == 1) {
                return args[(Integer)this.names.firstKey()];
            } else {
                Map<String, Object> param = new ParamMap();
                int i = 0;
                //遍历names: {0=sid,1=sname}
                for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                    Entry<Integer, String> entry = (Entry)var5.next();
                    //names集合的value作为key;
                    //names集合的key作为取值的参考args[0]
                    //params: sid=args[0]=2  sname=args[1]=学生2
                    param.put(entry.getValue(), args[(Integer)entry.getKey()]);
                    String genericParamName = "param" + String.valueOf(i + 1);
                    //注意从1开始, param1...paramN
                    //params: param1=2  param2=学生2
                    if (!this.names.containsValue(genericParamName)) {
                        param.put(genericParamName, args[(Integer)entry.getKey()]);
                    }
                }

                return param;
            }
        } else {
            //参数为空的时候直接return null
            return null;
        }
    }

五、总结

  • 方法不加@param注解时,并且只有一个参数的时候:

【a】String类型等,xml中可以通过#{任意值}都可以取出参数值;
【b】Entity对象类型:xml中可以通过#{实体对象中的属性名称}取出参数值;
【c】Map 类型:xml文件可以通过#{map中的键key}可以取出参数值;
【d】List 类型:xml中必须要写collection=”list”或者 collection=”collection”才能够取出参数值;
【e】Array数组类型:xml中必须要写collection=”array”才能够取出参数值;

  • 方法加@param注解时,并且只有一个参数的时候:

【a】String类型等:如@param(value="name") ,xml中可以使用#{name}或者#{param1}取出参数值;
【b】对象Entity类型: 如@param(value="student"),xml可以通过#{student.对象属性}取值,或者#{param1.对象属性}取出参数值;
【c】Map 类型:如@param(value="paramsMap"),xml可以通过#{paramsMap.map的key}取值,或者#{param1.map的key}取出参数值;
【d】List集合类型:如@param(value="ids"),xml中可以通过 collection=“ids”或者collection=”param1”取出参数值;
【e】Array类型:如@param(value="ids"),此时要写collection=“ids”或者collection=”param1”取出参数值;

  • 当有多个参数的时候,如果没有指定@Param,那么可以使用#{param1},#{param2}…#{paramN}取出 或者 使用#{0},#{1}…#{N}取出,否则会报参数找不到异常;如果指定了@Param(value="xxx"),那么取参数的时候 可以使用@Param中指定的名称#{xxx}取出 或者 使用#{param1},#{param2}…#{paramN}取出。

猜你喜欢

转载自blog.csdn.net/Weixiaohuai/article/details/90605569
今日推荐