Mybatisソースコードの初期解析(2)

前回の記事に引き続き、私が言ったと思う3つの部分

  • 情報元
  • SQL文
  • SQL文を実行する

前の記事ではデータ ソースへの接続を確認しましたが、次に SQL ステートメントがどのように取得されるかを調べる必要があります。
mybatis に詳しい人なら誰でも、SQL をマップするには 2 つの方法があることを知っています。1 つ目はアノテーションを使用する方法、2 つ目は XML ファイルを使用する方法です。
実際、xml はより頻繁に使用されており、mybatis は xml ファイルの使用を公式に推奨しています。xml
でのマッピングは、名前空間のバインドと完全修飾名によるバインドによって行われます。インターフェイスとインターフェイスの実装と同様に、アノテーションはアノテーション上のインターフェイスの実装です。どちらも似ています。
ただし、SQL ステートメントの取得方法について私が話しているのは、フレームワークそのものだけです。
sqlseeionfactory を取得し、opensession を通じて sqlsession を取得しました。

  @Override
  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);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
    
    
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
    
    
      ErrorContext.instance().reset();
    }
  }

new DefaultSqlSession(configuration, executor, autoCommit); が表示されます。以前に構成した構成、executor、autoCommit を構造体に配置し、使用する sqlsession executor、autoCommit executor である DefaultSqlSession を直接取得します。今のところ autoCommit については見ていません。おそらく、自動送信を設定するだけのものでしょう。
その後、歩き続けると、公式ドキュメントに記載されているチュートリアルに従って sqlSeesion を取得しました。sqlsession を通じて selectone またはその他のメソッドを実行できます。これは単なる方法です。最初に getmapper (クラス) を実行し、次にこのマッパーを通じて独自に定義したインターフェイスを呼び出すことをお勧めします。このインターフェイスのメソッドがバインドされていることはわかっています。マッパーで書かれたSQL文。
ということで、このgetmapperメソッドがsqlを取得するためのキーポイントになるのですが、そのメソッドの中に呼び出しシーケンスがあることが分かりました。

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

次に、非常に重要な点に到達し、Java の動的プロキシであるプロキシを見つけました。

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

明らかに、mapperProxyFactory.newInstance(sqlSession); は、mapperProxy のプロキシ クラス インスタンスを取得し、以下に進みます。

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    
    
      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);
          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;
  }

selectone が引き続き select area で呼び出されていることがわかります
。したがって、selectone とカスタム selecy メソッドは本質的に同じです。最下層は同じで、前の 2 つのメソッドの呼び出しに戻ります。
「選択」をクリックします

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    
    
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
    
    
      return list.get(0);
    } else if (list.size() > 1) {
    
    
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
    
    
      return null;
    }
  }

selectList の詳細を参照してください。

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

基本的に、baseexecutor の executor.query() クエリ メソッドが呼び出されます。

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    
    
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

キャッシュキーとは mybatis に詳しい人ならキャッシュがあることを知っており、この名前を見るとキャッシュを思い浮かべるでしょう。
話はさておき、以前にも getBoundSql というのがありましたが、このネーミングは勝利の夜明けを感じます。

 public BoundSql getBoundSql(Object parameterObject) {
    
    
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
    
    
      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();
        }
      }
    }

この時点で SQL ステートメントが取得されました。
おそらく2人は別の検証を受けたと思います。
全体的なプロセスはどのようなものですか? それを少し整理しました
sqlsessionfactory.opensession (configuration、executor、autocommit を通じてdefaultsqlsession を構築します)—>sqlsession.getmapper (configuration.getmapper–>mapperregister.getmapper (f メソッドの実装は、mapperproxyfactory–》newInstance で、mapperProxy を取得します) ) ---- >invoke で実行メソッドが呼び出されます—>selectone が表示されます—>selectList (executor.query()—>getBoundSql が呼び出されます) この時点で、SQL ステートメントは完全に取得されています (select のみが使用されます)使用例ですが、他のものも同様だと思います)

残りは SQL ステートメントを実行する方法です。
このプロセスで重要なのは getboundsql です。

Mybatisソースコードの初期解析(3)

おすすめ

転載: blog.csdn.net/Yoke______/article/details/123956923