Mybatis源码阅读系列(七)

12 篇文章 0 订阅
9 篇文章 0 订阅

今天继续来看下MapperMethod.execute方法后续通过Executor都做了些什么?今天主要梳理整个流程,不会过的很细,后续有需要会详细过一些重要类的实现,比如MetaObject。

MapperMethod.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());
  }
、、、、、、、、、、、、、、、、、省略
  }
  return result;
}

可以看出,这里会根据我们前面构造的SqlCommand的type去进行分类执行,这个type是如何得到的呢?根据xml里面的标签或者mapper接口里面的注解,就是SELECT这个type,注意FLUSH对应的是@Flush注解,用来刷新用的。

很容易看出select的实现会比其他场景复杂的多,主要是有返回值的解析和数据组装。

INSERT的调用流程

我就以第一个Insert来描述一下整个流程。首先拿到Param,这个在上一张说过整个参数的详细获取流程和主要的实现类,这里就不再赘述。

然后是调用defaultSqlsession的insert方法,很有意思的是,insert其实会调用update方法,其实也很好理解所有的insert,update,delete其实在数据库看来都是update。

然后一直往下会到BaseExecutor的doUpadte方法,这个是一个抽象方法,会由它的子类实现,这三个子类对应的三个type,simple,resued,batch,你设置的哪个就走到哪个里面,如果没有设置默认是走的simleExecutor。

这里会调用BaseStatementHandler的构造函数,其他不重要,比较重要的是会去生成BoundSql,它里面会持有所有xml里面的sql语句和参数,属性如下:

private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;

这个是一个很重要的类,如果更改这个类,你可以做很多事,比如有的基于mybatis的分页插件实现就是在修改boundSql里面的sql,在后面加上你设定好的page和size就行。

然后进入simleExecutor的prepareStatement方法,这个方法做的事情也比较简单:

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

获取连接,生成statement,然后调用prepareStatement的set方法进行赋值,这个和我最开始基础知识里面说的jdbc底层操作是一样的。

最终实现是在DefaultParameterHandler的setParameters方法:

  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

最后的typeHandler.setParameter(ps, i + 1, value, jdbcType);就是进行赋值,只是这里面会根据你定义的typeHandler做相应的类型处理而已。

设置完值之后会调用prepareStatement的execute方法,具体实现是在PreparedStatementHandler的update方法:

public int update(Statement statement) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  int rows = ps.getUpdateCount();
  Object parameterObject = boundSql.getParameterObject();
  KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
  return rows;
}
总结

今天简单梳理了下Mybatis在动态代理的方法调用之后是如何进行对数据库的操作的,底层是交给executor去做的,然后会给具体的StatementHandler操作,当然里面还有很多细节,比如如何对xml数据赋值,类似#{a.b.c}这种,还有在哪里将sql的#{}参数替换为?的,有兴趣的可以自己看看,或者留言,后续会更新该部分内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值