JavaアプリケーションにおけるMySQL接続管理の実装パターン

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);
        }
    }
}

タグ: JDBC接続管理 SQLパラメータ化 トランザクション制御 コネクションプール データマッピング

7月23日 19:05 投稿