刘耀文

刘耀文

java开发者
github

Mybatis[plus] ソースコード読書ノート

呼び出しフローの概要:#

mybatis の前身は ibatis で、ネイティブの ibatis は curd 操作を固定的に実行し、SQLSession インターフェース内のメソッドを使用して追加、削除、検索、更新を行います。まず、Ibatis 部分のコードを読み、以降の mybatis [plus] はこの基盤の上に MapperProxy やプリセット SQL 文の動的 SQL 条件などのラッピングを拡張しています。ibatis の Session のコアインターフェースを見てみましょう:

public interface SqlSession extends Closeable {
    
  <E> List<E> selectList(String statement);

  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);

  <T> Cursor<T> selectCursor(String statement);

  int insert(String statement);

  int update(String statement);

  int delete(String statement);

}

このインターフェース内で最終的に必要な 4 つのメソッドは(selectList、selectMap、selectCursor)、insert、delete、update です。

しかし、実際に SQL 文を実行するのは Executor 内のメソッドです。

public interface Executor {

  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;

  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
}

ここでは query と update メソッドに分かれ、最終的に実際のサブクラスの doQuery、doUpdate が本当の SQL 文を実行し、さらに StatementHandler オブジェクトに処理を委ねます。

public interface StatementHandler {

  Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;

  void parameterize(Statement statement) throws SQLException;

  void batch(Statement statement) throws SQLException;

  int update(Statement statement) throws SQLException;

  <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;

  <E> Cursor<E> queryCursor(Statement statement) throws SQLException;

  BoundSql getBoundSql();

  ParameterHandler getParameterHandler();

}

最終的に update または query メソッドに到達することがわかります。

image-20240611225027427

したがって、呼び出しロジックは SqlSession->Executor->StatementHandler であり、各 SqlSession には 1 つの Executor があり、各 Executor には 1 つの StatementHandler(configuration から取得され、プラグインでラッピングされたもの)が存在します。

SqlSession の作成プロセス:

image-20240611231131172

そのコアは Configuration であり、全体の ibatis はこの Configuration クラスを中心に構築されており、Executor、ParameterHandler、ResultSetHandler の作成や MapperStatements の保存を担当しています。

一、二次キャッシュ#

一次キャッシュ

実行者内に存在し、毎回作成されるたびに持ち運ばれるため、各セッションには一次キャッシュが存在します:BaseExecutor.java

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

クエリの前に、キャッシュ内を検索し、key 値は 4 つの側面に関連しています。

CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

SQL 文、パラメータ、ページング、およびバインドされたパラメータの SQL が一致する場合のみマッチします。

二次キャッシュ

ms 内の cache 属性に存在し、configuration 内の cacheEnable が true の場合にのみ cacheExecutor が作成されます。

// Configuration.java
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : 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;
  }
// デフォルトはtrue

同時に ms 内の useCache が true である場合、実際に ms 内の cache を操作します。

// CachingExecutor.java
  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);
  }
// デフォルトもtrue

キャッシュが null でない場合にのみ実際にキャッシュを操作することがわかります。mapper.xml を解析する際に cache タイプが設定されているかどうかが判断されます。

// XMLMapperBuilder.java  
private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      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);
    }
  }
// タイプは多くの種類があります

image

この記事は Mix Space によって xLog に同期更新されました
元のリンクは https://me.liuyaowen.club/posts/default/Mybatisplus


読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。