mybatis源码分析(下)

上篇我感觉逻辑有点乱,开篇先理理主要逻辑:
1.加载配置,构造Configuration对象
2.创建DefaultSqlSessionFactory工厂
3.通过DefaultSqlSessionFactory.openSession()创建DefaultSqlSession对象,同时会创建Executor,设置到SqlSession属性中。
4.通过sqlSession.getMapper()方法,返回一个mapper代理对象
5.通过mapper代理对象,执行具体查询

mybatis主要有四大神器:Executor,StatmentHandler,ParameterHandler,ResultSetHandler
上篇已经分析了Executor,StatmentHandler,ParameterHandler的源码,但是感觉不是连贯,下面先把逻辑连一下:
上篇说了Executor是在创建SqlSession的时候就会创建Executor(执行器)。

那其他几个对象是在什么时候调用的呢?具体是在什么去查的数据库呢?
具体开端就在对mapper的代理里面:

public class MapperProxy<T> implements InvocationHandler

这个类就实现了对mapper的增强逻辑

  @Override
  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);
    //在代理mapper具体调用某个方法的时候
    return mapperMethod.execute(sqlSession, args);
  }

////缓存  Map<Method, MapperMethod> methodCache
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;
  }

下面就是主要的mapperMethod.execute(SqlSession,args)的源码

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //判断是什么数据库操作
    switch (command.getType()) {
      case INSERT: {
      // 处理方法的参数名 一个的时候不用匹配  当有多个的时候需要用@param申明的name匹配
      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());
    }
    return result;
  }

到这,流程就是代理对象在具体执行的时候,是调用的sqlSession提供的方法

下面接着看,看看sqlSession.selectOne();

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // 也是调用的selectList方法,判断结果为1才返回,大于1就报错
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }
//主要看selectList方法
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    //方法名和数据库操作对象关系以及sql关联的sql标签信息(比如resultmap,sql,statmentType等熟悉)
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
  }

到这 ,可以看到sqlSession在执行具体逻辑的时候调用的是执行器executor

接着看executor.query()


  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) {
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
    //清本地缓存
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {//callable
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {//主要的逻辑看这个方法
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      deferredLoads.clear();
      //如果本地缓存的作用域是statment,每次都会请缓存
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    //这个地方我估计是先放个占位的,后面查询完了会把结果放进去
    //key 必须你的sql sql参数 statmentid都一样 生成的key才会一样
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
    //下面主要看这个方法
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    //mybatis的一级缓存,缓存查询结果 ,默认开启了的
    localCache.putObject(key, list);
    return list;
  }

这个doQuery方法 具体就是executor中定义的方法,接着就是上篇的 “3.创建StatmentHandler对象…”

后面的大致流程就是:executor调用的是StatmentHandler对象(会创建数据库对象PrepareStatment),设置参数调用parameterHandler,结果映射调用ResultSetHandler对象,主要逻辑就完了。

发布了42 篇原创文章 · 获赞 29 · 访问量 2546

猜你喜欢

转载自blog.csdn.net/qq_32314335/article/details/103458208