# 03 — The SQL log [← 02 the JPA side](02-the-jpa-side.md) · [next: the JDBC side →](04-the-jdbc-side.md) Source: [`support/StatementLoggingDataSource.java`](../src/main/java/com/ankurm/jdbcvsjpa/support/StatementLoggingDataSource.java), [`support/SqlLog.java`](../src/main/java/com/ankurm/jdbcvsjpa/support/SqlLog.java). Endpoint: [`DiagController.java`](../src/main/java/com/ankurm/jdbcvsjpa/DiagController.java) at `/diag/sql-log`. Every statement-count claim in this article comes from one mechanism, not from reading Hibernate's `show_sql` output in one format and Spring Data JDBC's `JdbcTemplate` logging in a different one. `StatementLoggingDataSource` wraps the single H2 `DataSource` both stacks share: it hands out a JDK dynamic proxy for every `Connection`, which in turn hands out a proxy for every `Statement`/`PreparedStatement`, and any method starting with `execute` gets its SQL text recorded to `SqlLog` before the call is delegated. This is deliberately *below* both ORMs — it counts what actually reached the database driver, not what each framework's own debug logging chose to print. ```java @Bean public DataSource dataSource(SqlLog sqlLog) { HikariDataSource real = new HikariDataSource(); real.setJdbcUrl("jdbc:h2:mem:jdbcvsjpa;DB_CLOSE_DELAY=-1;MODE=LEGACY"); // ... return new StatementLoggingDataSource(real, sqlLog); } ``` Two things this caught that a naive count would have missed: - **JDBC batching.** Saving three `OrderItem` rows as part of one aggregate save (see [07](output/07-delete-then-insert.txt)) shows up as *one* `INSERT` line, not three — Spring Data JDBC calls `addBatch()` three times and `executeBatch()` once. Counting SQL text seen by `Statement.executeQuery`/`executeUpdate` would have reported 3 inserts; counting actual `execute*` invocations on the proxy correctly reports 1, because that is the true number of round trips to the database. - **Consistent counting across two completely different SQL-generation paths.** Hibernate's HQL compiler and Spring Data JDBC's `JdbcTemplate`-based query building produce differently formatted SQL for the same logical operation (see the JPA transcripts' lower-case, unquoted style versus the JDBC transcripts' upper-case, quoted style — both are the frameworks' own defaults, untouched). A statement counter that lived inside either framework would only ever see its own side; this one sees both, so [00 — the comparison table](output/00-statement-count-comparison.txt) is a fair, apples-to-apples number. `/diag/sql-log` exposes the same log at runtime for manual exploration — hit an endpoint, then `curl localhost:8080/diag/sql-log` to see exactly what ran. Delete this controller before shipping; it has no business existing outside a demo. Continue to [04 — the JDBC side](04-the-jdbc-side.md).