[ハンドティア MyBatis ソースコード] JDBC プロセッサ StatementHandler

MyBatis の実行プロセスを確認する

MyBatis は JDBC ベースの Dao フレームワークですが、MyBatis はすべての JDBC 関連の操作を StatementHandler に入れるため、前に説明したセッションとエグゼキュータでは JDBC についてまったく言及していませんでした。

SQL リクエストはセッションを通過し、次にエグゼキュータを通過し、最後に StatementHandler によって JDBC が実行され、最終的にデータベースに到達します。関係は次のとおりです。

ここに画像の説明を挿入

注: セッション内の SqlSession、Executer、および StatementHandler の比率は 1:1:N であると前述しました (ここでの N は、セッションを通じて Sql が呼び出される回数によって異なります)。SQL を 1 回実行するだけでよく、SQL は StatementHandler に対応するため、この比率は再利用可能なエグゼキュータの場合にも当てはまります。ただし、データベース操作をまったく行わないため、キャッシュが取得されると影響を受けます。

StatementHandler の定義と構造

StatementHandler定义:

JDBCプロセッサは、JDBCに基づいてJDBC文を構築し、パラメータを設定してSQLを実行します。セッションで SQL が呼び出されるたびに、対応する一意の Statement インスタンスが存在します

StatementHandler结构

ここに画像の説明を挿入

StatementHandler インターフェースは、JDBC 操作に関連するメソッドを次のように定義します。

// 基于JDBC 声明Statement
Statement prepare(Connection connection, Integer transactionTimeout)
    throws SQLException;
// 为Statement 设置方法参数
void parameterize(Statement statement)
    throws SQLException;
// 添加批处理(并非执行)
void batch(Statement statement)
    throws SQLException;
// 执行update操作
int update(Statement statement)
    throws SQLException;
// 执行query操作
<E> List<E> query(Statement statement, ResultHandler resultHandler)
    throws SQLException;

StatementHandler には 3 つのサブクラスがあります。

  • SimpleStatementHandler
  • PreparedStatementHandler
  • CallableStatementHandler

それぞれJDBCのStatement、PreparedStatement、CallableStatementに相当します。

3 種類のエグゼキュータが頻繁に使用されるのとは異なり、ここでの 3 種類は、ほとんどの場合に使用される PreparedStatementHandler です。

PreparedStatement には次の主な機能があります。

  1. プリコンパイルされた SQL ステートメント。PreparedStatement オブジェクトは、プリコンパイルされた SQL ステートメントを表します。SQL ステートメントは、実行のためにデータベースに送信される前にコンパイルされます。これにより、ステートメントの実行効率が向上します。
  2. パラメータ設定を実現します。PreparedStatement を使用すると、setXXX() メソッドを通じてパラメータを設定し、この SQL ステートメントを実行できます。これにより、SQL インジェクションが防止され、セキュリティが向上します。
  3. ステートメントを再利用します。PreparedStatement は繰り返し実行しても安全です。データベース リソースを最適化し、ステートメントを効率的に実行します。
  4. バッチ更新。PreparedStatement は、addBatch() メソッドによる複数の SQL コマンドの追加と、executeBatch() メソッドによるバッチでの実行をサポートしています。これにより、レコードの更新/挿入/削除の速度が大幅に向上します。

PreparedStatement の例を次に示します。

String sql = "INSERT INTO EMP(ID, NAME) VALUES (?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setInt(1, 101);    // 为第一个参数设置 int 类型的值 101  
pstmt.setString(2, "John"); // 为第二个参数设置 String 类型的值 "John"
pstmt.addBatch();

pstmt.setInt(1, 102);   
pstmt.setString(2, "Tom");
pstmt.addBatch();  

pstmt.executeBatch(); // 批量执行命令

この例では:

  1. まず、2 つのパラメーターを使用する SQL ステートメントをプリコンパイルします。
  2. 次に、これら 2 つのパラメータに特定の値を設定し、バッチに追加します。
  3. 最後に、executeBatch() メソッドによってバッチ処理が実行され、2 つのレコードが挿入されます。

