Mybatis源码详解之接口方法被执行流程源码解析

来源:互联网 发布:牵机药 知乎 编辑:程序博客网 时间:2024/06/11 16:32

        与上一篇Mybatis源码解析的博客已经隔了好长一段时间,最近发生了一些乱七八糟糟心的事情,甚至每天加班,没来得及写点什么,最近一个月的学习是乱的一塌糊涂。

        接着上一篇的分析,上一篇完成了所有配置文件的解析,将各个配置文件都解析到一个叫Configuration的类里,这些就是接口方法可以被执行的元数据,任何一个方法的执行必然依赖于此。接口方法执行流程就是怎样使用这些元数据呢?

        还是以最开始的实例工程引入方法执行,相信已经走到这里了,也肯定知道接口方法的执行就是对接口的一个动态代理。

        还记得上一篇博客中addMapper函数,最终把接口注册到一个Map缓存中,即knownMappers。

        下面开始今天的分析:

(1)对接口实现动态代理

          IPlayDao play = sqlSession.getMapper(IPlayDao.class);
主要就是这句,经过getMapper方法以后,返回的已经不是接口了,而是一个经过动态代理的代理类了。

public <T> T getMapper(Class<T> type) {    return configuration.<T>getMapper(type, this);  }public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    return mapperRegistry.getMapper(type, sqlSession);  }//没有什么值得分析的,前面两个类就是带着传入的接口层层传递@SuppressWarnings("unchecked")public <T> T getMapper(Class<T> type, SqlSession sqlSession) {  //这里就是上一篇一个mapper.xml文件中,namespace接口注册的一个缓存,从这个缓存中用接口提取出代理工厂  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);  }}

     带着接口步步深入,看到了proxy工厂,没有什么难易理解的部分,主要就是那个缓存中取代理工厂,这是初始化中非常关键的一个点。还有就是sqlSession的传递,也算是个关键,这块儿应该也算一种命令模式,因为未来不知道要调用接口的什么方法,所有要执行该方法的流程最终需要借助于sqlSession的分配,所以此处带着它层层传递,sqlSession是规划执行接口方法的总管。接着看代理工厂是怎么对所有的接口统一处理的。

