zoukankan      html  css  js  c++  java
  • 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);可修饰为一个不可变的集合

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

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

  • 相关阅读:
    Hadoop系列:在Linux下部署hadoop 0.20.1
    EasyNet.Solr开发历程
    使用ios开发者证书
    多台电脑共用一个开发证书的方法
    ios学习第二天,类和属性
    ios开发第一天mvc介绍
    IOS的Bundle资源束制作
    ios证书申请,使用,调试,发布app
    paste命令
    shell的使用技巧
  • 原文地址:https://www.cnblogs.com/zhengzuozhanglina/p/11212200.html
Copyright © 2011-2022 走看看