mybatis源碼分析(三):mybaits是如何執(zhí)行一條sql語(yǔ)句的

mybatis代理對(duì)象的創(chuàng)建過(guò)程

在上一遍 mybatis源碼分析(二):mybatis在執(zhí)行SQL語(yǔ)句之前都做了什么 中我們通過(guò)源碼分析看到mybatis是如何構(gòu)建SqlSessionFactory和SqlSession的瘾敢。其中我們看到SqlSession在構(gòu)建的過(guò)程中構(gòu)建了一個(gè)Executor,在Executor里包含了大量的數(shù)據(jù)庫(kù)操作,于是猜測(cè)Executor就是mybatis執(zhí)行SQL語(yǔ)句的核心組件。

這篇文章我們將繼續(xù)深入mybatis源碼來(lái)研究一下mybatis是如何執(zhí)行一條sql語(yǔ)句的戴已,同時(shí)mybatis又是如何處理輸入?yún)?shù)和輸出結(jié)果的固额。

首先我們寫(xiě)一段測(cè)試代碼:

執(zhí)行sql語(yǔ)句的mapper接口:


public interface DistrictMapper {

    District getById(Integer id);
}

mapper對(duì)應(yīng)的sql xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="me.binf.mybatistudy.dao.DistrictMapper">

    <select id="getById" resultType="me.binf.mybatistudy.entity.District">
        select * from district where id = #{id}
    </select>

</mapper>

測(cè)試用例:

    @Test
    public void testMyBatis() throws IOException {
        //1头谜、得到 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        //2狐蜕、得到 sqlSession ,代表和數(shù)據(jù)庫(kù)一次回話(huà)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //3宠纯、得到真正操作數(shù)據(jù)庫(kù)的Mapper
        DistrictMapper mapper = sqlSession.getMapper(DistrictMapper.class);
        //4.執(zhí)行方法方法并得到結(jié)果
        District district = mapper.getById(1);
        System.out.println(district);
        //5.關(guān)閉sqlSession
        sqlSession.close();
    }

上面三段代碼我們上一篇文章已經(jīng)分析過(guò)了,今天我們主要是Debug這兩段代碼:

    //3层释、得到真正操作數(shù)據(jù)庫(kù)的Mapper
    DistrictMapper mapper = sqlSession.getMapper(DistrictMapper.class);
    //4.執(zhí)行方法方法并得到結(jié)果
    District district = mapper.getById(1);

要知道DistrictMapper是一個(gè)接口婆瓜,接口是沒(méi)辦法執(zhí)行的。

DistrictMapper mapper = sqlSession.getMapper(DistrictMapper.class);

那么這段代碼一定是mybatis為我們生成了一個(gè)代理對(duì)象贡羔,我們進(jìn)去看一下廉白。

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

public class Configuration {

  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

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

public class MapperRegistry {

  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

 @SuppressWarnings("unchecked")
 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);
    }
  }
......
}

我把mybatis生成代理對(duì)象核心的三個(gè)方法摘錄下來(lái)了,可以看到mybatis并沒(méi)有直接生成代理對(duì)象乖寒,而是使用MapperProxyFactory來(lái)生成DistrictMapper的代理對(duì)象猴蹂。而MapperProxyFactory是mybatis在構(gòu)建SqlSessionFactory的時(shí)候就已經(jīng)創(chuàng)建好,并且放入到MapperRegistry這個(gè)類(lèi)的knownMappers屬性Map中的楣嘁。

那么我們可以再來(lái)回溯一下MapperProxyFactory的創(chuàng)建過(guò)程:

public class XMLMapperBuilder extends BaseBuilder {

  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      //用xml的namespace構(gòu)建mapper
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        // ignore, bound type is not required
      }
      if (boundType != null && !configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        configuration.addLoadedResource("namespace:" + namespace);
        configuration.addMapper(boundType);
      }
    }
  }

 public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }
......
}
public class MapperRegistry {

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
......
}

