Mybatis源码分析: MapperMethod功能讲解(1)

Mybatis源码分析: MapperMethod功能讲解(1)

      MapperMethod主要的功能是执行SQL的相关操作,在初始化时会实例化两个组件Sql命令(SqlCommand)和方法签名(MethodSignature)这两个组件会在后续进行详解,同时MapperMethod必须提供Mapper的接口路径,待执行的方法,配置Configuration作为入参。通过获取SqlCommand中的执行类型,MapperMethod才知道该Mapper接口将要执行什么样的操作。构造方法如下所示:

1 public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
2 //初始化SqlCommand
3 this.command = new SqlCommand(config, mapperInterface, method);
4 //初始化方法签名
5 this.method = new MethodSignature(config, mapperInterface, method);
6 }

Mybatis中定义了5种操作:SELECT,INSERT,UPDATE,DELETE,FLUSH。其中SELECT中包含了5中查询,结果类型处理器,返回NULL值,多条记录查询,单记录查询,游标查询,和map查询。相关操作和方法如下表对应。从表中可以看到,Sql执行方式本质上时通过sqlSession调用的,在SELECT操作中,虽然调用了MapperMethod中的方法,但本质上仍是通过Sqlsession下的select(),selectList(),selectCursor(),selectMap()等方法实现的。

操作  方法名
INSERT  sqlSession.insert()
UPDATE  sqlSession.update()
DELETE  sqlSession.delete()
FLUSH  sqlSession.flushStatements()
SELECT  executeWithResultHandler(sqlSession, args)
executeForMany(sqlSession, args)
method.returnsMap()
executeForCursor()
sqlSession.selectOne(command.getName(), param)

 1 public Object execute(SqlSession sqlSession, Object[] args) {
 2 Object result;
 3 //判断命令类型
 4 switch (command.getType()) {
 5 case INSERT: {
 6 //将参数转为Sql参数
 7 Object param = method.convertArgsToSqlCommandParam(args);
 8 //获取插入结果
 9 result = rowCountResult(sqlSession.insert(command.getName(), param));
10 break;
11 }
12 case UPDATE: {
13 Object param = method.convertArgsToSqlCommandParam(args);
14 result = rowCountResult(sqlSession.update(command.getName(), param));
15 break;
16 }
17 case DELETE: {
18 Object param = method.convertArgsToSqlCommandParam(args);
19 result = rowCountResult(sqlSession.delete(command.getName(), param));
20 break;
21 }
22 case SELECT:
23 if (method.returnsVoid() && method.hasResultHandler()) {
24 //带有结果处理器的
25 executeWithResultHandler(sqlSession, args);
26 result = null;
27 } else if (method.returnsMany()) {
28 //多条查询
29 result = executeForMany(sqlSession, args);
30 } else if (method.returnsMap()) {
31 //返回map
32 result = executeForMap(sqlSession, args);
33 } else if (method.returnsCursor()) {
34 //游标查询
35 result = executeForCursor(sqlSession, args);
36 } else {
37 //单条查询
38 Object param = method.convertArgsToSqlCommandParam(args);
39 result = sqlSession.selectOne(command.getName(), param);
40 }
41 break;
42 case FLUSH:
43 //清除操作
44 result = sqlSession.flushStatements();
45 break;
46 default:
47 throw new BindingException("Unknown execution method for: " + command.getName());
48 }
49 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
50 throw new BindingException("Mapper method '" + command.getName() 
51 + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
52 }
53 return result;
54 }


