りんごとバナナとエンジニア

エンジニア修行の記録

ドライバからDBを操作するときはPreparedStatementを使う

今仕事で使っているコードではJavaからCassandraとMySQLを動かしているが、その両方で使っているのがPreparedStatement。
これはどういうものかというと、あらかじめクエリのひな形を用意しておいて、実際に投げるときに値をバインドするというもの。

例えばJavaのCassandra Driverなら、

PreparedStatement psSelectTestTable = session.prepare("SELECT * FROM test_table WHERE id = ?;");

のようにあらかじめクエリを宣言しておく。 そして投げるときは、

BoundStatement bdSelectTestTable = psSelectTestTable.bind("1");
ResultSetFuture resultSetFuture = session.executeAsync(bdSelectTestTable);

のようにする。PreparedStatementの"?"のプレースホルダ部分に1が入った状態で、クエリが投げられることになる。

このようなPreparedStatementに相当する機能は、Cassandra Driverだけの話ではなく、Pythonのmysqlclientなど、他のDBドライバにもある。
名前は異なれど、「クエリのひな形を用意して後から値をバインドする」という方式を取っているものが多いだろう。

PreparedStatementを使うと何が良いのかは、大きく2つある。
1つは、クエリをあらかじめ用意しておくことで、毎回コンパイルする手間を減らし、パフォーマンスを向上させること。
しかしこれは、単にクエリを1回投げるだけであれば意味がない。むしろ、アプリケーションのサーバサイドなど、同じクエリのひな形を何度も繰り返し使うときに有用となる。
この意味で、上にあげたようなCassandra DriverはPreparedStatementを積極的に使うべき例といえる。上の例ではクエリを1回しか投げていないが、Cassandraは非同期で多数のリクエストを処理するのが得意なDBなので、大規模データを高速に処理したい状況で使われることが多いためである。

もう1つは、SQLインジェクションの基本的な対策ができること。クエリの構文解析自体はプレースホルダの状態で行われ、それが通った後に値をはめ込んで実行する。このため、値の部分に悪意あるクエリが入力されたとしても、そもそもクエリとはみなされないため、実行されてしまうことがない。