Skip to content

Reject overlapping declared and generated key columns in SimpleJdbcInsert - #37014

Draft
junhyeong9812 wants to merge 1 commit into
spring-projects:mainfrom
junhyeong9812:fix/simplejdbcinsert-generated-key-mismatch
Draft

Reject overlapping declared and generated key columns in SimpleJdbcInsert#37014
junhyeong9812 wants to merge 1 commit into
spring-projects:mainfrom
junhyeong9812:fix/simplejdbcinsert-generated-key-mismatch

Conversation

@junhyeong9812

Copy link
Copy Markdown
Contributor

Overview

TableMetaDataContext.reconcileColumnsToUse() excludes generated key columns from the auto-discovered column list, but when columns are declared explicitly via SimpleJdbcInsert.usingColumns(...), a column also declared via usingGeneratedKeyColumns(...) 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 with usingGeneratedKeyColumns("id"):

protected List<String> reconcileColumnsToUse(List<String> declaredColumns, String[] generatedKeyNames) {
    if (generatedKeyNames.length > 0) {
        this.generatedKeyColumnsUsed = true;
    }
    if (!declaredColumns.isEmpty()) {
        return new ArrayList<>(declaredColumns);
    }
    ...
}

returns ["id", "name"] unfiltered when declaredColumns is non-empty, while the auto-discovery branch below it already filters out generated key names. createInsertString() independently filters generatedKeyNames when building the SQL, so the insert statement only gets a placeholder for name:

INSERT INTO customers (name) VALUES(?)

but matchInParameterValuesWithInsertColumns(...) and createInsertTypes() both iterate the unfiltered tableColumns (= 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:

protected List<String> reconcileColumnsToUse(List<String> declaredColumns, String[] generatedKeyNames) {
    if (generatedKeyNames.length > 0) {
        this.generatedKeyColumnsUsed = true;
    }
    Set<String> keys = CollectionUtils.newLinkedHashSet(generatedKeyNames.length);
    for (String key : generatedKeyNames) {
        keys.add(key.toUpperCase(Locale.ROOT));
    }
    if (!declaredColumns.isEmpty()) {
        List<String> columns = new ArrayList<>();
        for (String column : declaredColumns) {
            if (!keys.contains(column.toUpperCase(Locale.ROOT))) {
                columns.add(column);
            }
        }
        return columns;
    }
    ...
}

This makes tableColumns — the single source consumed by createInsertString(), createInsertTypes(), and matchInParameterValuesWithInsertColumns(...) — consistently exclude generated key columns regardless of whether they came from auto-discovery or an explicit usingColumns(...) 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(...) and usingGeneratedKeyColumns(...) — 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 a protected extension 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.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jul 7, 2026
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>
@junhyeong9812
junhyeong9812 force-pushed the fix/simplejdbcinsert-generated-key-mismatch branch from f47278b to 15d9e57 Compare July 7, 2026 23:24
@sbrannen sbrannen added the in: data Issues in data modules (jdbc, orm, oxm, tx) label Jul 9, 2026
@sbrannen sbrannen self-assigned this Sep 3, 2026
@sbrannen sbrannen added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Sep 3, 2026
@sbrannen sbrannen added this to the 7.1.0-M2 milestone Sep 3, 2026

@sbrannen sbrannen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(), throw InvalidDataAccessApiUsageException (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

@sbrannen sbrannen added the status: waiting-for-feedback We need additional information before we can continue label Sep 5, 2026
@sbrannen sbrannen changed the title Exclude declared generated key columns from SimpleJdbcInsert Reject overlapping declared and generated key columns in SimpleJdbcInsert Sep 5, 2026
@sbrannen
sbrannen marked this pull request as draft September 5, 2026 13:10
@sbrannen

sbrannen commented Sep 5, 2026

Copy link
Copy Markdown
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: data Issues in data modules (jdbc, orm, oxm, tx) status: waiting-for-feedback We need additional information before we can continue type: enhancement A general enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants