日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

MyBatis原理

mybatis源碼原理主要是分為三個部分 初始化、創建會話、語句執行

一、初始化

1.讀取配置文件mybatis-config.xml。
2.解析mApper.xml文件。
3.最后將mapper.xml中的sql語句全部保存到了Map mappedStatements中(mappedStatements的key:namespace+sqlId,value:MappedStatement
mappedStatements還會保存namespace和代理工廠的映射關系,存入到knownMappers.put(type, newMapperProxyFactory<>(type));)。

源碼實現

public void prepare() throws IOException {
    //加載mybatis-config.xml配置文件
    String resource = "mybatis-config.xml";
    //將mybatis配置文件轉換成流的形式
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //new一個SqlSessionFactoryBuilder,然后將流傳進去,是為了得到一個 sqlSessionFactory 
    //這里使用到了建造者模式
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}

我們通過上面的build開始閱讀我們mybatis的源碼

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 開始解析 mybatis-config.xml,并且創建了 Configuration 對象 
    //Configuration 對象是對應mybatis-config.xml中的一些配置的
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // 解析XML,最終返回一個 DefaultSqlSessionFactory >>
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

解析mybatis-config.xml 從 configuration 開始解析,也就是我們xml中的最上層配置開始解析

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

是一層一層對mybatis-config.xml進行解析,并將所有的標簽都放到了Configuration中,我們可以看到一下代碼中的參數

private void parseConfiguration(XNode root) {
  try {
    // 對于全局配置文件各種標簽的解析,將標簽放到Configuration中
    propertiesElement(root.evalNode("properties"));
    // 解析 settings 標簽
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(settings);
    loadCustomLogImpl(settings);
    // 類型別名
    typeAliasesElement(root.evalNode("typeAliases"));
    // 插件
    pluginElement(root.evalNode("plugins"));
    // 用于創建對象
    objectFactoryElement(root.evalNode("objectFactory"));
    // 用于對對象進行加工
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    // 反射工具箱
    reflectorFactoryElement(root.evalNode("reflectorFactory"));
    // settings 子標簽賦值,默認值就是在這里提供的 >>
    settingsElement(settings);
    
    // 創建數據源
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 解析引用的Mapper映射器 這個是最重要的操作 
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

我們來看一下最重要的方法都做了哪些操作

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      // 不同的定義方式的掃描,最終都是調用 addMapper()方法(添加到 MapperRegistry)。這個方法和 getMapper() 對應
      // <package name="com.xinyu.demo"/>  包含這樣的標簽才會走if操作
      //else 走的是 <mapper resource="BlogMapper.xml"/>這樣的標簽
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          // resource  相對路徑
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          // 解析 Mapper.xml,詳情看下方代碼塊
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          // url 絕對路徑
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          // class   單個接口
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

把sql都封裝成一個個的statement,然后我們再去把namespace+id綁定一個代理的工廠,后面用來調用,最終也是調用的MapperRegistry.addMapper

public void parse() {
  // 總體上做了兩件事情,對于語句的注冊和接口的注冊
  if (!configuration.isResourceLoaded(resource)) {
    // 1、具體增刪改查標簽的解析。
    // 一個標簽一個MappedStatement。 
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    // 2、把namespace(接口類型)和工廠類綁定起來,放到一個map。
    // 一個namespace 一個 MapperProxyFactory
    bindMapperForNamespace();
  }

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

parse方法中的第一步configurationElement()操作參考以下代碼

private void configurationElement(XNode context) {
  try {
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    // 添加緩存對象
    cacheRefElement(context.evalNode("cache-ref"));
    // 解析 cache 屬性,添加緩存對象
    cacheElement(context.evalNode("cache"));
    // 創建 ParameterMapping 對象
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    // 創建 List<ResultMapping>
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    // 解析可以復用的SQL
    sqlElement(context.evalNodes("/mapper/sql"));
    // 解析增刪改查標簽,得到 MappedStatement 也是重要的
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

private void buildStatementFromContext(List<XNode> list) {
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  // 解析 Statement list代表的是XXXMapper.xml中的所有的sql語句
  buildStatementFromContext(list, null);
}


private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) {
    // 用來解析增刪改查標簽的 XMLStatementBuilder
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      // 解析 Statement,添加 MappedStatement對象 詳情看下方代碼
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

public void parseStatementNode() {
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

 
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);

  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }

  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  String resultType = context.getStringAttribute("resultType");
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultSetType = context.getStringAttribute("resultSetType");
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  if (resultSetTypeEnum == null) {
    resultSetTypeEnum = configuration.getDefaultResultSetType();
  }
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  String resultSets = context.getStringAttribute("resultSets");

  // 關鍵的一步: MappedStatement 的創建  封裝成MappedStatement對象
  //每一個sql變成一個MappedStatement
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}


public MappedStatement addMappedStatement(
    String id,
    SqlSource sqlSource,
    StatementType statementType,
    SqlCommandType sqlCommandType,
    Integer fetchSize,
    Integer timeout,
    String parameterMap,
    Class<?> parameterType,
    String resultMap,
    Class<?> resultType,
    ResultSetType resultSetType,
    boolean flushCache,
    boolean useCache,
    boolean resultOrdered,
    KeyGenerator keyGenerator,
    String keyProperty,
    String keyColumn,
    String databaseId,
    LanguageDriver lang,
    String resultSets) {

  if (unresolvedCacheRef) {
    throw new IncompleteElementException("Cache-ref not yet resolved");
  }
  //設置id  是namespace+sqlId判斷最終的sql標簽是否重復
  id = applyCurrentNamespace(id, false);
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

  MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
      .resource(resource)
      .fetchSize(fetchSize)
      .timeout(timeout)
      .statementType(statementType)
      .keyGenerator(keyGenerator)
      .keyProperty(keyProperty)
      .keyColumn(keyColumn)
      .databaseId(databaseId)
      .lang(lang)
      .resultOrdered(resultOrdered)
      .resultSets(resultSets)
      .resultMaps(getStatementResultMaps(resultMap, resultType, id))
      .resultSetType(resultSetType)
      .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
      .useCache(valueOrDefault(useCache, isSelect))
      .cache(currentCache);

  ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
  if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
  }

  MappedStatement statement = statementBuilder.build();
  // 最關鍵的一步,在 Configuration 添加了 MappedStatement 
  configuration.addMappedStatement(statement);
  return statement;
}

public void addMappedStatement(MappedStatement ms) {
  //Id 是namespace+sqlId 也是這個map中的key 
  //key是 namespace+sqlId value是 MappedStatement 
  mappedStatements.put(ms.getId(), ms);
}

parse方法中的第二步bindMapperForNamespace()操作參考以下代碼

private void bindMapperForNamespace() {
  //拿到namespace
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      //拿到相應的class對象
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      //判斷configuration 是否有 如果沒有 就添加到configuration中
      if (!configuration.hasMapper(boundType)) {
        configuration.addLoadedResource("namespace:" + namespace);
        // 添加到 MapperRegistry,本質是一個 map,里面也有 Configuration 
        configuration.addMapper(boundType);
      }
    }
  }
}


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 {
      // !Map<Class<?>, MapperProxyFactory<?>> 存放的是接口類型,和對應的工廠類的關系
      //key是namespace那個接口的對象 value 對應的代理工廠類
      knownMappers.put(type, new MapperProxyFactory<>(type));

      // 注冊了接口之后,根據接口,開始解析所有方法上的注解,例如 @Select >>
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }

二、創建會話

拿到執行器(Excutor)如果沒有傳入執行器類型,默認就是SIMPLE,否則就是傳入的類型。如果開啟的緩存,將會把SimpleExecutor執行器封裝成CachingExecutor創建會話最終就是為了得到SqlSession。

源碼實現

@Test
public void testSelect() throws IOException {
    SqlSession session = sqlSessionFactory.openSession();
    try {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Blog blog = mapper.selectBlogById(1);
        System.out.println(blog);
    } finally {
        session.close();
    }
}
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);
    //將得到的執行器放入到默認的sqlSession中  最后得到一個sqlSession
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
    closeTransaction(tx);
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

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 {
    // 默認 SimpleExecutor
    executor = new SimpleExecutor(this, transaction);
  }
  // 二級緩存開關,settings 中的 cacheEnabled 默認是 true
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  // 植入插件的邏輯,至此,四大對象已經全部攔截完畢
  //緩存的執行器用到的是裝飾器模式
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

簡單舉例上面提到的執行器

//傳入執行器的方式 
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);

public enum ExecutorType {
    //執行mapper.save()方法時,就相當于jdbc的stmt.execute(sql)
    SIMPLE,
    //會重用sql,通過stmt傳入多項參數值
    REUSE,
    //相當于stmt.addBatch(sql)這樣的,批量
    BATCH;

    private ExecutorType() {
    }
}

三、語句執行

通過接口的類型,在knownMappers中去獲取代理工廠,然后創建代理類,代理類的handler是MapperProxy調用方法,走到MapperProxy中invoke方法,去掉object中的方法,直接調用。如果你在settings全局配置文件中配置了cacheEnabled為true,并且xml中配置了 標簽,并且在sql語句上配置了useCache,就會調用CachingExecutor對象的query方法CachingExecutor類中的query方法會先看二級緩存中有沒有,如果有直接返回,如果沒有,會去走到SimpleExecutor的query方法最后就和調用JDBC的流程一樣,打開連接,查詢數據,返回數據。

源碼實現