可以看到mybatis創(chuàng)建MapperProxyFactory過(guò)程是:

  1. 加載mybatis配置文件對(duì)應(yīng)的所有mapper.xml路徑
  2. 解析mapper.xml里所有的namespace標(biāo)簽磅轻,根據(jù)標(biāo)簽生成對(duì)應(yīng)接口的Class對(duì)象
  3. 把Class對(duì)象作為key,創(chuàng)建MapperProxyFactory對(duì)象作為value逐虚,然后放進(jìn)knownMappers里面

同時(shí)我們還可以看到mybatis在初始化階段不光解析了mapper.xml里namespace標(biāo)簽聋溜,還解析了ResultMaps、CacheRefs叭爱、Statements具體有什么左右我們后面再研究撮躁。可以看到mybatis這么設(shè)計(jì)的好處是可以在項(xiàng)目啟動(dòng)階段就可以幫你檢查出xml和接口對(duì)應(yīng)關(guān)系是否正確,同時(shí)提前把MapperProxyFactory構(gòu)建好緩存起來(lái)還可以加快后面程序運(yùn)行的效率买雾。

mybatis是如何為執(zhí)行SQL語(yǔ)句做準(zhǔn)備的

MapperProxyFactory對(duì)象已經(jīng)有了把曼,那么接下來(lái)就是mybatis如何執(zhí)行SQL語(yǔ)句了。

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

 ......
}

動(dòng)態(tài)代理newProxyInstance核心參數(shù)是InvocationHandler漓穿,而MapperProxy就是mybatis為InvocationHandler提供的實(shí)現(xiàn)類(lèi)祝迂。我們主要去看MapperProxy類(lèi)的invoke()方法,這個(gè)方法就是mybatis執(zhí)行sql語(yǔ)句的入口器净。

public class MapperProxy<T> implements InvocationHandler, Serializable {
 @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 {
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {
        if (m.isDefault()) {
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }

  private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {
      super();
      this.mapperMethod = mapperMethod;
    }

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

......
}

可以看到mybatis緩存很多,調(diào)用invoke方法的時(shí)候mybatis會(huì)把目標(biāo)方法對(duì)象給緩存起來(lái)当凡,每一個(gè)目標(biāo)方法對(duì)應(yīng)一個(gè)私有靜態(tài)類(lèi)PlainMethodInvoker再去執(zhí)行山害。

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

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

PlainMethodInvoker再去調(diào)用MapperMethodexecute方法去執(zhí)行SQL語(yǔ)句,mybatis的MapperMethod方法相當(dāng)于整個(gè)框架的分揀車(chē)間的作用沿量,分揀過(guò)程大概做了這幾件事:
1.MapperMethod實(shí)例化方法會(huì)把目標(biāo)方法的所有信息包裝成一個(gè)SqlCommand對(duì)象
2.根據(jù)目標(biāo)方法執(zhí)行的SQL類(lèi)型做判斷浪慌,通過(guò)不同類(lèi)型去執(zhí)行對(duì)應(yīng)的SQL邏輯
3.根據(jù)查詢(xún)方法的返回值類(lèi)型去調(diào)用不同查詢(xún)方法從而可以把查詢(xún)結(jié)果包裝成目標(biāo)方法定義的返回類(lèi)型
4.根據(jù)入?yún)⒌念?lèi)型和個(gè)數(shù)把入?yún)b成框架方便解析的包裝類(lèi)型

這里可以看到mybatis的一個(gè)核心組件:ParamNameResolver參數(shù)解析器

public class ParamNameResolver {
  public static final String GENERIC_NAME_PREFIX = "param";

  public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      Object value = args[names.firstKey()];
      return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }


public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    } else if (object != null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("array", object);
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    }
    return object;
  }
}

解析參數(shù)的邏輯并不復(fù)雜朴则,mybatis會(huì)判斷你定義的目標(biāo)方法參數(shù)的個(gè)數(shù)和類(lèi)型:

1.如果只有一個(gè)參數(shù)权纤,并且參數(shù)不是集合類(lèi)型或者是數(shù)組類(lèi)型就直接返回參數(shù)值

