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.属性。