JDBC(Java Database Connectivity)は、Javaアプリケーションがリレーショナルデータベースと連携するための標準APIです。各データベースベンダーが独自のドライバーを提供し、JDBC仕様に準拠して実装します。
主な利点
- 統一されたインターフェースで、異なるDBへの対応が容易
- 学習コストが低く、初学者にも扱いやすい
- パフォーマンスが高く、大規模データ処理にも対応可能
基本的な接続手順
- ドライバークラスを動的にロード
- データベースとの物理接続を確立
- SQL実行用オブジェクトを生成
- 結果セットを走査してデータ取得
- リソースを明示的に解放
Mavenプロジェクトでは、依存関係としてドライバーを追加するのが一般的です。手動でJARをlibフォルダに配置する場合は、ビルドパスに含める必要があります。
接続コード例
public class DatabaseConnector {
public static void main(String[] args) {
try (Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:5001/company?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai",
"admin", "password123")) {
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, name, dept FROM staff WHERE dept = '開発部'")) {
while (rs.next()) {
System.out.printf("%s - %d - %s%n",
rs.getString("dept"),
rs.getInt("id"),
rs.getString("name"));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
再利用可能なユーティリティクラス
public class ConnectionHelper {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:5001/company?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
throw new RuntimeException("ドライバーの読み込みに失敗しました", e);
}
}
public static Connection establishConnection() throws SQLException {
return DriverManager.getConnection(URL, "admin", "password123");
}
public static void releaseResources(ResultSet rs, Statement stmt, Connection conn) {
if (rs != null) try { rs.close(); } catch (SQLException ignored) {}
if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
if (conn != null) try { conn.close(); } catch (SQLException ignored) {}
}
}
SQLインジェクション対策
ユーザー入力をそのままSQL文に埋め込むと、悪意のあるSQLコードが実行される危険があります。これを防ぐにはPreparedStatementを使用し、パラメータバインディングを行います。
トランザクション制御
複数の操作を論理的な単位として扱うには、手動コミットモードを有効にします。失敗時はrollback()で状態を復元できます。
try (Connection conn = ConnectionHelper.establishConnection()) {
conn.setAutoCommit(false);
String sql = "INSERT INTO staff (id, name, salary, dept) VALUES (?, ?, ?, ?)";
for (int i = 5001; i <= 6000; i++) {
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, i);
pstmt.setString(2, "スタッフ" + i);
pstmt.setFloat(3, 4500.0f);
pstmt.setString(4, "営業部");
pstmt.executeUpdate();
}
}
conn.commit();
} catch (Exception e) {
if (conn != null && !conn.isClosed()) {
conn.rollback();
}
throw e;
}
エンティティマッピングとページネーション
検索結果をJavaオブジェクトに変換することで、型安全な操作が可能になります。LIMIT句とオフセット計算でページ分割も容易です。
public class Staff {
private Integer id;
private String name;
private Float salary;
private String department;
private java.sql.Date joinDate;
// getter/setter省略
}
// ページ取得処理
String query = "SELECT * FROM staff LIMIT ?, 20";
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
pstmt.setInt(1, (pageNumber - 1) * 20);
ResultSet rs = pstmt.executeQuery();
List<Staff> page = new ArrayList<>();
while (rs.next()) {
Staff s = new Staff();
s.setId(rs.getInt("id"));
s.setName(rs.getString("name"));
s.setSalary(rs.getFloat("salary"));
s.setDepartment(rs.getString("dept"));
s.setJoinDate(rs.getDate("join_date"));
page.add(s);
}
}
日付型の取り扱い
文字列形式の日付をデータベースに格納する際は、java.util.Date → java.sql.Dateへの変換が必要です。
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date parsed = parser.parse(inputString);
java.sql.Date sqlDate = new java.sql.Date(parsed.getTime());
pstmt.setDate(5, sqlDate); // PreparedStatementに設定
バッチ処理による性能向上
大量のINSERT/UPDATEを個別に実行するとオーバーヘッドが大きくなります。addBatch()とexecuteBatch()を組み合わせることで、ネットワークラウンドトリップを削減できます。
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
conn.setAutoCommit(false);
for (int i = 10000; i < 20000; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, "バッチ" + i);
pstmt.setFloat(3, 3800.0f);
pstmt.setString(4, "物流部");
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
}
コネクションプールの導入
DruidやHikariCPなどのプール管理ライブラリを使うことで、接続の再利用と負荷分散が可能になります。初期接続数と最大接続数は、アプリケーションの同時アクセス数に基づいて調整します。
Apache Commons DBUtilsの活用
定型的なJDBCコードを簡略化するためのユーティリティです。Beanマッピングや自動リソース管理機能により、生産性が向上します。
QueryRunner runner = new QueryRunner(dataSource);
List<Staff> results = runner.query(
"SELECT * FROM staff WHERE dept = ?",
new BeanListHandler<>(Staff.class),
"人事部"
);