Add jdbc-vs-jpa module: Spring Data JDBC vs JPA on a shared Order aggregate
Same Customer/Order/OrderItem domain modelled with Hibernate/Spring Data JPA and Spring Data JDBC side by side, both running through a shared StatementLoggingDataSource so SQL-statement counts are directly comparable. 11 tests, 11 captured transcripts, 6 doc chapters. Companion repo for the ankurm.com article on when to drop the ORM.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user