2.如果只有一個(gè)參數(shù),但是參數(shù)的類(lèi)型是集合類(lèi)型或者數(shù)組類(lèi)型,就會(huì)把參數(shù)放到map里面用collection(如果參數(shù)是List類(lèi)型還會(huì)再多放一個(gè)list名字的key和value)或者array作為key汹想,值作為value

3.如果有多個(gè)參數(shù)外邓,就會(huì)把參數(shù)名和參數(shù)值分別作為key和value放到map里面再返回,值得注意的是mybatis還會(huì)在map里再給你放一個(gè)別名的key古掏,也就是param+參數(shù)位置如過(guò)參數(shù)是兩個(gè)就是param1损话,param2分別對(duì)應(yīng)第一個(gè)和第二個(gè)參數(shù)

看到這里我們就可以得出一個(gè)結(jié)論,在mybatis的mapper.xml里面動(dòng)態(tài)取值槽唾,不光可以用#{參數(shù)名}這種方式還可以用#{param1}這種方式丧枪。

MapperMethod類(lèi)在分揀完目標(biāo)方法后就正式調(diào)用sqlSession來(lái)執(zhí)行SQL了,我們看一下sqlSession是如何執(zhí)行的庞萍。這里執(zhí)行的是selectOne()方法拧烦。從源碼中可以看到mybatis做查詢(xún)的時(shí)候除了selectCursor方法,其余的查詢(xún)最終都會(huì)用selectList方法去執(zhí)行:

public class DefaultSqlSession implements SqlSession {

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

mybatis執(zhí)行SQL語(yǔ)句的過(guò)程

可以看到executor正是我們上一篇分析出來(lái)的mybatis執(zhí)行SQL語(yǔ)句的核心組件钝计。也就是說(shuō)mybatis前面都在準(zhǔn)備食材恋博,現(xiàn)在才正式下鍋炒菜。

那么mybatis是如何炒菜的呢葵蒂?我們繼續(xù)看一下:

public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;

  int update(MappedStatement ms, Object parameter) throws SQLException;

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
 ......
}

Executor是一個(gè)接口永高,首先要搞清楚誰(shuí)是實(shí)現(xiàn)類(lèi)饲宛,我們上一篇分析的是如果沒(méi)有指定executorType,那么默認(rèn)的Executor就是SimpleExecutor

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

其實(shí)不是殴瘦,因?yàn)槟J(rèn)cacheEnabled是true辣吃,也就是說(shuō)executor是CachingExecutor,而CachingExecutor再把SimpleExecutor包裝起來(lái)别智。也就是說(shuō)mybatis在這個(gè)地方使用了一個(gè)簡(jiǎn)單的裝飾器模式,使SimpleExecutor增強(qiáng)了一個(gè)緩存功能敞恋。

我們?cè)賮?lái)看一下CachingExecutor是如何使用緩存的:

public class CachingExecutor implements Executor {

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

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

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
......
}

CachingExecutor在這里操作的cache是二級(jí)緩存丽啡,二級(jí)緩存策略我們放到后面再研究,這里我們知道二級(jí)緩存是存在MappedStatement這個(gè)組件里的硬猫,大概的邏輯就是查詢(xún)結(jié)果存在緩存里就直接返回补箍,不存在再去調(diào)用SimpleExecutor去執(zhí)行SQL語(yǔ)句。

我們繼續(xù)往下看SimpleExecutor的執(zhí)行邏輯:

public abstract class BaseExecutor implements Executor {

  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;

@Override
  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;
  }

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

SimpleExecutor繼承了BaseExecutorquery()方法啸蜜, BaseExecutor首先做的事情是從一級(jí)緩存localCache中去取值馏予,取不到的話(huà)再去調(diào)用子類(lèi)的doQuery方法去執(zhí)行SQL,拿到緩存結(jié)果后再放到localCache里面盔性。localCache緩存使用是有一定條件的,這個(gè)我們也放到后面再講呢岗。

繼續(xù)來(lái)看doQuery方法:

