Summary
TableMetaDataProviderFactory
in Spring Framework 6.1.x (used by Spring Boot 3.2.0) omits a dedicated branch for MySQL and MariaDB. As a result, all MySQL-based connections fall into GenericTableMetaDataProvider
, which incorrectly reports generatedKeysColumnNameArraySupported = true
despite MySQL only returning the last key for multi-row INSERTs. This leads to broken key retrieval in SimpleJdbcInsert
and related APIs.
Steps to Reproduce
1. Configure a Spring Boot 3.2.0 application with a MySQL datasource.
2. Use SimpleJdbcInsert
or JdbcTemplate
with Statement.RETURN_GENERATED_KEYS
on a multi-row insert:
java
jdbcTemplate.update(
"INSERT INTO test (name) VALUES (?), (?), (?)",
ps -> {
ps.setString(1, "A");
ps.setString(2, "B");
ps.setString(3, "C");
}
);
ResultSet rs = ps.getGeneratedKeys();
// rs only contains the last key, but Spring thinks multi-key arrays are supported
3. Observe that Spring’s generic provider sets supportsGetGeneratedKeysColumnNameArray=true
, leading code paths that expect full key arrays to misbehave.
Expected Behavior
- TableMetaDataProviderFactory
should detect "MySQL"
and "MariaDB"
(via DatabaseMetaData.getDatabaseProductName()
) and return MySqlTableMetaDataProvider
.
- MySqlTableMetaDataProvider
must inherit the correct generatedKeysColumnNameArraySupported=false
behavior as defined in Connector/J performance extensions.
Proposed Fix
In
public static TableMetaDataProvider createMetaDataProvider(DataSource dataSource, TableMetaDataContext context) {
try {
return JdbcUtils.extractDatabaseMetaData(dataSource, databaseMetaData -> {
String databaseProductName = JdbcUtils.commonDatabaseName(databaseMetaData.getDatabaseProductName());
TableMetaDataProvider provider;
if ("Oracle".equals(databaseProductName)) {
provider = new OracleTableMetaDataProvider(
databaseMetaData, context.isOverrideIncludeSynonymsDefault());
}
else if ("PostgreSQL".equals(databaseProductName)) {
provider = new PostgresTableMetaDataProvider(databaseMetaData);
}
else if ("Apache Derby".equals(databaseProductName)) {
provider = new DerbyTableMetaDataProvider(databaseMetaData);
}
else if ("HSQL Database Engine".equals(databaseProductName)) {
provider = new HsqlTableMetaDataProvider(databaseMetaData);
}
else {
provider = new GenericTableMetaDataProvider(databaseMetaData);
}
if (logger.isDebugEnabled()) {
logger.debug("Using " + provider.getClass().getSimpleName());
}
provider.initializeWithMetaData(databaseMetaData);
if (context.isAccessTableColumnMetaData()) {
provider.initializeWithTableColumnMetaData(databaseMetaData,
context.getCatalogName(), context.getSchemaName(), context.getTableName());
}
return provider;
});
}
catch (MetaDataAccessException ex) {
throw new DataAccessResourceFailureException("Error retrieving database meta-data", ex);
}
}
add
else if ("MySQL".equals(databaseProductName) || "MariaDB".equals(databaseProductName)) {
provider = new MySqlTableMetaDataProvider(databaseMetaData);
}
Impact
Restores proper generated-keys support for MySQL and prevents regressions in SimpleJdbcInsert
, JdbcTemplate.update(..., RETURN_GENERATED_KEYS)
, and any higher-level abstractions relying on accurate metadata.
Comment From: github-actions[bot]
Fixed via 2ee34a5632a15e2e5f9dcb46d1999139cfc31011