public T newInstance(SqlSession sqlSession) {//sqlsession,接口都传入到了MapperProxy里,这个类就是实现InvocationHandler的类,methodCache这里传入一个空的参数    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);    return newInstance(mapperProxy);  }@SuppressWarnings("unchecked")protected T newInstance(MapperProxy<T> mapperProxy) {//看到了动态代理的实例化部分,相当于接口被实例化了,这就是getMapper最终返回的一个接口代理  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}
显然,所有的接口子这个工厂类里被代理了,下一次调用接口中的方法的时候,直接进入了MapperProxy类的invoke方法中了,MapperProxy类里也包含了所有用到的属性,知道是属于哪个接口的代理,还有sqlSession属性,该属性中包含有Configuration,Executor。下面接着分析这个MapperProxy类。

(2)动态代理的统一处理,invoke方法

Ball ball = play.selectBall("1");
调用接口的方法,直接跳转到MapperProxy类的invoke方法,具体看一下:

public class MapperProxy<T> implements InvocationHandler, Serializable {  private static final long serialVersionUID = -6424540398559729838L;  private final SqlSession sqlSession;  private final Class<T> mapperInterface;  private final Map<Method, MapperMethod> methodCache;  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {    this.sqlSession = sqlSession;    this.mapperInterface = mapperInterface;    this.methodCache = methodCache;  }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//首先判断该方法所属的类是否是一个对象,如果是直接反射调用该方法,显然,这里传递进来的是接口,不会进入if分支    if (Object.class.equals(method.getDeclaringClass())) {      try {        return method.invoke(this, args);      } catch (Throwable t) {        throw ExceptionUtil.unwrapThrowable(t);      }    }    //这里将传入的方法包装成为一个MapperMethod    final MapperMethod mapperMethod = cachedMapperMethod(method);    return mapperMethod.execute(sqlSession, args);  }      //包装每个被调用的方法  private MapperMethod cachedMapperMethod(Method method) {//这就是当时代理工厂传进来的空的methodCache参数,就是一个简单的Map缓存,提高效率,只要被调用过一次就不会重新包装,直接从缓存中取    MapperMethod mapperMethod = methodCache.get(method);    if (mapperMethod == null) {     //方法包装      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());      methodCache.put(method, mapperMethod);    }    return mapperMethod;  }}
这里就是动态代理的最关键类了,看起来是不是其实也很简单,下面看一下方法的包装里做了些什么。

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {//传入的三大参数构造出两个内部类,类似于初始化,封装资源的一个过程,后面分析一下这两个内部类    //这个是提取以前初始化时候注册的MappedStatement    this.command = new SqlCommand(config, mapperInterface, method);    //这个是对被调用方法的一个封装提取    this.method = new MethodSignature(config, method);  }//SqlCommand构造函数public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {//还记得在构建MappedStatement的过程中,有这么一句注册configuration.addMappedStatement(statement);//这里就是在构造提取MappedStatement的Map中的key    String statementName = mapperInterface.getName() + "." + method.getName();    MappedStatement ms = null;    if (configuration.hasStatement(statementName)) {      ms = configuration.getMappedStatement(statementName);    } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35      String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();      if (configuration.hasStatement(parentStatementName)) {        ms = configuration.getMappedStatement(parentStatementName);      }    }    if (ms == null) {      throw new BindingException("Invalid bound statement (not found): " + statementName);    }    //提取出ms,初始化了些参数    name = ms.getId();//其实这个就是mapper.xml中每个sql节点的id,也就是方法名    type = ms.getSqlCommandType(); //这个是该条sql语句的标签,是insert|select|delete|update    if (type == SqlCommandType.UNKNOWN) {      throw new BindingException("Unknown execution method for: " + name);    }  }//MethodSignature的构造函数,就是对调用方法的一个解剖封装,提取各项参数public MethodSignature(Configuration configuration, Method method) throws BindingException {    this.returnType = method.getReturnType();    this.returnsVoid = void.class.equals(this.returnType);    this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());    this.mapKey = getMapKey(method);    this.returnsMap = (this.mapKey != null);    this.hasNamedParameters = hasNamedParams(method);    this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);    this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);    this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));  }
这里其实也可以看作是一个初始化的过程,在执行方法之前,提取资源库中的各种该方法应用的资源

(3)开始方法调用的执行

在mapperProxy的invoke方法最后一句,即调用封装后的MapperMethod的execute方法,从这里展开了接口方法的具体执行

//传入sqlSession和待执行方法的参数(也就是sql语句中需要的参数(PrepareStatement的参数))public Object execute(SqlSession sqlSession, Object[] args) {    Object result;    //首先根据封装该方法时候所封装的type判断是什么类型的sql语句, 选择不同的方法处理    //insert    if (SqlCommandType.INSERT == command.getType()) {      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.insert(command.getName(), param));      //update    } else if (SqlCommandType.UPDATE == command.getType()) {      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.update(command.getName(), param));      //delete    } else if (SqlCommandType.DELETE == command.getType()) {      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.delete(command.getName(), param));      //select   讲解以select为例子,各中sql语句的流程基本一样    } else if (SqlCommandType.SELECT == command.getType()) {    //判断是select语句以后,还有不同的情况,就是封装方法时候的另一个类,methodsigature中的字段再次判断判断      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 {        Object param = method.convertArgsToSqlCommandParam(args);        result = sqlSession.selectOne(command.getName(), param);      }    } else {      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;  }
这里仍旧将sqlSession带在方法的参数里往下一级传递。

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {    List<E> result;    //作了一个简单处理,如果有多个参数,将其封装在一个Map里    Object param = method.convertArgsToSqlCommandParam(args);    //如果method有分页信息,进if分支,大多数情况不会用系统自带分页,都是自己写分页拦截器实现分页    if (method.hasRowBounds()) {      RowBounds rowBounds = method.extractRowBounds(args);      result = sqlSession.<E>selectList(command.getName(), param, rowBounds);    } else {    //sqlSession开启了真正的运筹帷幄的时候了(也就是命令模式应用的时候了,封装了各种功能,可以统一处理增删改查)      result = sqlSession.<E>selectList(command.getName(), param);    }    // issue #510 Collections & arrays support    if (!method.getReturnType().isAssignableFrom(result.getClass())) {      if (method.getReturnType().isArray()) {        return convertToArray(result);      } else {        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);      }    }    return result;  }
终于到了sqlSession调用它方法的时候了。即将运筹它下面的四大对象来完成调用流程。

//第一个参数statement,忘记了没有,封装方法的时候(SqlCommand类中),就是接口名.方法名;//第二个参数就是sql语句中缺的参数public <E> List<E> selectList(String statement, Object parameter) {    return this.selectList(statement, parameter, RowBounds.DEFAULT);  }public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {    //用这个statement从资源类中取出MappedStatement,这个类包含了mapper.xml文件中一个sql节点的所有信息      MappedStatement ms = configuration.getMappedStatement(statement);      //executor是什么,执行器,他来调度其他三个StatementHandler,ParameterHandler,ResultHandler来执行sql语句,      //他是怎么来的呢,在初始化出来sqlSession的时候就newExecutor过了,如果不传入特殊的Executor,会有一个默认的执行器      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);      return result;    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }
解析来到了Executor中,执行BaseExecutor中的qurey方法

//前两个参数,一个是sql节点,一个是传入的参数,后两个为nullpublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    //进来首先获取BoundSql,这个BoundSql是什么呢,他就是建立sql和参数的地方,调用ms的方法获取,下面具体看怎么获取BoundSql boundSql = ms.getBoundSql(parameter);    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);    return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }public BoundSql getBoundSql(Object parameterObject) {//ms中没干什么,直接将其转入下一层,sqlSource中获取,还记得初始化收构建SqlSource吗,这里确实是一个难点,下面再进入到SqlSource类    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();    if (parameterMappings == null || parameterMappings.size() <= 0) {      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;  }//这里是一个DynamicSqlSource的getBoundSql方法(mybatis中大多数的sql都是带有where,if等各种条件的也就是动态的)public BoundSql getBoundSql(Object parameterObject) {    DynamicContext context = new DynamicContext(configuration, parameterObject);    rootSqlNode.apply(context);    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());    }    return boundSql;  }
这里的逻辑有一点没一步步分析清楚,总之最后将各个条件合并,封装出一个BoundSql,其里面已经包含了sql语句执行的所有的完整内容,等后面会继续对这部分完善,逐步分析到底是怎么一步步将sql语句拼接,封装的。

    拿到boundSql以后调到另一个query方法中

//看这个方法,其实什么也没做,只是些逻辑判断或者安全处理,关键的一步就是又转到了另一个函数queryFromDatabase@SuppressWarnings("unchecked")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();    }    deferredLoads.clear(); // issue #601    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {      clearLocalCache(); // issue #482    }  }  return list;}//这个方法也是些逻辑判断等简单处理,就不细纠了,咱们只是跟踪执行流程,下面看到doQuery方法, 这个就是Executor中真正做事的方法了private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    List<E> list;    localCache.putObject(key, EXECUTION_PLACEHOLDER);    try {      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);    } finally {      localCache.removeObject(key);    }    localCache.putObject(key, list);    if (ms.getStatementType() == StatementType.CALLABLE) {      localOutputParameterCache.putObject(key, parameter);    }    return list;  }
上面一些流程没有什么实质性的内容,下面主要分析doQuery方法,这个方法是执行器中的真正方法了, 在baseExecutor中是个抽象方法,根据自己配置的Executor,执行不同的查询方法,大多数时候只需要使用默认执行器即可,即SimpleExecutor

/** *  * @param ms 被执行方法对应的sql节点全部信息 * @param parameter sql参数 * @param rowBounds null * @param resultHandler null * @param boundSql  sql语句 * @return * @throws SQLException */public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    Statement stmt = null;    try {    //取到资源类      Configuration configuration = ms.getConfiguration();      //这句创建出执行sql的关键类,整个流程会创建出ParameterHandler,ResultHandler,下面看详细流程      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);      stmt = prepareStatement(handler, ms.getStatementLog());      return handler.<E>query(stmt, resultHandler);    } finally {      closeStatement(stmt);    }  }//各个参数于上面一样public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    //RoutingStatementHandler不是我们真实服务的对象,它是用适配器模式找到对应的StatementHandler来执行,RoutingStatementHandler继承自StatementHandler,//有一个StatementHandler属性,一种特殊的适配器模式,具体选择哪种还是靠简单工厂来选择StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);    //Mybatis插件,前面博客专门讲解过statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);    return statementHandler;  }public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    //简单工厂模式选择合适的StatementHandler,与三种执行器相对应,这里StatementHandler对应这jdbc的Statement,PrepareStatement.    switch (ms.getStatementType()) {      case STATEMENT:        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);        break;      case PREPARED:        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);        break;      case CALLABLE:        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);        break;      default:        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());    }  }//对应JDBC的prepareStatementpublic PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);  }//构造函数protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {    this.configuration = mappedStatement.getConfiguration();    this.executor = executor;    this.mappedStatement = mappedStatement;    this.rowBounds = rowBounds;        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();    this.objectFactory = configuration.getObjectFactory();    if (boundSql == null) { // issue #435, get the key before calculating the statement      generateKeys(parameterObject);      boundSql = mappedStatement.getBoundSql(parameterObject);    }    this.boundSql = boundSql;     //其他两大对象,参数和结果集处理器,创建的过程就不看了, 比较简单,Mybatis都有ParameterHandler和ResultSetHandler的default实现    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);  }
newStatementHadler的过程将sql语句执行的前置条件都准备好了,到这里四大对象已经都初始化完成,下面就是利用四大对象来完成执行的时候了。下面看doQuery的后两句内容。

stmt = prepareStatement(handler, ms.getStatementLog());
其实执行sql语句,最终还是用jdk的jdbc来处理,前面只是为了便利对他的各种封装,下面又即将要看到他的庐山真面目了,其实所说的基础知识最重要,也就重要在这种地方,最终的本质肯定是万变不离其宗,扎实的基础会对理解框架的核心部分事半功倍。

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {    //这个就是jdbc的statement,这里就看到了熟悉的味道Statement stmt;//connection,从我们配置文件初始化中初始化的dataSource取得连接,具体和jdbc的处理一样    Connection connection = getConnection(statementLog);    //用connection创建statement    stmt = handler.prepare(connection);    handler.parameterize(stmt);    return stmt;  }//BaseStatementHandlerpublic Statement prepare(Connection connection) throws SQLException {    ErrorContext.instance().sql(boundSql.getSql());    Statement statement = null;    try {    //创建statement,Statement和PrepareStatement的创建略有不同,后者在创建的时候    //需要将sql语句模板传进去,也就是两者使用sql的时机不同,这个方法是个抽象方法,根据不同的StatementHandler    //选择不同的处理方式,下面具体看      statement = instantiateStatement(connection);      setStatementTimeout(statement);      setFetchSize(statement);      return statement;    } catch (SQLException e) {      closeStatement(statement);      throw e;    } catch (Exception e) {      closeStatement(statement);      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);    }  }
创建Statement,下面看下instantiateStatement方法

//SimpleStatementHandler中的是实现,这个没什么逻辑直接创建Statementprotected Statement instantiateStatement(Connection connection) throws SQLException {    if (mappedStatement.getResultSetType() != null) {      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);    } else {      return connection.createStatement();    }  }//PreparedStatementHandler中的实现,这个创建PrepareStatement提前需要sqlprotected Statement instantiateStatement(Connection connection) throws SQLException {    //从boundSql中取得sqlString sql = boundSql.getSql();    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {      String[] keyColumnNames = mappedStatement.getKeyColumns();      if (keyColumnNames == null) {        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);      } else {        return connection.prepareStatement(sql, keyColumnNames);      }    } else if (mappedStatement.getResultSetType() != null) {      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);    } else {      return connection.prepareStatement(sql);    }  }
逻辑也比较简单,只要对JDBC的逻辑过程比较熟悉,这里都是一样的,经过这个方法以后就从Connection中取到了Statement或者PreparedStatement

接着就是像jdbc一样设置sql模板的参数值,当然如果是Statement的话就不需要设置了,看下Mybatis是怎么处理的。

handler.parameterize(stmt);

这里就是为sql设置参数,下面看下不同的参数Handler是怎么处理的    

//SimpleStatementHandler中不需要设置,空函数public void parameterize(Statement statement) throws SQLException {    // N/A  }//PreparedStatementHandlerpublic void parameterize(Statement statement) throws SQLException {    parameterHandler.setParameters((PreparedStatement) statement);  }//newParameterHandler的时候初始化了DefaultParameterHnadler,这里用初始化时候的参数组装sql中的参数之public void setParameters(PreparedStatement ps) throws SQLException {    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());    //取得sql语句和类对应的数据类型    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();          typeHandler.setParameter(ps, i + 1, value, jdbcType);        }      }    }  }
下面就剩下最后一步,执行sql语句了,不论是prepareStatement还是Statement都统一处理完了sql语句,剩下执行了

handler.<E>query(stmt, resultHandler);
大家发现没有,之前的所有逻辑是在Executor中处理,后来所有的这些组装过程都是在StatementHandler中处理的,有种感觉就是Executor包含者StatementHandler,将逻辑转到StatementHandler以后,感觉StatementHandler中包含着ParameterHandler和ResultSetHandler.

public <E> List<E> query(Statement statement, ResultHandler resultHandler)      throws SQLException {    String sql = boundSql.getSql();    statement.execute(sql);    return resultSetHandler.<E>handleResultSets(statement);  }public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    ps.execute();    return resultSetHandler.<E> handleResultSets(ps);  }
比较简单,就不分析了,直接调用jdbc的函数,执行sql语句,对sql语句的执行结果Mybatis又进行了封装,也就是ResultSetHandler的工作开始了
public List<Object> handleResultSets(Statement stmt) throws SQLException {    final List<Object> multipleResults = new ArrayList<Object>();    int resultSetCount = 0;    ResultSetWrapper rsw = getFirstResultSet(stmt);    List<ResultMap> resultMaps = mappedStatement.getResultMaps();    int resultMapCount = resultMaps.size();    validateResultMapsCount(rsw, resultMapCount);    while (rsw != null && resultMapCount > resultSetCount) {      ResultMap resultMap = resultMaps.get(resultSetCount);      handleResultSet(rsw, resultMap, multipleResults, null);      rsw = getNextResultSet(stmt);      cleanUpAfterHandlingResultSet();      resultSetCount++;    }    String[] resultSets = mappedStatement.getResulSets();    if (resultSets != null) {      while (rsw != null && resultSetCount < resultSets.length) {        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);        if (parentMapping != null) {          String nestedResultMapId = parentMapping.getNestedResultMapId();          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);          handleResultSet(rsw, resultMap, null, parentMapping);        }        rsw = getNextResultSet(stmt);        cleanUpAfterHandlingResultSet();        resultSetCount++;      }    }    return collapseSingleResultList(multipleResults);  }
       最终封装为一个list返回了,到这里整个方法的执行就结束了,其实看起来整个逻辑也没这么复杂,最后的关键部分都是对jcbc的封装,确实基础很关键。还有比较关键的一个部分就是sql语句的拼接部分,也就是BoundSql的产生过程,后面还值得分析理解。

      做一个对上面过程简单的总结梳理:SqlSession是通过Executor创建StatementHandler来运行的,而StatementHandler经过下面三步来执行sql

 (1)prepared预编译SQL;

 (2)parameterize设置参数;

 (3)query执行sql

     parameterize是调用parameterHandler的方法去设置的,而参数是根据类型处理器typeHandler去处理的。query方法是通过resultHandler进行结果封装的,如果是update直接返回证书,否则它通过typeHandler处理结果类型,然后用ObjectFactory提供的规则组装对象,返回给调用者。

最后QQ交流群(341252823),新建的,目前没有人,期望你的加入,可以一起讨论一些问题

下一篇文章准备写一下Mybatis和Spring的连接部分。

原创粉丝点击