要約すると、PreparedStatement の主な機能は次のとおりです。

  1. SQL ステートメントをプリコンパイルして実行効率を向上させます。
  2. SQLインジェクションを防ぐためのパラメータ設定を実装します。
  3. データベース リソースを最適化するために、これを安全に繰り返すことができます。
  4. バッチ更新をサポートし、レコードの更新/挿入/削除の速度を大幅に向上させます。

PreparedStatement はデータベースへの JDBC アクセスの重要な実装であり、プリコンパイルとパラメータ設定を通じてデータベース アクセスをより効率的かつ安全にします。

これら 3 つの StatementHandler には次の共通点があります。

  • バッチパラメータを追加する
  • 返される行数を設定します

ここに画像の説明を挿入

したがって、現時点では、これらの共通点を処理するために BaseStatementHandler を抽象化します。
ここに画像の説明を挿入

PreparedStatementHandler実行処理

ここに画像の説明を挿入

前回の記事で述べたように、データをクエリするための重要なメソッドは doQuery() です。これは、キャッシュ関連のビジネスを処理するためにクエリ メソッドによってラップされています。

ここに画像の説明を挿入

データベースにクエリを実行する実行プロセスは、次の 3 つのフェーズに分かれています。

  • 预处理: ここでの前処理は、Connection を介して Statement を作成するだけでなく、パラメーターの設定も行います。
    SimpleExecuter クラスでは次のようになります。
    ここに画像の説明を挿入

  • 执行: SQL を実行して結果セットを取得する部分と、結果マッピングの処理 (後述) の 2 つの部分が含まれます。
    ここに画像の説明を挿入
    ここに画像の説明を挿入

    結果セットを取得した後、ResultSetWrapper でラップされていることがわかります。

  • 关闭: ステートメントを直接閉じます。

パラメータの処理と結果セットのカプセル化には、データベース フィールドと JavaBean 間の相互マッピングが含まれますが、これは比較的複雑です。したがって、ParameterHandler と ResultSetHandler の 2 つの特別なコンポーネントをそれぞれ使用します。次に、パラメータ処理と結果セットのカプセル化の処理フローを見てみましょう。

MyBatis では、PreparedStatementHandler の実行処理は次のようになります。

  1. Connection.prepareStatement(sql) を呼び出して PreparedStatement オブジェクトを作成します。sql はマッピング ファイルに設定された SQL ステートメントであり、パラメータ #{property} が含まれる場合があります。
  2. SQL ステートメントのパラメータの値を解析して設定します。
    • #{property} を解析してパラメータ名とパラメータ値を取得します。パラメータ値は、渡された Java オブジェクトのプロパティ値から派生します。
    • パラメータの型に応じて、PreparedStatement の setXxx() メソッドを呼び出してパラメータ値を設定します。整数を設定する setInt()、文字列を設定する setString() など。
  3. PreparedStatement の useUpdate() を呼び出して挿入、更新、削除操作を実行するか、executeQuery() を呼び出してクエリ操作を実行します。
  4. クエリ操作を実行する場合は、ResultSetHandler を呼び出して結果セットを処理する必要もあります。
    • DefaultResultSetHandler は、ResultSet を List にラップして返します。
    • BeanResultSetHandler は、ResultSet を JavaBean オブジェクトとしてカプセル化して返します。
  5. PreparedStatement.close() および ResultSet.close() を呼び出してリソースを解放します。

パラメータの処理

以下の部分に相当します。
ここに画像の説明を挿入

パラメータ処理は、Java Bean をデータ型に変換することです。合計 3 つのステップがあります。

  • パラメータ変換
  • パラメータマッピング
  • パラメータの割り当て

パラメータ変換

MyBatis では、パラメータ変換ロジックは次のようにParamNameResolver実装されています。

パラメータ変換とは、JAVA メソッドの通常のパラメータをカプセル化して Map に変換し、マップ内のキーが SQL のパラメータ参照に対応するようにすることです。

@Select({
    
    "select * from users where name=#{name} or age=#{user.age}"})
@Options
User selectByNameOrAge(@Param("name") String name, @Param("user") User user);
  • パラメータが 1 つで @param アノテーションが設定されていない場合は、SQL の参照名を無視して直接変換されます。
  • 複数のパラメータの場合: @Param で設定された名前が優先され、そうでない場合はパラメータ番号、つまり「param1、param2...」(キーとして) が代わりに使用されます。
    • JDK1.8以降、リフレクションにより変数名をキーとして直接取得できるようになりました javacのコンパイル時に -parametersコンパイルパラメータが設定されていることが前提です 設定されていない場合はarg0、arg1に変換されます

