JDBC(Java Database Connectivity)は、Java アプリケーションがリレーショナルデータベースと対話するための標準化された API です。Sun Microsystems(現 Oracle)が策定したこの仕様により、MySQL、PostgreSQL、Oracle など異なるデータベース製品に対し、共通のインターフェースでアクセス可能になります。開発者はデータベース固有の実装を意識せずに、SQL の実行・結果取得・トランザクション制御などの基本操作を記述できます。
アーキテクチャの概要
JDBC は「アプリケーション → JDBC API(java.sql, javax.sql)→ JDBC ドライバ → データベースサーバー」というレイヤード構造で動作します。各データベースベンダーが提供する JDBC ドライバ(例:MySQL Connector/J)が、共通のインタフェースを実装することで、上位層のコードはドライバの差異を意識せずに記述可能です。
接続設定と DataSource の活用
接続管理には DataSource インタフェースを推奨します。これは、接続プーリングやセキュリティ設定の柔軟性を高めるためのベストプラクティスです。
import javax.sql.DataSource;
import com.mysql.cj.jdbc.MysqlDataSource;
// 抽象型で保持 → 実装依存を最小限に抑える
DataSource ds = new MysqlDataSource();
((MysqlDataSource) ds).setUrl("jdbc:mysql://localhost:3306/accounting_db?serverTimezone=UTC&useSSL=false");
((MysqlDataSource) ds).setUser("app_user");
((MysqlDataSource) ds).setPassword("secure_password");
上記のように、DataSource 型で変数を宣言し、キャストによる設定を行うことで、後続のデータベース移行時(例:MySQL → PostgreSQL)に、ドライバクラス名と設定メソッドの変更のみで済み、ビジネスロジックへの影響をほぼゼロに抑えられます。
安全な SQL 実行:PreparedStatement の利用
動的なクエリ生成では、文字列連結による SQL インジェクションリスクが発生します。これを回避するには、プレースホルダ(?)を用いた PreparedStatement を使用します。
String insertSql = "INSERT INTO transactions (category, amount, date, note) VALUES (?, ?, ?, ?)";
try (Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
pstmt.setString(1, "食費"); // 1番目の ?
pstmt.setDouble(2, 3850.0); // 2番目の ?
pstmt.setDate(3, java.sql.Date.valueOf("2024-06-15"));
pstmt.setString(4, "スーパーでの買い物");
int rowsAffected = pstmt.executeUpdate();
System.out.println(rowsAffected + " 行が挿入されました。");
}
結果セットの処理
SELECT 文の実行結果は ResultSet オブジェクトとして返されます。各行へのアクセスには next() メソッドでカーソルを進める必要があります。
String selectSql = "SELECT id, category, amount, date FROM transactions WHERE amount > ?";
try (Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement(selectSql)) {
pstmt.setDouble(1, 2000.0);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
long id = rs.getLong("id");
String category = rs.getString("category");
double amount = rs.getDouble("amount");
LocalDate date = rs.getDate("date").toLocalDate();
System.out.printf("ID:%d | %s | ¥%.2f | %s%n", id, category, amount, date);
}
}
}
CRUD 操作の統一フロー
- CREATE(挿入):
executeUpdate()を使用し、影響行数を確認 - READ(検索):
executeQuery()でResultSetを取得し、while(rs.next())で反復 - UPDATE(更新): WHERE 句付きの UPDATE 文を
executeUpdate()で実行 - DELETE(削除): 同様に WHERE 条件付き DELETE を
executeUpdate()で実行
リソース管理の重要性
JDBC では、Connection・Statement・ResultSet の各オブジェクトがシステムリソース(ソケット、メモリ)を占有します。必ず try-with-resources 構文または明示的な close() 呼び出しで解放してください。特に、ResultSet は Statement を閉じると自動的に無効化されるため、処理順序に注意が必要です。
実用例:シンプルな家計簿アプリケーション
以下は、収入/支出を記録・照会する最小限の JDBC 実装です。テーブル構造は以下の通りです:
CREATE TABLE transactions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
category VARCHAR(32),
amount DECIMAL(10,2),
type ENUM('income', 'expense'),
date DATE,
note TEXT
);
このスキーマに基づき、insertTransaction() および listExpensesByMonth() のようなメソッドを実装することで、JDBC の基本操作を体系的に習得できます。