
【JSPウェブアプリ】JDBC – 課題30問
2025-08-05初級問題 (9問)
- JDBCとは何か、その主要なコンポーネントを説明してください。
- MySQLに接続するためのJDBCドライバーをロードするコードを書いてください。
- JDBCを使用してMySQLデータベースへの接続を確立する方法を説明してください。
- Statementオブジェクトを使用して簡単なSELECTクエリを実行するコードを示してください。
- ResultSetからデータを取得する基本的な方法を説明してください。
- JDBCリソース(Connection, Statement, ResultSet)を適切にクローズする方法を説明してください。
- PreparedStatementの利点を3つ挙げてください。
- 基本的なDAO(Data Access Object)パターンのクラス図を説明してください。
- JDBCで発生する可能性のある主な例外を3つ挙げてください。
中級問題 (15問)
- PreparedStatementを使用してパラメータ化クエリを実行する方法を説明してください。
- コネクションプールとは何か、その利点を説明してください。
- HikariCPを使用してコネクションプールを設定する方法を説明してください。
- JDBCトランザクションを手動で管理する方法を説明してください。
- バッチ処理を使用して複数のINSERTを効率的に実行する方法を説明してください。
- ResultSetのデータをJavaオブジェクトにマッピングする方法を説明してください。
- DAOパターンを使用してユーザーデータを取得するインターフェースと実装例を示してください。
- トランザクション分離レベルを設定する方法と、各レベルの違いを簡単に説明してください。
- ストアドプロシージャをJDBCから呼び出す方法を説明してください。
- データベースメタデータを取得する方法を説明してください。
- ロールバックの適切なタイミングについて説明してください。
- 複数のテーブルにまたがるトランザクション処理の例を示してください。
- データアクセス層での例外処理のベストプラクティスを説明してください。
- コネクションプールの設定パラメータで重要なものを3つ挙げ、その意味を説明してください。
- DAOパターンでジェネリクスを使用する利点と実装例を示してください。
上級問題 (6問)
- スキーマ移行ツール(FlywayやLiquibase)とJDBCを組み合わせるメリットを説明してください。
- コネクションプールの監視とメトリクス収集方法を説明してください。
- 分散トランザクションをJDBCで扱う際の考慮点を説明してください。
- 大量データを効率的に処理するためのJDBCの最適化手法を3つ説明してください。
- DAOパターンとリポジトリパターンの違いを説明し、それぞれの適切な使用場面を述べてください。
- モダンなJavaアプリケーションにおけるJDBCの位置付けと、Spring JDBCなどのライブラリを使用する利点を説明してください。
解答例
初級問題解答
- JDBCの基本
JDBC(Java Database Connectivity)は、Javaアプリケーションがデータベースと接続するためのAPIです。
主要コンポーネント:
- DriverManager: データベース接続を管理
- Connection: データベース接続を表す
- Statement/PreparedStatement: SQL文を実行
- ResultSet: クエリ結果を保持
- JDBCドライバーロード
Class.forName("com.mysql.cj.jdbc.Driver");
- データベース接続
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
String user = "username";
String password = "password";
Connection connection = DriverManager.getConnection(url, user, password);
- SELECTクエリ実行
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
- ResultSetからのデータ取得
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
// 処理...
}
- リソースクローズ
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
// 処理...
} // 自動的にクローズ
- PreparedStatementの利点
- SQLインジェクション防止
- パフォーマンス向上(プリコンパイル)
- コードの可読性向上
- DAOパターンクラス図
[User] -- [UserDAO] <|-- [UserDAOImpl]
User: エンティティクラス
UserDAO: データアクセスインターフェース
UserDAOImpl: JDBC実装クラス
- JDBC例外
- SQLException: 一般的なJDBC例外
- SQLTimeoutException: クエリタイムアウト
- BatchUpdateException: バッチ更新失敗
中級問題解答
- PreparedStatement使用例
String sql = "SELECT * FROM users WHERE age > ? AND status = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setInt(1, 18);
pstmt.setString(2, "active");
ResultSet rs = pstmt.executeQuery();
// 結果処理...
}
- コネクションプール
コネクションプールは、データベース接続を事前に作成してプールしておき、必要時に貸し出す仕組みです。 利点: – 接続作成オーバーヘッド削減 – リソース使用効率向上 – 接続数の制御可能
- HikariCP設定
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("user");
config.setPassword("password");
config.setMaximumPoolSize(10);
config.setMinimumIdle(5);
HikariDataSource dataSource = new HikariDataSource(config);
- トランザクション管理
connection.setAutoCommit(false);
try {
// トランザクション処理...
connection.commit();
} catch (SQLException e) {
connection.rollback();
} finally {
connection.setAutoCommit(true);
}
- バッチ処理
try (PreparedStatement pstmt = connection.prepareStatement("INSERT INTO users (name) VALUES (?)")) {
for (String name : names) {
pstmt.setString(1, name);
pstmt.addBatch();
}
int[] results = pstmt.executeBatch();
}
- ResultSetマッピング
List users = new ArrayList<>();
while (resultSet.next()) {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setName(resultSet.getString("name"));
users.add(user);
}
return users;
- DAOパターン例
// インターフェース
public interface UserDao {
User findById(int id) throws SQLException;
}
// 実装
public class UserDaoImpl implements UserDao {
private final DataSource dataSource;
public User findById(int id) throws SQLException {
// JDBC実装...
}
}
- トランザクション分離レベル
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
レベル:
- READ_UNCOMMITTED: ダーティーリード可能
- READ_COMMITTED: ダーティーリード防止
- REPEATABLE_READ: 非リピータブルリード防止
- SERIALIZABLE: 完全分離
- ストアドプロシージャ呼び出し
try (CallableStatement cstmt = connection.prepareCall("{call get_user_by_id(?)}")) {
cstmt.setInt(1, userId);
ResultSet rs = cstmt.executeQuery();
// 結果処理...
}
- メタデータ取得
DatabaseMetaData metaData = connection.getMetaData();
ResultSet tables = metaData.getTables(null, null, "%", new String[]{"TABLE"});
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
// 処理...
}
- ロールバックタイミング
ロールバックは以下の場合に実行: – SQL例外発生時 – ビジネスロジックエラー時 – トランザクションタイムアウト時 – データ整合性違反検出時
- 複数テーブルトランザクション
try {
connection.setAutoCommit(false);
// テーブルA更新
updateTableA(connection, dataA);
// テーブルB更新
updateTableB(connection, dataB);
connection.commit();
} catch (SQLException e) {
connection.rollback();
throw e;
}
- 例外処理ベストプラクティス
- 低レベルなSQLExceptionをアプリケーション固有例外でラップ
- リソースリーク防止のためtry-with-resources使用
- エラーコードより例外タイプで処理分岐
- ユーザーにわかりやすいメッセージ提供
- コネクションプール設定
- maximumPoolSize: 最大接続数(デフォルト10)
- connectionTimeout: 接続取得タイムアウト(デフォルト30秒)
- idleTimeout: アイドル接続の最大保持時間
- ジェネリックDAO例
public interface GenericDao {
T findById(ID id) throws DataAccessException;
List findAll() throws DataAccessException;
ID save(T entity) throws DataAccessException;
}
public class JdbcGenericDao implements GenericDao {
// ジェネリックな実装...
}
上級問題解答
- スキーマ移行ツール
メリット: – データベーススキーマのバージョン管理可能 – チーム全体で一貫したスキーマを共有 – 変更スクリプトの自動適用 – ロールバック機能 – JDBCと連携したデータ初期化可能
// 例: Flywayの基本的な使用法
Flyway flyway = Flyway.configure()
.dataSource(dataSource)
.load();
flyway.migrate();
- コネクションプール監視
監視方法: 1. JMX経由でメトリクス取得 2. HikariCPのgetPoolStats()使用 3. 監視ダッシュボードと連携(Prometheusなど) 重要なメトリクス: – アクティブ接続数 – アイドル接続数 – 接続待機数 – 接続取得時間
- 分散トランザクション
考慮点:
1. XAプロトコルを使用した2相コミット
2. トランザクションマネージャー(Atomikosなど)の導入
3. パフォーマンスへの影響
4. デッドロックリスク増加
5. リカバリプロセスの複雑化
// 実装例
XADataSource xaDataSource = new MysqlXADataSource();
// 設定...
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
dataSource.setXaDataSource(xaDataSource);
e = new MysqlXADataSource(); // 設定... AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean(); dataSource.setXaDataSource(xaDataSource);
- 大量データ処理最適化
// 1. フェッチサイズ設定:
statement.setFetchSize(1000);
// 2. バッチ処理の活用:
addBatch()とexecuteBatch()
// 3. ストリーミング処理:
statement.setFetchSize(Integer.MIN_VALUE);
ResultSet.TYPE_FORWARD_ONLY
// 4. カーソルを使用したページネーション
// 5. 必要な列のみ選択
- DAO vs リポジトリ
DAOパターン: – データストアへの低レベルアクセス – テーブル中心の設計 – CRUD操作に特化 – 単一データソース向け リポジトリパターン: – ドメイン中心の設計 – 集約ルートを扱う – 複雑なクエリをカプセル化 – 複数データソース統合可能 使用場面: – 単純なCRUD: DAO – 複雑なドメインロジック: リポジトリ
- モダンなJDBC
JDBCの位置付け:
– 低レベルなデータアクセス基盤
– ORMやマイクロサービス基盤の下層
Spring JDBCの利点:
1. ボイラープレートコード削減(JdbcTemplate)
2. 例外階層の体系化
3. 宣言的トランザクション管理
4. リソース管理の自動化
5. テスト容易性向上
jdbcTemplate.query(
"SELECT * FROM users WHERE age > ?",
new Object[]{18},
(rs, rowNum) -> new User(rs.getInt("id"), rs.getString("name"))
);