MapperMethod of mybatis source code analysis

Previous:

Mapper dynamic proxy for mybatis source code analysis

https://my.oschina.net/u/657390/blog/748646

The relationship between MapperMethod and MapperProxy, MapperProxyFactory, MapperRegistry, Configuration

When analyzing the mapper dynamic proxy, it can be seen that the final execution is mapperMethod.execute

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //当执行的方法是继承自Object时执行this里的相应方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //最终执行的是mapperMethod.execute
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

Then analyze mapperMethod.execute()

First look at cachedMapperMethod(method) in MapperProxy

/**
 * methodCache中已经存在传入的参数method
 * 则从methodCache中取MapperMethod,
 * 否则根据method生成MapperMethod实例并存储到methodCache中
 *
 * @param method
 * @return
 */
  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

MapperProxy中methodCache

private final Map<Method, MapperMethod> methodCache;

Trace the source of methodCache

//MapperProxy构造方法  
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

MapperProxyFactory中

private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();  

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

It can be seen from the source code of MapperProxyFactory that methodCache is initially an empty ConcurrentHashMap,

It can be seen from the cachedMapperMethod(method) in MapperProxy

 The incoming parameter method already exists in the methodCache, then the MapperMethod is taken from the methodCache, otherwise the MapperMethod instance is generated according to the method and stored in the methodCache.

Then look at the source code of MapperMethod

Construction method

  /**
   * @param mapperInterface 接口
   * @param method 调用的方法
   * @param config 配置
   */
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

SqlCommand and MethodSignature instances are created

command type

public enum SqlCommandType {
  UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}

The most important method in MapperMethod

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

First call the method in MethodSignature

public Object convertArgsToSqlCommandParam(Object[] args)

Process the parameters. Then,

if command type is

INSERT, UPDATE, DELETE

Then directly call the corresponding method of sqlSession

if command type is

SELECT

Then call the corresponding query method in sqlSession according to the returned result.

So far, the process of MapperMethod from creation to execution has been roughly analyzed.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325458163&siteId=291194637