Mybatis 源 码 初 解 析 (二)

2023-11-09

言接上篇,我说过的我认为的三部分

  • 数据源
  • sql语句
  • 执行sql语句

上篇验证了数据源的一个连接,接下来就需要探究sql语句究竟是如何获得的。
熟悉mybatis的都知道sql的映射有两种方式,第一种是通过注解,第二种是通过xml文件。
实际上xml用的比较多,而且mybatis官方提倡使用xml文件
关于xml里的映射就是通过一个命名空间的绑定,通过全限名来进行一个绑定。类似于一个接口和接口的实现,然后注释就是在注释上进行一个接口的实现。两种是类似的。
但是我所说的如何获得sql语句是只框架本身。
我们得到了一个sqlseeionfactory 然后就要通过opensession获取到一个sqlsession

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

可以看到new DefaultSqlSession(configuration, executor, autoCommit); 把之前配置好的configuration和executor, autoCommit放进构造里,直接得到一个DefaultSqlSession 也就是我们会用到的sqlsession executor, autoCommit executor这个是什么东西,我们暂时不看 autoCommit 大概就是一个设置自动提交的东西把。
然后继续走,我们得到了一个sqlSeesion 按照官方文档给的教程。我们可以通过sqlsession执行selectone 或者是其他方法,这只是一种方式,更为推荐的是先getmapper(class),然后通过这个mapper去调用我们自己所定义好的接口,我们知道这个接口里的方法绑定着在mapper里写的sql语句。 所以这个getmapper方法他就是获得sql的关键 点进方法里我发现有个调用的顺序
首先是configuration.getMapper

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

然后是

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

然后进入到一个很关键的东西 我看到了proxy 这可是java的动态代理哎

  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);
    }
  }

显然 mapperProxyFactory.newInstance(sqlSession); 获得了一个mapperProxy的代理类实例 继续往下

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }

看到这个execute方法

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);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        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;
  }

看到在select的区域依然调用了selectone
所以说selectone和自定义selecy方法在本质上是相同的。底层是一样的 回到了之前两种方法的调用上。
点进selectone

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.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深入深入再深入

  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

本质上是调用了一个executor.query() baseexecutor的query方法

  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);
  }

这是什么 出现了 cachekey,熟悉mybatis都知道是有缓存的这个命名 看到之后就会联想到缓存啊。
先不说这个,之前是有一个getBoundSql的啊 ,这个命名就感觉自己有了胜利的曙光

 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();
        }
      }
    }

到这就已经获取到sql语句了。
我的猜想二又得到了一个验证。
整体流程是什么样的呢。我稍作梳理
sqlsessionfactory.opensession(通过configuration,executor,autocommit构建一个defaultsqlsession)—>sqlsession.getmapper(configuration.getmapper–>mapperregister.getmapper(f方法实现是mapperproxyfactory–》newInstance 获得一个mapperProxy) ) ---->invoke里面调用execute方法—>出现了selectone —>selectList(调用了executor.query()–> getBoundSql 到这里就是完全得到了一个sql语句了(仅仅以select为用例,我觉得其他的也类似)

剩下的就是如何执行sql语句了。
这一个流程重要的是getboundsql

Mybatis 源 码 初 解 析 (三)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mybatis 源 码 初 解 析 (二) 的相关文章

随机推荐