Javaでのデータベース操作とPreparedStatementの基礎
目次
1. 基本:データベース接続とSQL文の準備
Javaでデータベース(ここではOracle)を操作するには、まずデータベースに接続する必要があります。そして、接続後にSQL文を準備し、実行します。以下はデータベース接続と基本的なクエリの流れです。
下記のプログラムはJavaからOracleデータベースに接続して、ユーザーが入力した商品名をもとに例としてITEM_TABLEから該当するデータを検索し、その結果を表示するプログラムです。
package dbsample; // このクラスが所属するパッケージ名
import java.sql.Connection; // データベース接続を表すクラス
import java.sql.DriverManager; // データベース接続を管理するクラス
import java.sql.PreparedStatement; // プリコンパイル済みのSQL文を実行するクラス
import java.sql.ResultSet; // SQLの実行結果を格納するクラス
import java.sql.SQLException; // SQL関連のエラー処理を行うクラス
import java.util.Scanner; // ユーザー入力を取得するためのクラス
public class DBSample { // データベースサンプルプログラムの開始
public static void main(String[] args) { // プログラムのメインメソッド
// データベース接続用変数
Connection conn = null; // データベース接続を保持する変数を初期化
// ステートメント実行用変数
PreparedStatement stmt = null; // プリコンパイル済みSQL文の実行変数を初期化
try {
// Oracle JDBCドライバのロード(JDBCドライバを使用可能にする)
Class.forName("oracle.jdbc.driver.OracleDriver"); // JDBCドライバを読み込み
// DBに接続(データベースURL、ユーザー名、パスワードを指定)
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "USER_NAME", "PASSWORD"); // データベースに接続
// ユーザー入力を取得するための準備
Scanner sc = new Scanner(System.in); // ユーザー入力を受け取るスキャナーを作成
System.out.print("検索したい商品名を入力してください:"); // ユーザーに商品名を入力させるプロンプトを表示
String name = sc.next(); // ユーザーが入力した商品名を変数nameに格納
// プレースホルダーを使ったSQL文を準備
String sql = "SELECT * FROM ITEM_TABLE WHERE ITEM_NAME = ? ORDER BY ITEM_NO"; // 商品名を条件にしてデータを取得するSQL文
// SQLをプリコンパイル(事前に準備しておく)
stmt = conn.prepareStatement(sql); // SQL文をプリコンパイルして準備
stmt.setString(1, name); // SQL文の最初の?に、ユーザーが入力した商品名を設定
// SQLの実行
ResultSet rs = stmt.executeQuery(); // SQLを実行して結果セットを取得
// 結果の取得と表示
while (rs.next()) { // 取得した結果を1行ずつ処理
System.out.println(rs.getString("ITEM_NAME")); // "ITEM_NAME"列の値を取得して表示
}
// 結果セットをクローズ(リソース解放)
rs.close(); // 結果セットを閉じ、リソースを解放
} catch (SQLException | ClassNotFoundException e) { // SQL実行やクラスロードでエラーが発生した場合
e.printStackTrace(); // エラーの詳細を表示
} finally { // エラーの有無に関わらず、最後に実行されるブロック
// リソースの解放
try {
// ステートメントがある場合はクローズ
if (stmt != null) { // ステートメントが開かれている場合
stmt.close(); // ステートメントを閉じ、リソースを解放
}
// データベース接続がある場合はクローズ
if (conn != null) { // データベース接続が確立されている場合
conn.close(); // データベース接続を閉じ、リソースを解放
}
} catch (SQLException e) { // リソース解放中にエラーが発生した場合
e.printStackTrace(); // エラーの詳細を表示
}
}
}
}
コードの解説
- データベース接続の準備
Connection connとPreparedStatement stmtを宣言して、データベース接続とSQL文の実行に使います。
- JDBCドライバのロードjavaコードをコピーする
Class.forName("oracle.jdbc.driver.OracleDriver");- JDBCドライバをロードして、Oracleデータベースに接続できるようにしています。
- データベース接続javaコードをコピーする
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "USER_NAME", "PASSWORD");- データベースURL、ユーザー名、パスワードを指定して接続しています(
USER_NAMEとPASSWORDは実際のアカウント情報を使ってください)。
- データベースURL、ユーザー名、パスワードを指定して接続しています(
- ユーザー入力の取得javaコードをコピーする
Scanner sc = new Scanner(System.in); String name = sc.next();- ユーザーに検索したい商品名を入力してもらい、その入力を変数
nameに保存しています。
- ユーザーに検索したい商品名を入力してもらい、その入力を変数
- SQL文の準備とプレースホルダーjavaコードをコピーする
String sql = "SELECT * FROM ITEM_TABLE WHERE ITEM_NAME = ? ORDER BY ITEM_NO"; stmt = conn.prepareStatement(sql); stmt.setString(1, name);- SQL文の中に「?」を使い、ユーザーが入力した商品名を後から設定しています。これにより、SQLインジェクションのリスクを減らします。
- SQLの実行と結果の取得javaコードをコピーする
ResultSet rs = stmt.executeQuery();- SQL文を実行し、検索結果を
ResultSetに格納します。
- SQL文を実行し、検索結果を
- 結果の表示javaコードをコピーする
while (rs.next()) { System.out.println(rs.getString("ITEM_NAME")); }- 検索結果を1行ずつ読み取り、
ITEM_NAME列の値を表示しています。
- 検索結果を1行ずつ読み取り、
- リソースの解放
- データベース接続やステートメントなどのリソースは
finallyブロックで閉じることで、使い終わった後に自動的に解放されます。
- データベース接続やステートメントなどのリソースは
2. PreparedStatementとは
PreparedStatement は、SQL文を効率的かつ安全に実行するためのオブジェクトです。SQL文に「?」を使うことで後から値を指定でき、次のようなメリットがあります。
- SQLインジェクション対策:ユーザーの入力がSQLの一部として実行されることを防ぎます。
- 効率的:SQL文を事前に準備(コンパイル)するため、同じSQLを繰り返し実行する場合にパフォーマンスが向上します。
- コードが見やすくなる:SQL文とパラメータの指定が分かれて書かれるため、見通しが良くなります。
3. プレースホルダーと値の設定方法
SQL文で使う「?」はプレースホルダーと呼ばれ、動的に値を設定できます。たとえば、「特定の商品名で検索するSQL」を準備するときは、以下のように「?」を使います。
String sql = "SELECT * FROM ITEM_TABLE WHERE ITEM_NAME = ?";<br>PreparedStatement stmt = conn.prepareStatement(sql);<br>stmt.setString(1, "Apple"); // 1つ目の ? に "Apple" を設定stmt.setString(1, "Apple");の 1 は、SQL文内で最初の?を指しており、これに"Apple"を代入しています。- 複数の
?がある場合も、順番に指定して埋め込みます。
4. try-with-resources構文での接続管理
try (Connection conn = ...) のように try の後にカッコでリソースを指定すると、自動で close() が呼ばれます。この構文は try-with-resources構文 と呼ばれ、リソースリークを防ぎつつコードを簡潔に書けます。
5. SQLインジェクションのリスクと防止
SQLインジェクションとは、悪意のある入力をSQL文に組み込ませる攻撃手法です。PreparedStatementを使わないで、文字列連結でSQLを組み立てると、次のような問題が起こり得ます。
String userInput = "Apple' OR '1'='1";<br>String sql = "SELECT * FROM ITEM_TABLE WHERE ITEM_NAME = '" + userInput + "'";<br>Statement stmt = conn.createStatement();<br>ResultSet rs = stmt.executeQuery(sql); // SQLインジェクションのリスクがあるこの例では、「' OR '1'='1」のように条件を操作されてしまい、本来表示されないデータまで表示されるリスクがあります。しかし、PreparedStatementを使えばこのリスクを回避できます。
まとめ
- データベース接続:
ConnectionとDriverManager.getConnection()で行い、try-with-resources構文で自動クローズします。 - PreparedStatementの使用:プレースホルダーでSQL文を準備し、
setString()やsetInt()で動的に値を埋め込むことで、SQLインジェクションのリスクを減らし、効率的にSQLを実行できます。
PreparedStatementは、Javaでデータベースを操作する際に安全で効率的な方法です。データベースを使うプログラムでは積極的に活用しましょう。

2009年那覇でホームレスになるも沖縄の方々に助けられ、2010年からNPOで地域密着で困窮支援。2016-2024年まで株式会社FM那覇代表取締役。沖縄の支援団体情報ポータルサイト「カケハシオキナワ」設立運営。防災士。コンサル・エンジニア。

