概要
業務システムの多くはデータベースとのやり取りを伴います。Java では JDBC(Java Database Connectivity)が標準 API として用意されており、外部ライブラリを追加しなくても基本的な DB 操作が可能です。フレームワークに任せる場面が増えた今でも、JDBC の基本を押さえておくことはトラブル時の原因調査や、フレームワークが生成する SQL の理解に直結します。この記事では、DriverManager による接続取得、Statement を使った SELECT・INSERT・UPDATE・DELETE、そして try-with-resources によるリソースの確実な解放までを整理します。保守案件や社内ツールで「素の JDBC」を触る場面に備え、動作する完結したコードで基本操作を確認します。
使いどころ
社内管理ツールから従業員マスタを参照・更新する画面の裏側を JDBC で実装する
バッチ処理で CSV から読み取ったデータを DB テーブルへ INSERT する
障害調査時にフレームワークを経由せず、直接 JDBC でクエリを実行して状態を確認する
コード例
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class JdbcBasicSample {
record Employee(int id, String name, String dept, int salary) {}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(
"jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1", "sa", "");
}
public static void setup(Connection conn) throws SQLException {
try (var stmt = conn.createStatement()) {
stmt.execute("""
CREATE TABLE IF NOT EXISTS employees (
id INT PRIMARY KEY,
name VARCHAR(50),
dept VARCHAR(30),
salary INT
)
""");
stmt.execute("DELETE FROM employees");
stmt.execute("INSERT INTO employees VALUES (1, '田中太郎', '営業', 350000)");
stmt.execute("INSERT INTO employees VALUES (2, '鈴木花子', '開発', 420000)");
stmt.execute("INSERT INTO employees VALUES (3, '佐藤次郎', '開発', 380000)");
}
}
public static List<Employee> findAll(Connection conn) throws SQLException {
var results = new ArrayList<Employee>();
var sql = "SELECT id, name, dept, salary FROM employees ORDER BY id";
try (var stmt = conn.createStatement();
var rs = stmt.executeQuery(sql)) {
while (rs.next()) {
results.add(new Employee(
rs.getInt("id"), rs.getString("name"),
rs.getString("dept"), rs.getInt("salary")));
}
}
return results;
}
public static int updateSalary(Connection conn, int id, int newSalary)
throws SQLException {
var sql = "UPDATE employees SET salary = ? WHERE id = ?";
try (var pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, newSalary);
pstmt.setInt(2, id);
return pstmt.executeUpdate();
}
}
public static int delete(Connection conn, int id) throws SQLException {
var sql = "DELETE FROM employees WHERE id = ?";
try (var pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
return pstmt.executeUpdate();
}
}
public static void main(String[] args) throws SQLException {
try (var conn = getConnection()) {
setup(conn);
System.out.println("=== 全件取得 ===");
for (var emp : findAll(conn)) {
System.out.printf("id=%d name=%s dept=%s salary=%d%n",
emp.id(), emp.name(), emp.dept(), emp.salary());
}
System.out.println("\n=== UPDATE ===");
updateSalary(conn, 1, 370000);
System.out.println("田中の給与更新完了");
System.out.println("\n=== DELETE ===");
delete(conn, 3);
System.out.println("削除後: " + findAll(conn).size() + " 件");
}
}
}Version Coverage
var による型推論とテキストブロック(""")で SQL を見やすく記述できる。record で行データを型安全に保持する設計が自然に書ける。
// Java 17: var + record + テキストブロックで簡潔に
record Employee(int id, String name, String dept, int salary) {}
var sql = "SELECT id, name, dept, salary FROM employees ORDER BY id";
try (var stmt = conn.createStatement();
var rs = stmt.executeQuery(sql)) {
while (rs.next()) {
var emp = new Employee(
rs.getInt("id"), rs.getString("name"),
rs.getString("dept"), rs.getInt("salary"));
}
}Library Comparison
注意点
Statement に直接文字列連結で値を埋め込むと SQL インジェクションの原因になる。ユーザー入力を含む場合は必ず PreparedStatement を使うこと
Connection・Statement・ResultSet は try-with-resources で閉じる。finally で close() を呼ぶ旧式の書き方はリソースリークの温床になりやすい
DriverManager.getConnection の接続文字列はデータベース製品ごとに異なる。H2 のインメモリ DB はテスト用であり、本番では MySQL / PostgreSQL の URL に読み替えること
ResultSet のカラム取得で列名と型を間違えると実行時例外になる。getInt で VARCHAR 列を取ると ClassCastException 相当のエラーになるため、カラム定義と型メソッドの対応に注意する
FAQ
本番ではコネクションプーリング付きの DataSource が推奨です。DriverManager は学習やツール用途に向いています。
可読性と保守性の観点から列名指定(getString("name"))が安全です。列順の変更に影響されません。
対象 DB の JDBC ドライバを CLASSPATH に追加し、接続文字列・ユーザー・パスワードを変更するだけです。