@Test
public void testSelect() throws IOException {
    SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); // ExecutorType.BATCH
    try {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        //走到代理類的invoke對象
        Blog blog = mapper.selectBlogById(1);
        System.out.println(blog);
    } finally {
        session.close();
    }
}

從knownMappers中拿到代理對象

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

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

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
  // 1:類加載器:2:被代理類實現的接口、3:實現了 InvocationHandler 的觸發管理類
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

執行該方法會走到代理類的invoke對象
Blog blog = mapper.selectBlogById(1);

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    // toString hashCode equals getClass等方法,無需走到執行SQL的流程
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else {
      // 提升獲取 mapperMethod 的效率,先到 MapperMethodInvoker(內部接口) 的 invoke
      // 普通方法會走到 PlainMethodInvoker(內部類) 的 invoke
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

interface MapperMethodInvoker {
  Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
}


private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
  try {
    // JAVA8 中 Map 的方法,根據 key 獲取值,如果值是 null,則把后面Object 的值賦給 key
    // 如果獲取不到,就創建
    // 獲取的是 MapperMethodInvoker(接口) 對象,只有一個invoke方法
    return methodCache.computeIfAbsent(method, m -> {
      if (m.isDefault()) {
        // 接口的默認方法(Java8),只要實現接口都會繼承接口的默認方法,例如 List.sort()
        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 {
        // 創建了一個 MapperMethod
        return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
    });
  } catch (RuntimeException re) {
    Throwable cause = re.getCause();
    throw cause == null ? re : cause;
  }
}

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

@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
  // SQL執行的真正起點
  return mapperMethod.execute(sqlSession, args);
}

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);
        // 普通 select 語句的執行入口 >>
        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;
}


//舉個查詢多條的例子
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
  List<E> result;
  Object param = method.convertArgsToSqlCommandParam(args);
  if (method.hasRowBounds()) {
    RowBounds rowBounds = method.extractRowBounds(args);
    result = sqlSession.selectList(command.getName(), param, rowBounds);
  } else {
    result = sqlSession.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;
}



@Override
public <E> List<E> selectList(String statement, Object parameter) {
  // 為了提供多種重載(簡化方法使用),和默認值
  // 讓參數少的調用參數多的方法,只實現一次
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
      //從configuration中獲取MappedStatement 這是在初始化的時候存進去的
      //這里是通過namespace+sqlId來進行獲取的
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 如果 cacheEnabled = true(默認),Executor會被 CachingExecutor裝飾
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

//我們在這里看的是走緩存的
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 獲取SQL
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  // 創建CacheKey:什么樣的SQL是同一條SQL? 
  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();
  // cache 對象是在哪里創建的?  XMLMapperBuilder類 xmlconfigurationElement()
  // 由 <cache> 標簽決定
  if (cache != null) {
    // flushCache="true" 清空一級二級緩存 >>
    flushCacheIfRequired(ms);
    //是否使用緩存
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);
      // 獲取二級緩存
      // 緩存通過 TransactionalCacheManager、TransactionalCache 管理
      @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;
    }
  }
  // 走到 SimpleExecutor | ReuseExecutor | BatchExecutor
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

//沒有走緩存的 ,是直接查詢數據庫的操作
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  // 異常體系之 ErrorContext
  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()) {
    // flushCache="true"時,即使是查詢,也清空一級緩存
    clearLocalCache();
  }
  List<E> list;
  try {
    // 防止遞歸查詢重復處理緩存
    queryStack++;
    // 查詢一級緩存
    // ResultHandler 和 ResultSetHandler的區別
    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 {
    // 三種 Executor 的區別,看doUpdate
    // 默認Simple
    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;
}

@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
      //拿到MappedStatement 中的所有信息
    Configuration configuration = ms.getConfiguration();
    // 注意,已經來到SQL處理的關鍵對象 StatementHandler 
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    // 獲取一個 Statement對象 詳情看下方代碼
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 執行查詢
    return handler.query(stmt, resultHandler);
  } finally {
    // 用完就關閉
    closeStatement(stmt);
  }
}

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

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  // StatementType 是怎么來的? 增刪改查標簽中的 statementType="PREPARED",默認值 PREPARED
  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      // 創建 StatementHandler 的時候做了什么? >>
      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());
  }
}


private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  // 獲取 Statement 對象
  stmt = handler.prepare(connection, transaction.getTimeout());
  // 為 Statement 設置參數
  handler.parameterize(stmt);
  return stmt;
}

//執行查詢 - 執行sql   handler.query(stmt, resultHandler);
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  // 到了JDBC的流程
  ps.execute();
  // 處理結果集 并返回
  return resultSetHandler.handleResultSets(ps);
}

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

  final List<Object> multipleResults = new ArrayList<>();

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

到此我們就已經閱讀完mybatis從初始化到創建會話最后到執行查詢的原理了。。。

分享到:
標簽:Mybatis
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定