🛁

PreparedStatementは静的SQL?それとも動的SQL?

2024/01/20に公開

はじめに

実務で使う機会のあったPreparedStatementについてまとめました。データベースとのやり取りは、現代のソフトウェア開発において不可欠な要素です。このプロセスを効率的かつ安全に行うための鍵となるのがPreparedStatementという技術なのです。
PreparedStatementを最大限に活用するためには、まず静的SQLと動的SQLの仕組みを理解することが重要です。本記事ではこれらの概念から説明し、PreparedStatementについて解説していきます。

静的SQLとは

プログラム作成時点で既にSQL文が決まっており、コンパイル時にDBへ問い合わせる手法のこと。
パフォーマンスが良くセキュリティリスクも低い反面、柔軟性に欠けたり、コードが膨大になりやすい。

静的SQLのメリット

  • SQL文がプログラムに組み込まれており、外部からの不正な操作を受けにくい
  • コンパイル時の最適化と実行時の効率性により、高速なパフォーマンスを提供する
  • コードが明確に定義されているため、メンテナンスがしやすい

静的SQLのデメリット

  • ユーザーの入力や条件によって変わるクエリに対して、柔軟な対応ができない
  • 微妙に異なるクエリを扱う場合も、別々のSQLを用意する必要があるため、コードが膨大になりやすい

静的SQLを使うべき場面

  • セキュリティが最優先される場面
  • パフォーマンスが重視され、クエリが事前にわかっている場合
  • 長期間にわたって安定して運用する必要があるアプリケーションの場合

静的SQLの使用例(Statementの使用)

静的SQLでは、SQL文はコンパイル時に固定される。

public class StaticSQLExampleWithStatement {
    public static void main(String[] args) {
        try {
            // Oracleデータベースに接続
            Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "username", "password");

            // 静的SQL
            String sql = "SELECT * FROM employees WHERE department_id = 10";

            // Statementを作成してSQLを実行
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(sql);

            // 結果を処理
            while (rs.next()) {
                // ...
            }

            // リソースを解放
            rs.close();
            stmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

動的SQLとは

プログラムの実行時にSQL文を生成し、DBへ問い合わせる手法のこと。
高い柔軟性や再利用性を発揮する反面、セキュリティやパフォーマンスに注意を払う必要がある。

動的SQLのメリット

  • ユーザーの入力や状況に応じて、リアルタイムでSQL文を組み立てられる
  • 似たような構造のクエリでもパラメータを変えるだけで対応できるため、コードが重複しにくい

動的SQLのデメリット

  • SQLインジェクション攻撃のリスクがある
  • クエリを実行の都度作成する必要があるため、静的SQLと比べるとパフォーマンスが劣る
  • SQL文が動的に生成されるため、可読性が低くなりやすい

動的SQLを使うべき場面

  • ユーザーの入力や条件に基づいて異なるクエリが必要な場合
  • 特定の複雑な条件や組み合わせに基づいてデータを取得する必要がある場合
  • 同じようなクエリを何度も実行するが、微妙に異なるパラメータや条件を使用する場合

動的SQLの使用例(Statementの使用)

動的SQLでは、SQL文が実行時に組み立てられる。

public class DynamicSQLExampleWithStatement {
    public static void main(String[] args) {
        try {
            // Oracleデータベースに接続
            Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "username", "password");

            // 動的SQL
            String column = "department_id";
            int value = 10;
            String sql = "SELECT * FROM employees WHERE " + column + " = " + value;

            // Statementを作成してSQLを実行
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery(sql);

            // 結果を処理
            while (rs.next()) {
                // ...
            }

            // リソースを解放
            rs.close();
            stmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

PreparedStatementとは

SQL文をコンパイルしてデータベースに送信する前に、パラメータをバインドするための機能を提供する、データベース操作のためのインターフェースのこと。
クエリ内の特定の値をパラメータとして設定できる。パラメータは実行時に値が設定される。

PreparedStatementと静的SQL/動的SQL

  • PreparedStatementは基本的に静的SQLの性質を持つが、クエリ内の特定の値をパラメータとして動的に設定することができる
  • クエリをプリコンパイルし、実行時にはパラメータの値だけがセットされるが、その際にクエリの構造が変更されることはないため、高い安全性を発揮する
  • パラメータの値は実行時に設定し、同じPreparedStatementを使って異なるデータをクエリすることができるため、一定の柔軟性が確保される

PreparedStatementの使用例

PreparedStatementを使用することで、SQL文が事前にコンパイルされ、実行時にパラメータがセットされる。SQL文が静的なままで、動的な要素を安全に組み込むことが可能。

public class PreparedStatementExample {
    public static void main(String[] args) {
        try {
            // Oracleデータベースに接続
            Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "username", "password");

            // 静的SQLを準備(パラメータは?で表される)
            String sql = "SELECT * FROM employees WHERE department_id = ?";
            PreparedStatement pstmt = conn.prepareStatement(sql);

            // パラメータを設定(1番目の?に10をセット)
            pstmt.setInt(1, 10);

            // SQLを実行
            ResultSet rs = pstmt.executeQuery();

            // 結果を処理
            while (rs.next()) {
                // ...
            }

            // リソースを解放
            rs.close();
            pstmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

まとめ

PreparedStatementは静的SQLの性質を持ちつつ、パラメータを通じて動的な要素を取り入れることができます。そのため、アプリケーションのニーズに応じて柔軟性と安全性をバランスよく提供することが可能な技術です。ただし、SQL文の構造を実行時に変更することは不可能なため、クエリ自体が動的に変更される必要がある場合には、他の手法を検討するようにしましょう。
以上PreparedStatementについてまとめました。読んでいただきありがとうございました。

参考

https://docs.oracle.com/javase/jp/17/docs/api/java.sql/java/sql/PreparedStatement.html
https://www.javadrive.jp/servlet/database/index10.html
https://www.ibm.com/docs/ja/db2-for-zos/11?topic=program-differences-between-static-dynamic-sql

Discussion