JDBCとデータベース接続プールの活用

JDBCとは

JDBC(Java Database Connectivity)は、Java言語でリレーショナルデータベースを操作するためのAPIセットです。JDBCは、MySQLやPostgreSQLなど、異なるデータベース間で共通のインターフェースを提供し、Javaアプリケーションからデータベースを操作する手段を統一しています。

このAPIはインターフェースで構成されており、各データベースベンダーが実装を提供します。これらの実装は通常、JDBCドライバとして知られるJARファイルに含まれています。

JDBCの基本的な使い方

以下はJDBCを使用してデータベースに接続し、SQLを実行する基本的な手順です。

// JDBCドライバをロード
Class.forName("com.mysql.cj.jdbc.Driver");

// データベース接続
String url = "jdbc:mysql://localhost:3306/sample_db";
String user = "root";
String password = "password";
Connection conn = DriverManager.getConnection(url, user, password);

// SQLステートメントを作成
Statement stmt = conn.createStatement();
String sql = "UPDATE accounts SET balance = 5000 WHERE id = 1";
int rowsAffected = stmt.executeUpdate(sql);

// 結果を出力
System.out.println(rowsAffected + "件更新しました");

// リソースを解放
stmt.close();
conn.close();

JDBC APIの主要コンポーネント

DriverManager

DriverManagerは、JDBCドライバを管理し、データベース接続を確立する役割を持っています。

  • registerDriver():JDBCドライバを登録
  • getConnection():データベース接続を取得

MySQL 5以降では、JDBCドライバクラスを明示的にロードする必要がなくなりました。JDBCドライバのJARファイル内に、META-INF/services/java.sql.Driverというファイルがあり、そこにドライバクラス名が記載されています。

Connection

Connectionインターフェースは、データベースとの接続を表します。

  • createStatement():SQLを実行するためのStatementオブジェクトを生成
  • prepareStatement():SQLインジェクション対策に有効なPreparedStatementを生成
  • setAutoCommit():自動コミットを無効化し、トランザクション制御を行う
  • commit():トランザクションをコミット
  • rollback():トランザクションをロールバック

トランザクション処理の例:

try {
    connection.setAutoCommit(false); // トランザクション開始
    Statement stmt = connection.createStatement();
    stmt.executeUpdate("UPDATE accounts SET balance = 4000 WHERE id = 1");
    stmt.executeUpdate("UPDATE accounts SET balance = 6000 WHERE id = 2");
    connection.commit(); // コミット
} catch (SQLException e) {
    connection.rollback(); // エラー時のロールバック
    e.printStackTrace();
}

Statement

SQL文を実行するためのインターフェースです。

  • executeUpdate():INSERT、UPDATE、DELETEなどのDML文を実行
  • executeQuery():SELECTなどのDQL文を実行し、ResultSetを返す

ResultSet

SELECT文の実行結果を格納するオブジェクトです。

ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
    int id = rs.getInt("id");
    String name = rs.getString("name");
    System.out.println("ID: " + id + ", 名前: " + name);
}

PreparedStatement

SQLインジェクションを防ぐために、SQL文のプレースホルダに値を安全に設定できるインターフェースです。

String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();

プレースホルダ(?)に値を設定する際には、setInt()setString()などのメソッドを使用します。

データベース接続プールの概要

データベース接続プールは、アプリケーションがデータベースに頻繁に接続する場合に、接続の再利用を可能にする仕組みです。これにより、接続コストを削減し、パフォーマンスを向上させます。

標準インターフェース DataSource

JDBCでは、接続プールを管理するための標準インターフェースとしてDataSourceが定義されています。

  • getConnection():プールから接続を取得

代表的な接続プール実装

  • Apache DBCP
  • C3P0
  • Alibaba Druid

Druidの使い方

Druidは、パフォーマンスと監視機能に優れた接続プール実装です。

設定ファイル(druid.properties):

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/sample_db
username=root
password=password
initialSize=5
maxActive=20
maxWait=3000

コード例:

Properties props = new Properties();
props.load(new FileInputStream("druid.properties"));
DataSource ds = DruidDataSourceFactory.createDataSource(props);
Connection conn = ds.getConnection();
System.out.println(conn);

CRUD操作の実装例

以下は、ブランド情報を管理するテーブルtb_brandに対する基本的なCRUD操作の実装例です。

全件取得

public List<Brand> selectAll() throws Exception {
    Properties props = new Properties();
    props.load(new FileInputStream("druid.properties"));
    DataSource ds = DruidDataSourceFactory.createDataSource(props);
    Connection conn = ds.getConnection();
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT * FROM tb_brand");

    List<Brand> brands = new ArrayList<>();
    while (rs.next()) {
        Brand brand = new Brand();
        brand.setId(rs.getInt("id"));
        brand.setName(rs.getString("brand_name"));
        brand.setCompany(rs.getString("company_name"));
        brand.setOrder(rs.getInt("ordered"));
        brand.setDescription(rs.getString("description"));
        brand.setStatus(rs.getInt("status"));
        brands.add(brand);
    }

    rs.close();
    stmt.close();
    conn.close();
    return brands;
}

データ追加

public boolean insert(Brand brand) throws Exception {
    Properties props = new Properties();
    props.load(new FileInputStream("druid.properties"));
    DataSource ds = DruidDataSourceFactory.createDataSource(props);
    Connection conn = ds.getConnection();
    PreparedStatement pstmt = conn.prepareStatement(
        "INSERT INTO tb_brand (brand_name, company_name, ordered, description, status) VALUES (?, ?, ?, ?, ?)"
    );
    pstmt.setString(1, brand.getName());
    pstmt.setString(2, brand.getCompany());
    pstmt.setInt(3, brand.getOrder());
    pstmt.setString(4, brand.getDescription());
    pstmt.setInt(5, brand.getStatus());

    int count = pstmt.executeUpdate();
    pstmt.close();
    conn.close();
    return count > 0;
}

データ更新

public boolean update(Brand brand) throws Exception {
    Properties props = new Properties();
    props.load(new FileInputStream("druid.properties"));
    DataSource ds = DruidDataSourceFactory.createDataSource(props);
    Connection conn = ds.getConnection();
    PreparedStatement pstmt = conn.prepareStatement(
        "UPDATE tb_brand SET brand_name = ?, company_name = ?, ordered = ?, description = ?, status = ? WHERE id = ?"
    );
    pstmt.setString(1, brand.getName());
    pstmt.setString(2, brand.getCompany());
    pstmt.setInt(3, brand.getOrder());
    pstmt.setString(4, brand.getDescription());
    pstmt.setInt(5, brand.getStatus());
    pstmt.setInt(6, brand.getId());

    int count = pstmt.executeUpdate();
    pstmt.close();
    conn.close();
    return count > 0;
}

データ削除

public boolean delete(int id) throws Exception {
    Properties props = new Properties();
    props.load(new FileInputStream("druid.properties"));
    DataSource ds = DruidDataSourceFactory.createDataSource(props);
    Connection conn = ds.getConnection();
    PreparedStatement pstmt = conn.prepareStatement("DELETE FROM tb_brand WHERE id = ?");
    pstmt.setInt(1, id);

    int count = pstmt.executeUpdate();
    pstmt.close();
    conn.close();
    return count > 0;
}

タグ: JDBC PreparedStatement データベース接続プール Druid SQLインジェクション対策

5月15日 13:19 投稿