Skip to content

Conversation

belljun3395
Copy link

@belljun3395 belljun3395 commented Sep 4, 2025

Optimize parameter matching in TableMetaDataContext

Problem

The TableMetaDataContext.matchInParameterValuesWithInsertColumns() method uses a nested loop for case-insensitive parameter matching, resulting in O(n×m) complexity where n = columns and m = parameters.

For each column, when exact matching fails, it iterates through all parameter entries to find case-insensitive matches:

for (String column : this.tableColumns) {                    // O(n)
    Object value = inParameters.get(column);
    if (value == null) {
        // ... lowercase lookup
        if (value == null) {
            for (Map.Entry<String, ?> entry : inParameters.entrySet()) {  // O(m)
                if (column.equalsIgnoreCase(entry.getKey())) {
                    value = entry.getValue();
                    break;
                }
            }
        }
    }
    values.add(value);
}

This becomes expensive with many columns and parameters (e.g., 100 columns × 1000 parameters = 100K iterations).

Solution

Pre-compute a case-insensitive lookup map, reducing complexity from O(n×m) to O(n+m):

// Build lookup map once - O(m)
Map<String, Object> caseInsensitiveLookup = new HashMap<>(inParameters.size());
for (Map.Entry<String, ?> entry : inParameters.entrySet()) {
    caseInsensitiveLookup.put(entry.getKey().toLowerCase(Locale.ROOT), entry.getValue());
}

for (String column : this.tableColumns) {  // O(n)
    Object value = inParameters.get(column);
    if (value == null) {
        value = inParameters.get(column.toLowerCase(Locale.ROOT));
        if (value == null) {
            value = caseInsensitiveLookup.get(column.toLowerCase(Locale.ROOT)); // O(1)
        }
    }
    values.add(value);
}

Performance Impact

  • Before: O(n×m) - quadratic growth
  • After: O(n+m) - linear growth
  • Applications affected: SimpleJdbcInsert users with many columns/parameters

Testing

  • ✅ All existing tests pass (no behavior change)
  • ✅ Verified case-sensitive/insensitive matching logic preserved

Replace nested loop with pre-computed case-insensitive lookup map,
reducing time complexity from O(n×m) to O(n+m) where n = number
of columns and m = number of parameters.

Before: Iterates through all parameter entries for each column
when case-insensitive matching is needed.

After: Build HashMap once for O(1) case-insensitive lookups.

Signed-off-by: belljun3395 <[email protected]>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Sep 4, 2025
@sbrannen sbrannen added the in: data Issues in data modules (jdbc, orm, oxm, tx) label Sep 6, 2025
List<Object> values = new ArrayList<>(inParameters.size());
List<Object> values = new ArrayList<>(this.tableColumns.size());

Map<String, Object> caseInsensitiveLookup = new HashMap<>(inParameters.size());
Copy link
Contributor

Choose a reason for hiding this comment

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

Spring itself provides a LinkedCaseInsensitiveMap which provides this functionality already. Wouldn't it be easier to use that, it seems to be used in other places in the JDBC access already.

Copy link
Author

Choose a reason for hiding this comment

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

I've updated the implementation to use LinkedCaseInsensitiveMap instead of manually creating a case-insensitive lookup map. The changes are available in commit cdb149b

break;
}
}
value = caseInsensitiveLookup.get(column.toLowerCase(Locale.ROOT));
Copy link
Contributor

Choose a reason for hiding this comment

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

When using the LinkedCaseInsensitiveMap you can just do a regular get without the need to convert to lowercase (the map will take care of this). Which would simplify this code.

Copy link
Author

@belljun3395 belljun3395 left a comment

Choose a reason for hiding this comment

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

Thanks for highlighting the existing implementation examples I overlooked, and I appreciate the review!

List<Object> values = new ArrayList<>(inParameters.size());
List<Object> values = new ArrayList<>(this.tableColumns.size());

Map<String, Object> caseInsensitiveLookup = new HashMap<>(inParameters.size());
Copy link
Author

Choose a reason for hiding this comment

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

I've updated the implementation to use LinkedCaseInsensitiveMap instead of manually creating a case-insensitive lookup map. The changes are available in commit cdb149b

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-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants