blog

Mybatisのソースコード(3) --- sqlSession完全なSQLクエリ

作成するプロセスを分析しましたが、今へのSQLクエリを完了するプロセスを見てください。 boolean :JDBCトランザクションの自動コミットを開くかどうか、デフォルトはfalseです。 ......

Mar 30, 2020 · 14 min. read
シェア

openSession()

SqlSession オブジェクトは、SqlSessionFactory の openSession メソッドを使用して作成します。

 SqlSession sqlSession = sessionFactory.openSession();

openSession のソース コードを見てください。

public interface SqlSessionFactory {
 SqlSession openSession();
 SqlSession openSession(boolean autoCommit);
 SqlSession openSession(Connection connection);
 SqlSession openSession(TransactionIsolationLevel level);
 SqlSession openSession(ExecutorType execType);
 SqlSession openSession(ExecutorType execType, boolean autoCommit);
 SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
 SqlSession openSession(ExecutorType execType, Connection connection);
 Configuration getConfiguration();
}

パラメータは次のとおりです。

  • boolean autoCommit: JDBC トランザクションの自動コミットを有効にするかどうか。
  • Connection: 接続を提供します。
  • TransactionIsolationLevel: トランザクションの分離レベルを定義します。
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();
 }
 }

ここで主なものは、SqlSessionの実装クラスの戻り値のオブジェクトを次のように作成するための役割--- DefaultSqlSession

  • TransactionFactoryによるトランザクションの作成
  • TransactionFactory でエクゼキュータを作成します;
  • デフォルトのSqlSessionを作成します: new DefaultSqlSession(configuration, executor, autoCommit);

デフォルトのSqlSessionを作成します。

public class DefaultSqlSession implements SqlSession {
 private final Configuration configuration;
 private final Executor executor;
 private final boolean autoCommit;
 private boolean dirty;
 private List<Cursor<?>> cursorList;
 ...
 ...
 SqlSessionメソッド固有の実装...
}

他のオブジェクトを無視して、SqlSession が作成されます。

SqlSession実行SQLステートメントプロセス

SqlSession selectOne

プロジェクト・コード

	String statement = "mapper.UserMapper.selectById";
 User user = sqlSession.selectOne(statement, 1);
 System.out.println("idで検索する "+user);
 System.out.println();
<select id="selectById" resultType="domain.User" parameterType="int">
 select * from user where id = #{id}
</select>

namespaceを設定すれば、idを繰り返すことができ、namespaceを設定しなければ、idを繰り返すことはできません。 その理由は、namespace + idはMapのキーとして使用され、namespaceがない場合、残りのidは、その後、idの重複は、互いのデータのカバレッジにつながる、namespaceを使用すると、自然なidが重複することができ、namespaceは異なり、namespace + idは当然異なります。だから、ステートメントは、名前空間+ idの組み合わせは、Mybatisがステートメントを介してselectOneビューのソースコードにSQLステートメントのポイントを見つけることが目的です。

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

ソースコードは、実際には、いわゆるselectOneを明確に見ることができる観察し、最終的に1かどうかを決定するためにリストの長さに応じて、リストコレクションを見つける、長さが1より大きい場合、それは例外を投げたに続けて

List<T> list = this.<T>selectList(statement, parameter);

メソッドのソースコード。

 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 {
 MappedStatement ms = configuration.getMappedStatement(statement);
 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();
 }
 }

DEFAULTはページングに使用され、wrapCollectionは、入力ソースコードの種類を決定することです次のとおりです。

private Object wrapCollection(final Object object) {
 if (object instanceof Collection) {
 StrictMap<Object> map = new StrictMap<Object>();
 map.put("collection", object);
 if (object instanceof List) {
 map.put("list", object);
 }
 return map;
 } else if (object != null && object.getClass().isArray()) {
 StrictMap<Object> map = new StrictMap<Object>();
 map.put("array", object);
 return map;
 }
 return object;
 }

コードの次の行を観察し、ステートメントは、名前空間+ idの組み合わせの値ではない、ブレークポイントをヒットし、それがSQLステートメントを見つけることではないことを確認します。

 MappedStatement ms = configuration.getMappedStatement(statement);
sqlステートメント内のxmlの比較は、実際にmapper.UserMapper.selectById sqlステートメントの内容の下に取得することです今取得されている、ソースコードを観察し、実行の実装では、次のステップは、エクゼキュータ(エクゼキュータ)ですので、Mybatisでは、sqlステートメントの実行で完了です。

Executor---

入力し続け、ソースコードを参照してください

executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
彼らはすべてのExecutorインターフェイスを実装しているため、O'Haoは、私はどのメソッドがそれにあるのかわからない、実際には、その後、ブレークポイントをヒットすると、呼び出しは現在CachingExecutorクラスメソッドであることを見つけることができます ソース: 。
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);
 }

前のステップによると、msはxmlの名前空間+ IDの組み合わせを介して、メソッド名によると、あなたはboundSqlを決定することができますSQLステートメントの実際の実装では、キーはキャッシュ項目である、キャッシュに後でメソッドを入力し続ける書き込まれます。

return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

ソースコードは次のとおりです。

 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.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
 tcm.putObject(cache, key, list); // issue #578 and #116
 }
 return list;
 }
 }
 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
 }

それはSQLの最初の実行ですので、キャッシュが存在しない、直接次のメソッドに

return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

呼び出しにポイントをメソッド内のBaseExExecutorになりました、ソースコードは次のとおりです。

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

呼び出しの開始時に、nullのresultHandlerの使用(Executor.NO_RESULT_HANDLER)コードを観察するために、おそらく他の一時的に見て、次のメソッドに直接する必要はありません理解しています。

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

ソースコードは次のとおりです。

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

さて、まだ直接メソッドの次の層に、に

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

今度はSimpleExecutorのdoQueryメソッドを呼び出しています。

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.<E>query(stmt, resultHandler);
 } finally {
 closeStatement(stmt);
 }
 }

これはステートメント内のjdbcではないのですか?

JDBCMybatisソース--- sqlSession 4つの大きなオブジェクト

Statement statement = connection.createStatement();
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;
 }

JDBCMybatisソース---接続

getConnection メソッドを使用します。

protected Connection getConnection(Log statementLog) throws SQLException {
 Connection connection = transaction.getConnection();
 if (statementLog.isDebugEnabled()) {
 return ConnectionLogger.newInstance(connection, statementLog, queryStack);
 } else {
 return connection;
 }
 }

トランザクション・オブジェクトはどこから来たのでしょうか?次に、openSessionメソッドを見て

アウトの作成のごく初期に元の小さなサンプルが、Connectionオブジェクトを作成していないああ、メソッドを指すように続けましょう
 @Override
 public Connection getConnection() throws SQLException {
 if (this.connection == null) {
 openConnection();
 }
 return this.connection;
 }

protected void openConnection() throws SQLException {
 if (log.isDebugEnabled()) {
 log.debug("Opening JDBC Connection");
 }
 this.connection = this.dataSource.getConnection();
 if (this.level != null) {
 this.connection.setTransactionIsolation(this.level.getLevel());
 }
 }

一目で

Statement作成例

public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
 ErrorContext.instance().sql(boundSql.getSql());
 Statement statement = null;
 try {
 statement = instantiateStatement(connection);
 setStatementTimeout(statement, transactionTimeout);
 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);
 }
 }

メソッド instantiateStatement(connection) に進みます。

protected Statement instantiateStatement(Connection connection) throws SQLException {
 String 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);
 }
 }

判断ステートメントの全体の多くは、実際には、最後の最後のconnection.prepareStatement(sql)に直接実行することですよくステートメントは、そのサブクラスのPreparedStatementの使用のうち、すべてが明確です!

正式な呼び出しは、データベースのデータを取得

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

さて、すべてが明確で、最後の呼び出しresultSetHandlerです。

public List<Object> handleResultSets(Statement stmt) throws SQLException {
 ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 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.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);
 }
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
 try {
 if (parentMapping != null) {
 handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
 } else {
 if (resultHandler == null) {
 DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
 handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
 multipleResults.add(defaultResultHandler.getResultList());
 } else {
 handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
 }
 }
 } finally {
 // issue #228 (close resultsets)
 closeResultSet(rsw.getResultSet());
 }
 }

要約

  1. MyBatisは、内部的にJDBCをカプセル化するセミORMフレームワークは、開発だけSQLステートメント自体に焦点を当てる必要がある、ドライバをロードするプロセスの複雑さに対処するためにエネルギーを費やす必要はありません、接続を作成し、ステートメントを作成するなど。プログラマが直接元のSQLを記述すると、厳密にSQL実行のパフォーマンスを制御することができます、高い柔軟性
  2. MyBatisは、XMLまたはアノテーションを使用して、ネイティブの情報を設定し、データベース媒体のレコードにPOJOのマッピングをマップすることができます。
  3. xmlファイルやアノテーションを介してステートメント構成の様々なアップで実行されるように、そしてSQLの動的パラメータでJavaオブジェクトとステートメントを介してSQLステートメントの最終的な実行を生成するためにマッピングされ、最終的にmybatisフレームワークによってSQLを実行し、結果はJavaオブジェクトとリターンにマップされます。
  4. エクゼキュータは2つのカテゴリに分けられます:
    • BaseExecutor

      • ReuseExecutor:ステートメントオブジェクトを、使用の存在を見つけるためのキーとして更新またはselect、sqlの実装では、作成時に存在しない、ステートメントオブジェクトの使用後に閉じていないが、マップには、次の時間を使用するために配置されます。
      • BatchExecutorは:更新を実行し、バッチにすべてのSQLを追加し、統一された実行を待って、それは複数のステートメントオブジェクトをキャッシュし、各ステートメントオブジェクトは、バッチによってバッチ()executeBatchの実行を待って、addBatch()終了です。バケツの数を維持することに相当し、各バケツは、独自のSQLの多くで満たされている、ちょうどリンゴの青はリンゴの多くでいっぱい、トマトの青はトマトの多くでいっぱい、そして最後に、倉庫に注ぐように。
    • CachingExecutor

Mybatisのソースコード --- SqlSessionFactoryBuilder (設定ファイルの取得)

Mybatisソースコード--- parseConfigurationは、XMLファイルを読み込みます

個人的な意見です。議論へようこそ、

Read next

バックエンドのXSS攻撃対策

もしパラメータを変更したいのであれば、クラスをデコレーションすることで、 必要に応じてデコレーション・クラスのメソッドを書き換えるだけです。

Mar 30, 2020 · 5 min read