PolarDB-X 1.0(Distributed Relational Database Service)は、サーバー側でプリペアドステートメントを実行するためのクライアント/サーバー間バイナリプロトコルをサポートしています。本ページでは、このプロトコルの仕組み、対応する SQL ステートメント、および Java クライアントでの有効化方法について説明します。
メリット
プリペアドステートメントでは、SQL の構造とパラメーター値が分離されます。データベースはステートメントのテンプレートを一度受信し、その解析および最適化を行った後、以降の実行ではパラメーター値のみを受け付けます。この分離により、以下の 2 つの具体的なメリットが得られます:
解析オーバーヘッドの削減:データベースはステートメントを一度だけ解析・最適化し、その後のすべての実行で実行計画を再利用します。アプリケーションがパラメーター値のみが異なる類似したステートメントを多数実行する場合、ステートメント全体に対する繰り返しの解析コストが完全に排除されます。
SQL インジェクションへの保護:パラメーターが SQL 構造とは別に送信されるため、ユーザーが提供した入力は決して SQL 構文として解釈されません。データベースはこれを常に「値」として扱い、「コマンド」としては扱いません。
対応コマンド
このプロトコルは、以下の MySQL バイナリプロトコルコマンドをサポートしています:
| コマンド | 説明 |
|---|---|
| COM_STMT_PREPARE | ステートメントのテンプレートをサーバーに送信し、解析および最適化を実行します |
| COM_STMT_EXECUTE | 事前に準備済みのステートメントを、バインドされたパラメーター値とともに実行します |
| COM_STMT_CLOSE | プリペアドステートメントが保持するサーバー側リソースを解放します |
| COM_STMT_RESET | プリペアドステートメントを閉じずに、初期状態にリセットします |
このプロトコルは Java をはじめとする各種プログラミング言語で利用可能です。MySQL におけるプリペアドステートメントコマンドの詳細については、「プリペアドステートメント」をご参照ください。
SQL ステートメントの対応状況
すべての DML ステートメント(SELECT、UPDATE、DELETE、INSERT)をプリペアドステートメントとして使用できます。
DML 以外のステートメントはプリペアドステートメントとして使用できません。SHOW および SET はサポートされていません。
以下のステートメントは、MySQL CLI ではプリペアドステートメントとして使用できません:
mysql> SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
mysql> PREPARE stmt2 FROM @s;
mysql> SET @a = 6;
mysql> SET @b = 8;
mysql> EXECUTE stmt2 USING @a, @b;Java クライアントでのサーバー側プリペアドステートメントの有効化
デフォルトでは、MySQL JDBC ドライバーはクライアント側でプリペアドステートメントを処理します。つまり、ドライバーがローカルでステートメントの準備を行い、COM_STMT_PREPARE をサーバーに送信しません。サーバー側プリペアドステートメントを利用するには、JDBC 接続 URL に useServerPrepStmts=true を追加してください。このパラメーターが指定されていない場合、コード内で prepareStatement() を呼び出しても、ドライバーは通常のクエリを送信します。
接続 URL の例:
jdbc:mysql://xxxxxx:3306/xxxxxx?useServerPrepStmts=trueJava のサンプルコード
以下のサンプルは、PolarDB-X 1.0 に接続し、サーバー側プリペアドステートメントを用いて INSERT を実行するものです:
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection(
"jdbc:mysql://xxxxxx:3306/xxxxxx?useServerPrepStmts=true",
"xxxxx",
"xxxxx"
);
String sql = "insert into batch values(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 0);
preparedStatement.setString(2, "corona-db");
preparedStatement.executeUpdate();接続 URL および認証情報内のプレースホルダー値を、ご利用の実際のエンドポイント、データベース名、ユーザー名、パスワードに置き換えてください。