Reject overlapping declared and generated key columns in SimpleJdbcInsert - #37014
Conversation
TableMetaDataContext.reconcileColumnsToUse() filtered generated key columns out of the auto-discovered column list, but returned declared columns verbatim when usingColumns() was used together with usingGeneratedKeyColumns() for the same column. This left the insert SQL (which filters generated keys separately in createInsertString()) out of sync with the bound values and types arrays (which iterate the unfiltered declared column list), causing a parameter count mismatch at execution time. Signed-off-by: junhyeong9812 <pickjog@gmail.com>
f47278b to
15d9e57
Compare
sbrannen
left a comment
There was a problem hiding this comment.
Hi @junhyeong9812,
Thanks for discovering this and submitting a proposal to address it. 👍
After reviewing the proposal and considering the potential ramifications of the change in behavior, we've decided that we would actually prefer an explicit failure over silent exclusion here. When a column is declared via both usingColumns(...) and usingGeneratedKeyColumns(...), that's a configuration error, and we'd rather fail fast with a clear message than have the column silently disappear from the generated SQL.
An InvalidDataAccessApiUsageException should suffice, and it's already what AbstractJdbcInsert.compile() throws for other misconfigurations (such as a missing table name). So, this would be consistent with existing validation.
Could you please rework the PR along those lines?
- In
reconcileColumnsToUse(), throwInvalidDataAccessApiUsageException(naming the offending column(s)) when a declared column overlaps a generated-key column, instead of excluding it. - Update/replace the added tests to assert the exception (message included) rather than the exclusion behavior.
Once you've completed those tasks, please force-push to this branch, and I'll take another look.
Cheers,
Sam
SimpleJdbcInsert
|
Please note that I changed the title of this PR to match the new direction, and I've converted this to a draft PR in the interim. |
Overview
TableMetaDataContext.reconcileColumnsToUse()excludes generated key columns from the auto-discovered column list, but when columns are declared explicitly viaSimpleJdbcInsert.usingColumns(...), a column also declared viausingGeneratedKeyColumns(...)was not excluded from that declared list. This leaves the generated insert SQL out of sync with the values/types arrays bound to it, causing a parameter count mismatch at execution time whenever a column is declared both ways.Problem
usingColumns("id", "name")combined withusingGeneratedKeyColumns("id"):returns
["id", "name"]unfiltered whendeclaredColumnsis non-empty, while the auto-discovery branch below it already filters out generated key names.createInsertString()independently filtersgeneratedKeyNameswhen building the SQL, so the insert statement only gets a placeholder forname:but
matchInParameterValuesWithInsertColumns(...)andcreateInsertTypes()both iterate the unfilteredtableColumns(= the declared columns, size 2), producing values/types arrays of size 2. The placeholder count (1) and the bound value count (2) diverge, and the JDBC driver rejects execution with a parameter index error.Fix
Apply the same generated-key filter to the declared-columns branch that the auto-discovery branch already uses:
This makes
tableColumns— the single source consumed bycreateInsertString(),createInsertTypes(), andmatchInParameterValuesWithInsertColumns(...)— consistently exclude generated key columns regardless of whether they came from auto-discovery or an explicitusingColumns(...)declaration. Added regression tests covering the partial-overlap, full-overlap (declared columns consist entirely of generated keys), and case-insensitive overlap cases.Note on impact
This changes behavior for the specific combination of declaring a column via both
usingColumns(...)andusingGeneratedKeyColumns(...)— previously that combination always failed at execution time, so no working code should depend on the old behavior. That said, it is a behavior change to aprotectedextension point (reconcileColumnsToUse), so I want to flag it explicitly in case there's a reason to prefer a different approach (e.g. rejecting the overlap explicitly instead of silently excluding it) — happy to adjust if so.