JDBCとは?
JDBC(Java Database Connectivity)は、Javaアプリケーションからデータベースへアクセスするための標準API。データベースの種類に依存しない統一的なインターフェースを提供し、CRUD操作、トランザクション管理、バッチ処理などを実現します。
ドライバ読み込みと接続
JDBC 4.0以降はドライバの明示的なロードが不要(自動検出)
// データベース接続
String url = "jdbc:mysql://localhost:3306/mydb";
String user = "username";
String password = "password";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// 接続成功
System.out.println("接続成功");
} catch (SQLException e) {
e.printStackTrace();
}
● try-with-resources で自動クローズ
● URL形式: jdbc:[subprotocol]://[host]:[port]/[database]
主要インターフェース
Connection
データベースとの接続を管理
Statement
静的SQL文を実行(PreparedStatementを推奨)
PreparedStatement
パラメータ付きSQL文を実行(SQLインジェクション対策)
ResultSet
SELECT文の結果セットを保持
CRUD操作
SELECT(データ取得)
String sql = "SELECT * FROM users WHERE age > ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 20);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println(id + ", " + name + ", " + age);
}
}
}
INSERT(データ追加)
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, "山田太郎");
pstmt.setInt(2, 25);
int rowsAffected = pstmt.executeUpdate();
System.out.println(rowsAffected + "件追加されました");
}
UPDATE(データ更新)
String sql = "UPDATE users SET age = ? WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 30);
pstmt.setInt(2, 1);
int rowsAffected = pstmt.executeUpdate();
System.out.println(rowsAffected + "件更新されました");
}
DELETE(データ削除)
String sql = "DELETE FROM users WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1);
int rowsAffected = pstmt.executeUpdate();
System.out.println(rowsAffected + "件削除されました");
}
トランザクション管理
デフォルトは自動コミット(autoCommit = true)
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
// 自動コミットを無効化
conn.setAutoCommit(false);
// SQL実行
String sql1 = "INSERT INTO accounts (name, balance) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql1)) {
pstmt.setString(1, "口座A");
pstmt.setInt(2, 1000);
pstmt.executeUpdate();
}
String sql2 = "INSERT INTO accounts (name, balance) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql2)) {
pstmt.setString(1, "口座B");
pstmt.setInt(2, 2000);
pstmt.executeUpdate();
}
// コミット
conn.commit();
System.out.println("トランザクション成功");
} catch (SQLException e) {
// ロールバック
if (conn != null) {
try {
conn.rollback();
System.out.println("トランザクションをロールバック");
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
主要メソッド
setAutoCommit(false)
自動コミットを無効化
commit()
変更をデータベースに確定
rollback()
変更を取り消して元に戻す
注意: autoCommit=false の状態で接続をクローズすると、未コミットの変更は失われます
バッチ処理
複数のSQL文をまとめて実行することでパフォーマンス向上
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// バッチに追加
pstmt.setString(1, "山田太郎");
pstmt.setInt(2, 25);
pstmt.addBatch();
pstmt.setString(1, "佐藤花子");
pstmt.setInt(2, 30);
pstmt.addBatch();
pstmt.setString(1, "鈴木一郎");
pstmt.setInt(2, 28);
pstmt.addBatch();
// バッチ実行
int[] results = pstmt.executeBatch();
System.out.println(results.length + "件のバッチ処理が完了");
}
addBatch()
バッチにSQL文を追加
executeBatch()
バッチ内の全SQL文を実行
clearBatch()
バッチをクリア
例外処理(SQLException)
try {
// JDBC処理
Connection conn = DriverManager.getConnection(url, user, password);
// ...
} catch (SQLException e) {
System.err.println("SQLエラー: " + e.getMessage());
System.err.println("SQLState: " + e.getSQLState());
System.err.println("ErrorCode: " + e.getErrorCode());
e.printStackTrace();
}
SQLExceptionのメソッド
getMessage()
エラーメッセージを取得
getSQLState()
SQLStateコードを取得
getErrorCode()
ベンダー固有のエラーコードを取得
実務で役立つTips
SQLインジェクション対策
必ずPreparedStatementを使用し、文字列結合でSQLを組み立てない
リソース管理
try-with-resourcesを使ってConnection、Statement、ResultSetを確実にクローズ
接続プール
実運用ではHikariCP等の接続プールライブラリを使用してパフォーマンス向上
NULL値の処理
ResultSet.wasNull()でNULL判定を行う(getString等はnullを返す)
JDBC実行フロー
try-with-resourcesを使えばステップ5は自動実行
よく使うパターン
単一レコード取得
String sql = "SELECT * FROM users WHERE id = ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, userId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
// レコードが存在
String name = rs.getString("name");
int age = rs.getInt("age");
// 処理...
} else {
// レコードが存在しない
System.out.println("ユーザーが見つかりません");
}
}
}
接続プール使用例(HikariCP)
// HikariCPの設定
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("username");
config.setPassword("password");
config.setMaximumPoolSize(10);
HikariDataSource dataSource = new HikariDataSource(config);
// 接続取得(プールから)
try (Connection conn = dataSource.getConnection()) {
// SQL実行
}
自動生成キーの取得
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql,
Statement.RETURN_GENERATED_KEYS)) {
pstmt.setString(1, "山田太郎");
pstmt.setInt(2, 25);
pstmt.executeUpdate();
// 自動生成されたIDを取得
try (ResultSet rs = pstmt.getGeneratedKeys()) {
if (rs.next()) {
int generatedId = rs.getInt(1);
System.out.println("生成されたID: " + generatedId);
}
}
}
メタデータ取得
// データベースメタデータ
DatabaseMetaData metaData = conn.getMetaData();
String dbName = metaData.getDatabaseProductName();
String dbVersion = metaData.getDatabaseProductVersion();
// ResultSetメタデータ
ResultSet rs = pstmt.executeQuery();
ResultSetMetaData rsMetaData = rs.getMetaData();
int columnCount = rsMetaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = rsMetaData.getColumnName(i);
String columnType = rsMetaData.getColumnTypeName(i);
}