参数解析器

     在上述代码中经常能看见这样一句代码 method.convertArgsToSqlCommandParam(args);,该方法主要的功能是获取@Param注解上的参数值,而实现方式便是通过参数解析器(ParamNameResolver),convertArgsToSqlCommandParam(args)方法实际上调用的是ParamNameResolver下的getNamedParams(args)方法。在分析该方法之前,先看看构造器都做了些什么操作。

 1 public ParamNameResolver(Configuration config, Method method) {
 2 final Class<?>[] paramTypes = method.getParameterTypes(); //获取所有参数类型
 3 final Annotation[][] paramAnnotations = method.getParameterAnnotations();//获取方法中的所有注解
 4 final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
 5 //判断注解数组长度
 6 int paramCount = paramAnnotations.length;
 7 // get names from @Param annotations
 8 for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
 9 if (isSpecialParameter(paramTypes[paramIndex])) {
10 // skip special parameters
11 continue;
12 }
13 String name = null;
14 for (Annotation annotation : paramAnnotations[paramIndex]) {
15 //判断是否是Param标签的子类,也就是说@param中是否存在value值
16 if (annotation instanceof Param) {
17 hasParamAnnotation = true;
18 //获取标签中的value值
19 name = ((Param) annotation).value();
20 break;
21 }
22 }
23 if (name == null) {
24 // @Param was not specified.
25 //是否使用了真实值,也就是说没有设置value值
26 if (config.isUseActualParamName()) {
27 //获取方法中参数的名字
28 name = getActualParamName(method, paramIndex);
29 }
30 if (name == null) {
31 // use the parameter index as the name ("0", "1", ...)
32 // gcode issue #71
33 name = String.valueOf(map.size());
34 }
35 }
36 map.put(paramIndex, name);
37 }
38 //设置所有参数名为不可修改的集合
39 names = Collections.unmodifiableSortedMap(map);
40 }


      构造器同样需要两个入参,配置类和方法名,ParamNameResolver下包含了两个属性字段GENERIC_NAME_PREFIX属性前缀和参数集合names,构造器会将Method中所有参数级别的注解全部解析出来方法有序参数集中,names中存储形式为<参数下标,参数名>,如果在注解上设置了参数名,则会直接获取注解的value值,如果没有使用@Param注解,则使用真实的参数名,注意,真实参数名其实是arg0,arg1....的形式展现的,在判断真实参数名时,Mybatis会检查JDK版本是否包含java.lang.reflect.Parameter类,不存在该类的化会抛出ClassNotFoundException异常。 完成初始化后,就可以调用getNamedParams(args)方法了,如下代码所示,该方法使用了类中的names属性,从有序集合中取出所有的<参数索引,参数名>键值对>,随后填充到另外一个集合中,以<参数名,参数下标索引,>形式呈现,同时会保留一份<param+下标索引,参数下标>的键值对

 1 public Object getNamedParams(Object[] args) {
 2 //判断参数个数
 3 final int paramCount = names.size();
 4 if (args == null || paramCount == 0) {
 5 return null;
 6 }
 7 //返回首个参数 
 8 else if (!hasParamAnnotation && paramCount == 1) {
 9 return args[names.firstKey()];
10 } else {
11 final Map<String, Object> param = new ParamMap<Object>();
12 int i = 0;
13 for (Map.Entry<Integer, String> entry : names.entrySet()) {
14 //设置参数的值和键名
15 param.put(entry.getValue(), args[entry.getKey()]);
16 // add generic param names (param1, param2, ...)
17 //增加参数名
18 final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
19 // ensure not to overwrite parameter named with @Param
20 if (!names.containsValue(genericParamName)) {
21 param.put(genericParamName, args[entry.getKey()]);
22 }
23 i++;
24 }
25 return param;
26 }
27 }

需掌握的知识点

  • Method类的使用,常用的方法
方法名  作用
getName()  获取方法名
isVarArgs()  如果该方法声明为采用可变数量的参数,则返回true; 否则返回false
getModifiers()  获取权限修饰符
getReturnType()  获取返回类型
getExceptionTypes()  获取所有抛出的异常类型
getGenericReturnType () 返回Type类型
getParameterTypes()  获取所有参数的类型
getParameterCount()  获取所有参数的个数
getAnnotations()  获取方法级别的注解

  • 集合的使用

Collections.unmodifiableSortedMap(map);可修饰为一个不可变的集合

如何实现自定义注解,并对注解进行解析

见实现自定义注解,并对注解进行解析 文章

猜你喜欢

转载自www.cnblogs.com/zhengzuozhanglina/p/11212200.html