public class SimpleExecutor extends BaseExecutor {
  @Override
  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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

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;
  }
......
}
public class Configuration {

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

}

doQuery()方法會(huì)先拿到一個(gè)默認(rèn)RoutingStatementHandler冕香,再去連接數(shù)據(jù)庫(kù)蛹尝。拿到數(shù)據(jù)庫(kù)連接后就是StatementHandler正式出場(chǎng)了。

先來(lái)看一下RoutingStatementHandler的構(gòu)造方法悉尾。

public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

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

可以看到RoutingStatementHandler實(shí)際上還不是真正去調(diào)用JDBC執(zhí)行SQL語(yǔ)句的組件突那,mybatis在這里用了一個(gè)簡(jiǎn)單的策略模式,真正去做事的StatementHandler是通過(guò)ms.getStatementType()來(lái)判斷出來(lái)的构眯。

執(zhí)行SQL語(yǔ)句的StatementHandler有三個(gè)策略類(lèi):
1.SimpleStatementHandler 執(zhí)行簡(jiǎn)單SQL語(yǔ)句的策略類(lèi)
2.PreparedStatementHandler 執(zhí)行預(yù)編譯SQL語(yǔ)句的策略類(lèi)
3.CallableStatementHandler 執(zhí)行存儲(chǔ)過(guò)程SQL語(yǔ)句的策略類(lèi)

我們的demo使用的是預(yù)編譯的SQL語(yǔ)句愕难,我們來(lái)看一下PreparedStatementHandler

public class PreparedStatementHandler extends BaseStatementHandler {
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }
......
}

到這里mybatis終于沒(méi)有什么騷操作了,直接使用JDBC的PreparedStatement執(zhí)行SQL語(yǔ)句惫霸,然后把數(shù)據(jù)庫(kù)的返回值交給ResultHandler來(lái)處理了猫缭。

ResultHandler顧名思義就是來(lái)處理返回值的,由于這篇篇幅太長(zhǎng)了壹店,mybatis是如何來(lái)處理返回值的我們放到下一篇來(lái)講猜丹。總之mybatis終于執(zhí)行SQL了硅卢,我們接下來(lái)總結(jié)一下整個(gè)過(guò)程射窒。

  1. 從Configuration中獲得MapperProxyFactory
  2. MapperProxyFactory構(gòu)建定義接口的動(dòng)態(tài)代理對(duì)象
  3. 再就是動(dòng)態(tài)代理對(duì)象執(zhí)行查詢(xún)方法步驟:
mybatis執(zhí)行過(guò)程.jpg

本文涉及的源碼放在github上:
https://github.com/burgleaf/mybatis-study

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市将塑,隨后出現(xiàn)的幾起案子脉顿,更是在濱河造成了極大的恐慌,老刑警劉巖点寥,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艾疟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡开财,警方通過(guò)查閱死者的電腦和手機(jī)汉柒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)责鳍,“玉大人碾褂,你說(shuō)我怎么就攤上這事±穑” “怎么了正塌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恤溶。 經(jīng)常有香客問(wèn)我乓诽,道長(zhǎng),這世上最難降的妖魔是什么咒程? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任鸠天,我火速辦了婚禮,結(jié)果婚禮上帐姻,老公的妹妹穿的比我還像新娘稠集。我一直安慰自己奶段,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布剥纷。 她就那樣靜靜地躺著痹籍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晦鞋。 梳的紋絲不亂的頭發(fā)上蹲缠,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音悠垛,去河邊找鬼线定。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鼎文,可吹牛的內(nèi)容都是我干的渔肩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拇惋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼周偎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起撑帖,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蓉坎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后胡嘿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蛉艾,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年衷敌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勿侯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缴罗,死狀恐怖助琐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情面氓,我是刑警寧澤兵钮,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站舌界,受9級(jí)特大地震影響掘譬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜呻拌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一葱轩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦靴拱、人聲如沸复亏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至抬闷,卻和暖如春妇蛀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笤成。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工评架, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炕泳。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓纵诞,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親培遵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浙芙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容