mybatis传参源码解析

本文详细解析MyBatis框架中Mapper接口传参规则,包括不同参数类型、注解使用及XML配置方式,帮助理解参数解析过程及提高开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mybatis3.5.4源码注释:https://gitee.com/ygtbdmz/mybatis_source_code_learning.git

一、主要解决的问题

使用mybatis框架已经有几年了,对于mapper接口传参的规则一直很模糊,大多数时候都是自己慢慢测试,尝试加@Param注解,尝试把mapper的参数名传入xml......然后就是各种试错,正好最近是在看源码,也就专门研究了下这个传参,下面专门记录下。

二、Mybatis的Mapper传参数的类型

示例类型:

方法参数

xml格式

说明

(Integer i)

#{i}

#内可以是任意字母都行

(Integer i,String b)

#{arg0},#{arg1} | #{param1},#{param2}

包含两种设置值的方式,arg和param,一个从0开始,一个从1开始,一次向后累加

(Blog blog)

#{id},#{title}

博客对象包含id、title、content属性

(Blog blog,Integer i)

#{arg0.id},#{arg1} | #{param1.id},#{param2}

多参数默认就是从arg0,arg1….或者param1,param2….,arg和param也可以混用,在赋值的时候,这些都会存在一个Map中

(List<Integer> ids)

<foreach item="item" collection="list" separator="," open="("close=")" index="">

#{item}

</foreach>

collection可以传入list或者collection,这里需要注意,只有传入的List的子类才能使用list,详情看下面的源码

(List<Integer> ids,Integer title)

<foreach item="item" collection="arg0" separator="," open="("close=")" index="">

#{item}

</foreach>

And title=#{arg1}

Arg0和arg1还是可以替换为param1和param2

(Integer[] ids)

<foreach item="item" collection="array" separator="," open="("close=")" index="">

#{item}

</foreach>

collection只能传入array

(Integer[] ids,Integer title)

<foreach item="item" collection="arg0" separator="," open="("close=")" index="">

#{item}

</foreach>

And title=#{arg1}

Arg0和arg1还是可以替换为param1和param2

(@Param("i") Integer i)

#{i} | #{param1}

只能输入id或者param1

(@Param("i")Integer i,@Param("b")String b)

#{i},#{b} | #{param1},#{param2}

 

(@Param("blog")Blog blog)

#{blog.id} |  #{param1.id}

 

(@Param("blog")Blog blog,@Param("i")Integer i)

#{blog.id},#{i} | #{param1.id},#{param2}

 

(@Param("ids")List<Integer> ids)

<foreach item="item" collection="ids" separator="," open="("close=")" index="">

#{item}

</foreach>

collection可以是ids或者param1

(@Param("ids")List<Integer> ids,@Param("title")Integer title)

<foreach item="item" collection="ids" separator="," open="("close=")" index="">

#{item}

</foreach>

And title=#{title}

collection可以是ids或者param1,title可以是tile或者param2

(@Param("ids")Integer[] ids)

<foreach item="item" collection="ids" separator="," open="("close=")" index="">

#{item}

</foreach>

collection可以是ids或者param1

(@Param("ids")Integer[] ids,@Param("title")Integer title)

<foreach item="item" collection="ids" separator="," open="("close=")" index="">

#{item}

</foreach>

And title=#{title}

collection可以是ids或者param1,title可以是tile或者param2

Mybatis的Mapper参数解析,返回参数的位置和参数的标记:

public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    //获取参数注解
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<>();
    //参数注解个数
    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.
        //没有@Param注解的执行
        if (config.isUseActualParamName()) {
          //会返回arg0、arg1...这样的数据,然后和参数的位置关联
          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是SortedMap,key是参数的位置,从0开始,value是参数的@Param中的value
    names = Collections.unmodifiableSortedMap(map);
  }

Mybatis的参数标记和具体值关系映射解析源码:

public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];                        //1
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);    //2
        // add generic param names (param1, param2, ...)
        // 添加通用参数名
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        // 确保不覆盖用@Param命名的参数
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);    //3
        }
        i++;
      }
      return param;
    }
  }

集合数组解析源码:

//object是所有的参数的映射,如果不是Collection和数组就返回前面解析的param1,param2....,如果是那么就不能使用param1
  private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      StrictMap<Object> map = new StrictMap<>();
      //默认Collection的所有子类都可以传入collection
      map.put("collection", object);
      if (object instanceof List) {
        //如果是List的子类,那么还可以传入list
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      StrictMap<Object> map = new StrictMap<>();
      //如果是数组传入array
      map.put("array", object);
      return map;
    }
    return object;
  }

三、总结

mybatis对传入的参数处理如果没有加@Param注解,那么在多参数的情况下默认会给占位符添加arg0,arg1....,如果添加了注解那么就会把arg替换为@Param的value的值,然后默认还会增加通用的占位符名称param1,param2...。如果是单参数的那么区分集合类型还是数组类型还是其他类型,集合、数组的占位符参考wrapCollection的返回即可,如果是其他类型,在没有注解的时候基础类型的占位符可以随便填写,对象类型可以直接使用参数名.属性来作为占位符,在有注解的时候占位符是@Param的value.属性或者param1.属性。

### 如何在 MyBatis 中通过注解方式传递参数给 Mapper 在 MyBatis 的注解映射器中,可以通过多种方法向 SQL 查询语句传递参数。以下是详细的说明以及一个完整的例子。 #### 参数传递的方式 1. **单个参数**: 如果只有一个参数,则可以直接将其作为查询的一部分传入。 2. **多个参数**: 当有多个参数时,可以使用 `@Param` 注解来指定每个参数的名字[^1]。 #### 示例代码 下面是一个具体的示例,展示如何通过注解方式传递参数到 MyBatis 映射器: ```java import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; public interface BlogMapper { /** * 使用 @Param 注解显式命名参数 */ @Select("SELECT * FROM blog WHERE id = #{blogId} AND author_id = #{authorId}") List<Blog> findBlogByIdAndAuthor(@Param("blogId") int blogId, @Param("authorId") String authorId); } ``` 在这个例子中: - `@Param("blogId")` 和 `@Param("authorId")` 是用来为对应的 Java 方法参数赋名的。 - 这些名字会被用于 SQL 语句中的占位符 `${}` 或者 `#{}` 中。 #### 执行流程解析 当调用上述接口的方法时,MyBatis 将会按照以下顺序操作: 1. 创建代理对象并拦截方法调用。 2. 解析方法签名及其参数。 3. 构建相应的 `BoundSql` 对象,其中包含了动态生成的 SQL 字符串和参数值。 4. 调用底层的 `StatementHandler` 来准备、设置参数、批量处理或者更新/查询数据库[^2]。 #### 缓存机制的影响 如果启用了缓存功能(例如 Ehcache),则 MyBatis 可能会在执行实际的数据库查询之前尝试从缓存中获取数据[^3]。因此,在设计注解式的 Mapper 接口时,也需要考虑缓存策略对性能的影响。 #### 完整的工作原理概述 为了更好地理解整个过程,可以从源码层面分析 MyBatis 是如何完成这些工作的。具体来说,就是追踪从 MethodInterceptor 到 SqlSessionExecutor 再到最后 StatementHandler 的一系列调用链路[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值