JdbcExecutor.javaAbstract JDBC operation executor using the Template Method pattern. 95 lines. This is the foundation for all raw JDBC operations in ProjectForge — DatabaseExecutorImpl creates anonymous subclasses for each SQL call. This class owns the connection lifecycle while letting subclasses define what happens with the statement.
Template Method — execute(String sql, boolean ignoreErrors, Object... args):
dataSource.getConnection() — gets a raw JDBC connection from the pool (or directly in test mode)con.prepareStatement(sql) — creates a PreparedStatementargs[] and calls stmt.setObject(i+1, args[i]) — note the i+1 conversion: JDBC parameters are 1-based (JDBC spec), while Java arrays are 0-based. This is a common source of bugs if forgottenexecute(stmt) — the abstract method overridden by subclasses. The return value is typed as Object because subclasses return different types: null for DDL, List<DatabaseResultRow> for queries, Integer for updates/countsSQLException is caught and ignoreErrors=true, logs the error at ERROR level and returns null. If ignoreErrors=false, wraps in RuntimeException and rethrows — this is an unchecked exception design choice; callers don't need try-catch for SQL errorsResource cleanup (finally block): Closes PreparedStatement first, then Connection. Each close is in its own try-catch — if closing the statement throws, the connection is still closed. Critical nuance: if either close throws an exception, a static boolean hasErrors flag is set. After both closes, if any errors occurred, a RuntimeException is thrown. This ensures resource leak errors are not silently swallowed — the caller knows resource cleanup failed.
Abstract method — execute(PreparedStatement stmt): Implemented by anonymous subclasses in DatabaseExecutorImpl for 4 different operations: DDL execution (stmt.execute()), query (stmt.executeQuery() + ResultSet mapping), scalar query (rs.getInt(1)), and update (stmt.executeUpdate()).
Missing connection pooling: The Javadoc warns "connections may loose" — this class gets a Connection via DataSource.getConnection() and closes it in finally. This is correct for pooled DataSources (the close returns the connection to the pool), but the author's concern was about direct DriverManager connections in test mode where each call opens a new TCP connection. In production with HikariCP, the pooling works fine — the warning is overly cautious.