Java環境でのMySQLデータベース接続実装において、効率的で保守性の高い設計パターンを解説します。特にJDBCを活用したコネクション管理とSQL実行の最適化に焦点を当てます。
接続設定の動的管理
データベース接続情報はハードコードせず、外部設定ファイルで管理します。リソースディレクトリに配置するdatabase-config.propertiesファイルの例:
db.url=jdbc:mysql://localhost:3306/corporate_db?useSSL=false&serverTimezone=UTC
db.user=admin
db.pass=secure_password
db.pool.size=10
設定値のロード処理:
public final class ConfigLoader {
private static final Properties config = new Properties();
static {
try (InputStream input = ConfigLoader.class
.getClassLoader()
.getResourceAsStream("database-config.properties")) {
if (input == null) {
throw new IllegalStateException("設定ファイルが見つかりません");
}
config.load(input);
} catch (IOException e) {
throw new RuntimeException("設定読み込みエラー", e);
}
}
public static String get(String key) {
return config.getProperty(key);
}
}
コネクションプールの実装
頻繁なコネクション確立を避けるため、プール化された接続を管理するユーティリティクラス:
public class ConnectionPool {
private static final BlockingQueue<Connection> connections = new LinkedBlockingQueue<>();
private static final String URL = ConfigLoader.get("db.url");
private static final String USER = ConfigLoader.get("db.user");
private static final String PASS = ConfigLoader.get("db.pass");
static {
int poolSize = Integer.parseInt(ConfigLoader.get("db.pool.size"));
for (int i = 0; i < poolSize; i++) {
connections.add(createNewConnection());
}
}
private static Connection createNewConnection() {
try {
return DriverManager.getConnection(URL, USER, PASS);
} catch (SQLException e) {
throw new RuntimeException("データベース接続失敗", e);
}
}
public static Connection acquire() throws InterruptedException {
return connections.take();
}
public static void release(Connection conn) {
if (conn != null) {
connections.offer(conn);
}
}
}
SQL実行の抽象化
CRUD操作を安全に実行するための汎用メソッド群:
public class DatabaseExecutor {
public static <T> List<T> query(String sql,
Function<ResultSet, T> mapper) {
try (Connection conn = ConnectionPool.acquire();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
List<T> results = new ArrayList<>();
while (rs.next()) {
results.add(mapper.apply(rs));
}
return results;
} catch (SQLException | InterruptedException e) {
throw new DataAccessException("クエリ実行エラー", e);
}
}
public static int executeUpdate(String sql,
Consumer<PreparedStatement> paramSetter) {
try (Connection conn = ConnectionPool.acquire();
PreparedStatement stmt = conn.prepareStatement(sql)) {
paramSetter.accept(stmt);
return stmt.executeUpdate();
} catch (SQLException | InterruptedException e) {
throw new DataAccessException("更新操作失敗", e);
}
}
}
実装例:部門データの取得
型安全なデータマッピングの実装パターン:
public class DepartmentRepository {
private static final String FETCH_SQL =
"SELECT dept_id, dept_name, description FROM departments WHERE dept_name LIKE ?";
public List<Department> searchByName(String namePattern) {
return DatabaseExecutor.query(FETCH_SQL, rs -> {
try {
return new Department(
rs.getInt("dept_id"),
rs.getString("dept_name"),
rs.getString("description")
);
} catch (SQLException e) {
throw new DataAccessException("データ変換エラー", e);
}
}, stmt -> {
try {
stmt.setString(1, "%" + namePattern + "%");
} catch (SQLException e) {
throw new DataAccessException("パラメータ設定エラー", e);
}
});
}
}
トランザクション制御
複数操作をアトミックに実行するためのパターン:
public void transferDepartment(int sourceId, int targetId) {
Connection conn = null;
try {
conn = ConnectionPool.acquire();
conn.setAutoCommit(false);
// 更新処理
String updateSql = "UPDATE employees SET dept_id = ? WHERE dept_id = ?";
try (PreparedStatement stmt = conn.prepareStatement(updateSql)) {
stmt.setInt(1, targetId);
stmt.setInt(2, sourceId);
stmt.executeUpdate();
}
// 削除処理
String deleteSql = "DELETE FROM departments WHERE dept_id = ?";
try (PreparedStatement stmt = conn.prepareStatement(deleteSql)) {
stmt.setInt(1, sourceId);
stmt.executeUpdate();
}
conn.commit();
} catch (SQLException | InterruptedException e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
throw new DataAccessException("ロールバック失敗", ex);
}
}
throw new DataAccessException("トランザクション処理失敗", e);
} finally {
if (conn != null) {
ConnectionPool.release(conn);
}
}
}