Prepared StatementはStatementのもう少し強力なバージョンであり、常にStatementと少なくとも同じくらい速くて扱いやすいものであるべきです。
準備文はパラメータ化することができる
ほとんどのリレーショナルデータベースは、4つのステップでJDBC/SQLクエリを処理します。
ステートメントは、データベースに送信された各SQLクエリに対して、常に上記の4つの手順を実行します。準備済みステートメントは、上記の実行プロセスのステップ(1) - (3)を事前に実行します。したがって、Prepared Statementを作成するときには、事前の最適化がただちに実行されます。その結果、実行時にデータベースエンジンの負荷が軽減されます。
今、私の質問は、「Prepared Statementを使用することの他の利点は何ですか?」です。
SQL文のプリコンパイルおよびDB側のキャッシュにより、全体的に高速な実行と バッチ での同じSQL文の再利用が可能になります。
引用符や他の特殊文字の組み込みエスケープによる SQLインジェクション攻撃 の自動防止。これには、値を設定するためにPreparedStatement
setXxx()
メソッドのいずれかを使用する必要があることに注意してください。
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
preparedStatement.setString(1, person.getName());
preparedStatement.setString(2, person.getEmail());
preparedStatement.setTimestamp(3, new Timestamp(person.getBirthdate().getTime()));
preparedStatement.setBinaryStream(4, person.getPhoto());
preparedStatement.executeUpdate();
したがって、don'tは、文字列連結によってSQL文字列の値をインライン化します。
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
preparedStatement.executeUpdate();
SQL文字列内の非標準Javaオブジェクトの設定を容易にします。 Date
、 Time
、 Timestamp
、 BigDecimal
、 InputStream
( Blob
) )および Reader
( Clob
)。これらのほとんどの型では、単純なStatement
のようにtoString()
を「単に」実行することはできません。以下のユーティリティメソッドに示すように、ループ内で PreparedStatement#setObject()
を使用するようにすべてをリファクタリングすることもできます。
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
以下のように使うことができます:
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
setValues(preparedStatement, person.getName(), person.getEmail(), new Timestamp(person.getBirthdate().getTime()), person.getPhoto());
preparedStatement.executeUpdate();
動的SQLの繰り返し実行(パラメータが変更される場合)のために高速になるので、事前にコンパイルされています(1回)。
データベース文キャッシングはDB実行性能を向上させる
データベースは、以前に実行された文の実行計画のキャッシュを格納します。これにより、データベースエンジンは以前に実行されたステートメントの計画を再利用できます。 PreparedStatementはパラメータを使用するため、実行されるたびに同じSQLとして表示されるため、データベースは以前のアクセスプランを再利用でき、処理が削減されます。ステートメントはパラメータをSQL文字列に「インライン」するため、DBに対して同じSQLとして表示されず、キャッシュの使用が妨げられます。
バイナリ通信プロトコルにより、帯域幅が狭くなり、DBサーバーへの通話が速くなります。
準備済みステートメントは通常、非SQLバイナリー・プロトコルを介して実行されます。これは、パケットに含まれるデータが少なくなるため、サーバーへの通信が速くなることを意味します。経験則として、ネットワーク操作はディスク操作よりも桁違いに速く、ディスク操作はインメモリーCPU操作よりも桁違いに高速です。したがって、ネットワークを介して送信されるデータ量を減らすと、全体的なパフォーマンスに大きな影響があります。
提供されたすべてのパラメータ値のテキストをエスケープすることで、SQLインジェクションから保護します。
それらはクエリコードとパラメータ値の間のより強い分離(連結されたSQL文字列と比較して)を提供し、読みやすさを高め、コード管理者がクエリの入力と出力を素早く理解するのを助けます。
Javaでは、getMetadata()とgetParameterMetadata()を呼び出して、それぞれ結果セットフィールドとパラメータフィールドに反映できます。
Javaでは、setObject、setBoolean、setByte、setDate、setDouble、setDouble、setFloat、setInt、setLong、setShort、setTime、setTimestampを介して、Javaオブジェクトをパラメータ型としてインテリジェントに受け入れます(toStringではなくJDBCに理解可能なJDBC型フォーマットに変換します)。 () フォーマット)。
Javaでは、setArrayメソッドを介してパラメータ型としてSQL ARRAYを受け入れます
Javaでは、setClob/setNClob、setBlob、setBinaryStream、setCharacterStream/setAsciiStream/setNCharacterStreamの各メソッドを介して、CLOB、BLOB、OutputStream、およびReadersをパラメータ "feeds"としてそれぞれ受け入れます。
Javaでは、setURL、setRowId、setSQLXML、およびsetNullメソッドを使用して、DBに固有の値をSQL DATALINK、SQL ROWID、SQL XML、およびNULLに設定できます。
Javaでは、Statementからすべてのメソッドを継承します。これはaddBatchメソッドを継承し、さらにaddBatchメソッドを介して一連のバッチSQLコマンドに一致するように一連のパラメーター値を追加することを可能にします。
Javaでは、特別なタイプのPreparedStatement(サブクラスCallableStatement)を使用してストアドプロシージャを実行できます - 高性能、カプセル化、手続き型プログラミングとSQL、DB管理/保守/ロジックの微調整、そして独自のDBロジックと機能の使用
PreparedStatement
は、 SQLインジェクション攻撃 を防ぐのに非常に優れた防御策です(しかし絶対確実というわけではありません)。パラメータ値をバインドすることは、 "little Bobby Tables" が不要な訪問をするのを防ぐための良い方法です。
Statementに対するPreparedStatementの利点は次のとおりです。
SQLインジェクションの問題について詳しくは、 http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example をご覧ください。
追加するものは何もない、
1 - あなたがループでクエリを実行したい場合(1回以上)、あなたが言及した最適化のため、準備されたステートメントはより速くなることができます。
2 - パラメータ化されたクエリはSQLインジェクションを避けるための良い方法です。これはPreparedStatementでのみ利用可能です。
ステートメント内でCLOBを実行できません。
そして:(OraclePreparedStatement)ps
sQLインジェクションは準備済みステートメントによって無視されるため、準備済みステートメントのセキュリティーが向上します。
として引用されるように mattjames
JDBCでのStatementの使用は、DDL(ALTER、CREATE、GRANTなど)に使用されるように100%ローカライズする必要があります。これらはBIND VARIABLESを受け入れられない唯一のステートメントタイプであるためです。 PreparedStatementsまたはCallableStatementsは、その他すべての種類のステートメント(DML、Queries)に使用する必要があります。これらはバインド変数を受け入れるステートメントタイプです。
これは事実であり、規則であり、法律である - どこでも準備文を使用する。ステートメントをほとんどどこにも使用しないでください。
ステートメントは静的SQLステートメントの実行に使用され、入力パラメーターを受け入れることはできません。
PreparedStatementは、SQLステートメントを何度も動的に実行するために使用されます。入力パラメータを受け入れます。
準備済みクエリまたはパラメータ化クエリのもう1つの特徴は、 この記事の参考文献です
このSQL文は、同じSQL文を効率よく繰り返し実行するというデータベースシステムの機能の1つです。プリペアドステートメントはテンプレートの一種で、さまざまなパラメータを持つアプリケーションで使用されます
ステートメントテンプレートが作成され、データベースシステムに送信され、データベースシステムはこのテンプレートおよび解析を実行せずに構文解析、コンパイルおよび最適化を実行する。
一部のパラメータは、テンプレート作成後のアプリケーションでwhere句が渡されない場合、これらのパラメータをデータベースシステムに送信し、データベースシステムはSQL文のテンプレートを使用して要求に従って実行します。
アプリケーションはさまざまな手法やプロトコルを使用してパラメータを準備できるため、準備済みステートメントはSQLインジェクションに対して非常に便利です。
その時点でデータ数が増え、索引が頻繁に変更されていると、このような状況では新しい照会計画が必要になるため、準備済みステートメントは失敗する可能性があります。
Statement
インターフェースは、パラメーターなしで静的SQLステートメントを実行します
PreparedStatement
インターフェース(Statementを拡張しています)は、パラメーターを指定して、または指定せずに、プリコンパイル済みSQLステートメントを実行します
繰り返し実行に効率的
プリコンパイルされているので速いです
混乱しないでください。
この質問のすべての回答に従い、-Statement
(ただし、SQLインジェクションを使用)を使用するレガシーコードを、Statement.addBatch(String sql)
周辺のセマンティクスの理解が不十分なため、PreparedStatement
を使用するソリューションに大幅に遅いコードで変更しましたPreparedStatement.addBatch()
。
だから、ここに自分のシナリオをリストして、他の人が同じ間違いをしないようにします。
私のシナリオは
Statement statement = connection.createStatement();
for (Object object : objectList) {
//Create a query which would be different for each object
// Add this query to statement for batch using - statement.addBatch(query);
}
statement.executeBatch();
したがって、上記のコードでは、何千もの異なるクエリがあり、すべて同じステートメントに追加され、キャッシュされていないステートメントが良いため、このコードはより速く動作し、このコードはアプリでほとんど実行されませんでした.
SQLインジェクションを修正するために、このコードをに変更しました
List<PreparedStatement> pStatements = new ArrayList<>();
for (Object object : objectList) {
//Create a query which would be different for each object
PreparedStatement pStatement =connection.prepareStatement(query);
// This query can't be added to batch because its a different query so I used list.
//Set parameter to pStatement using object
pStatements.add(pStatement);
}// Object loop
// In place of statement.executeBatch(); , I had to loop around the list & execute each update separately
for (PreparedStatement ps : pStatements) {
ps.executeUpdate();
}
ご覧のとおり、私は数千のPreparedStatement
オブジェクトを作成し始め、最終的には私のシナリオでは-何千ものUPDATEまたはINSERTクエリとこれらすべてが要求されたため、バッチ処理を利用できなくなりましたクエリが異なる
SQLインジェクションの修正は、パフォーマンスの低下を犠牲にすることなく必須であり、このシナリオでPreparedStatement
を使用して実行できるとは思いません。
また、組み込みのバッチ処理機能を使用する場合、1つのステートメントのみを閉じることを心配する必要がありますが、このリストアプローチでは、再利用する前にステートメントを閉じる必要があります Reusing a PreparedStatement