例えば:
ここに画像の説明を挿入

ここに画像の説明を挿入
プロセスは次のとおりです。

ここに画像の説明を挿入

パラメータマッピング

マッピングとは、マップ内のキーが SQL にバインドされたパラメーターにどのように対応するかを指します。以下の状況

  • 単一のプリミティブ型: 直接マッピング。SQL では参照名を省略しないでください。
  • マップタイプ: マップキーに基づくマッピング
  • オブジェクト: 属性名マッピングに基づいて、ネストされたオブジェクト属性アクセスをサポートします。

オブジェクト型では、プロパティ内の属性をマップするために「.」メソッドがサポートされています。例: user.age

@Select({
    
    "select * from users where name=#{name} or age=#{param2.age}"})
@Options
User selectByNameOrAge(@Param("name") String name, @Param("user") User user);

パラメータの割り当て

TypeHandler を通じて PrepareStatement の値を設定します。通常、一般的なデータ型 MyBatis には対応する TypeHandler があります。

ここに画像の説明を挿入

結果セットの処理

ここに画像の説明を挿入

ResultSet データを読み取り、各行を対応するオブジェクトに変換することを指します。変換プロセス中に、ユーザーは ResultContext を通じて変換を続行するかどうかを制御できます。変換されたオブジェクトは一時的に ResultHandler に保存され、最終的にリストにカプセル化されて呼び出し元に返されます。

ここに画像の説明を挿入

結果セット変換のロジックの 99% は DefaultResultSetHandler に実装されています。プロセス全体は次の段階に大別できます。

  • 結果セットの読み取り

  • 結果セット内の行を反復処理します。
    ここに画像の説明を挿入

    • その本質は、 resultSet.next() を呼び出して行を走査し、コンテキストに基づいて読み取りを続行するかどうかを判断することです。これは、前述した ResultContext の役割にも応答します。
      ここに画像の説明を挿入
    • 結果オブジェクトの一時的な保存では、解析されたオブジェクトを ResultHandler に置きます。
      ここに画像の説明を挿入
  • オブジェクトを作成する: 4 つのケースがあります。

  1. オブジェクトはコンストラクター メソッドを使用して作成されます。MyBatis はパラメータなしのコンストラクターを呼び出して Bean インスタンスを作成します。
    <resultMap id="UserResultMap" type="User">
      <constructor>
        <idArg column="id" />
      </constructor>
    </resultMap>
    
  2. オブジェクトはファクトリ メソッドを使用して作成されます。MyBatis は静的ファクトリ メソッドを呼び出して Bean インスタンスを作成します。
    <resultMap id="UserResultMap" type="User">
      <factory>
        <constructor>
          <idArg column="id" />
        </constructor>
      </factory>  
    </resultMap>
    
  3. 既存のオブジェクトにマッピングされます。MyBatis は結果セットから取得したフィールド値を Bean インスタンスのプロパティに設定します。
    <resultMap id="UserResultMap" type="User">
      <id property="id" column="id" />
      <result property="username" column="username"/>
    </resultMap>
    
  4. マッピングはマッパー インターフェイスを使用して実装されます。マッパー インターフェイスは、Bean プロパティ値を取得および設定するためのメソッドのみを含む単純なインターフェイスです。MyBatis はこのインターフェースの実装を作成し、それを使用して結果セットの値を設定します。
    public interface UserMapper {
          
          
      void setId(Integer id);
      void setUsername(String username);
    }
    
    <resultMap id="UserResultMap" type="UserMapper">
      <id property="setId" column="id" />
      <result property="setUsername" column="username"/>
    </resultMap> 
    
  • 塗りつぶしプロパティ
    ここに画像の説明を挿入

    • 自動マッピング
    • 手動マッピング: ResultMapping に基づいて属性の値を取得します。

マッピングの側面については、MyBatis マッピング システムの記事で説明します。

おすすめ

転載: blog.csdn.net/zyb18507175502/article/details/130833169