Mybatis源码分析之接口调用

上一章已经学习了初始化的过程,经过读取配置文件的形式已经得到了SqlSessionFactory,该对象持有Configuration。 给出总的时序图,带着问题来看源码sql

先写一个简单的例子

SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
    List<Product> productList = productMapper.selectProductList();
    for (Product product : productList) {
    System.out.printf(product.toString());
    }
} finally {
    sqlSession.close();
}
复制代码

在调用mapper的方法以前,第一步必定是先建立一个sqlsession,而后根据获取的到sqlsession来获取mapper代理对象。这里就用到了初始化中的knowMappers,经过传入的ProductMapper.class获取到初始化时保存的MapperProxyFactory,而且经过mapperProxyFactory.newInstance(sqlSession);来获取mapperProxy代理对象缓存

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
复制代码

当调用了代理对象的某一个代理函数后,这个调用请求首先会被发送给代理对象处理类MapperProxy的invoke()函数,MapperProxy实现了InvocationHandler,重写其invoke方法bash

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      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);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
复制代码

实际invoke方法在调用以前先缓存了method,保存在MapperProxy类的一个名为methodCache的Map中,并返回一个MapperMethod对象,而后开始调用excute方法。session

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;
  }
复制代码

能够看到excute方法根据不一样操做调用了不一样的方法,在这以select语句多条结果的sql为例。因此走的是app

result = executeForMany(sqlSession, args);
复制代码

executeForMany调用了sqlSession.<E>selectList方法来完成功能,实际是调用了sqlSessionProxy的代理ide

public <E> List<E> selectList(String statement, Object parameter) {
    return this.sqlSessionProxy.<E> selectList(statement, parameter);
}
复制代码

通过动态代理调用以后,来到了DefaultSqlSessionselectList方法函数

@Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
复制代码

能够看到该方法从configuration获取了MappedStatement,这个方法是经过Map的get方法获取到对应的MappedStatement,获取到以后调用executor.query方法,获取到BoundSql即绑定的sql学习

public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }
复制代码

在获取到BoundSql以后在进行excutor.query方法,这个方法的底层其实就是PreparedStatement,封装在下面这段代码的queryFromDatabase方法里面,内部的具体调用就再也不详细讲解,因此说Mybatis就是底层jdbc的封装。ui

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
复制代码

最后的结果实际上是个list,结果以object返回。this

最后来回顾一下调用过程,首先经过SqlSessionFactory获取sqlsession,经过sqlsessionknowMappers中查询MapperProxy代理对象,调用其invoke方法,该方法其实用调用了MapperMethodinvoke,最后这个方法的底层实现其实就是jdbc封装。

相关文章
相关标签/搜索