Conservatively handle JDBC MySQL-family batch inserts#3896
Conservatively handle JDBC MySQL-family batch inserts#3896radovanradic wants to merge 15 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refines how Micronaut Data JDBC selects the batch-insert path for MySQL-family databases, keeping MariaDB conservative around generated keys while allowing MySQL Connector/J batching when JDBC metadata indicates it’s safe. It also makes generated-key retrieval conditional on whether the calling operation actually requires keys (entity-returning / cascade / listeners) versus void/count insert methods.
Changes:
- Introduces
SqlBatchSupport(internal) to centralize batch-insert support checks, including JDBC metadata-based MySQL/MariaDB discrimination and operation-aware generated-key requirements. - Updates JDBC insert batching to optionally use
Statement.NO_GENERATED_KEYSfor void/count inserts (avoiding unnecessary key retrieval and entity mutation). - Adds targeted unit/integration tests covering MySQL vs MariaDB behavior and void/count insert semantics across multiple dialects.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| data-runtime/src/main/java/io/micronaut/data/runtime/operations/internal/sql/SqlBatchSupport.java | New internal helper encapsulating dialect/JDBC metadata batch-insert capability checks and generated-key requirement detection. |
| data-runtime/src/main/java/io/micronaut/data/runtime/operations/internal/sql/AbstractSqlRepositoryOperations.java | Delegates batch-insert support checks to SqlBatchSupport. |
| data-jdbc/src/main/java/io/micronaut/data/jdbc/operations/DefaultJdbcRepositoryOperations.java | Uses operation-aware generated-key requirements and JDBC metadata to choose batch vs fallback and whether to request generated keys. |
| data-runtime/src/test/groovy/io/micronaut/data/runtime/operations/internal/sql/SqlBatchSupportSpec.groovy | Unit tests for SqlBatchSupport decision matrix and generated-key requirement detection. |
| data-jdbc/src/test/groovy/io/micronaut/data/jdbc/mysql/MySqlBatchInsertSpec.groovy | MySQL integration coverage for generated-id batching and non-mutating void insertAll. |
| data-jdbc/src/test/java/io/micronaut/data/jdbc/mysql/MySqlBatchRecord.java | Test entity (record) for MySQL batch insert scenarios. |
| data-jdbc/src/test/groovy/io/micronaut/data/jdbc/mariadb/MariaBatchInsertSpec.groovy | MariaDB integration coverage for fallback vs no-generated-key batching behavior. |
| data-jdbc/src/test/java/io/micronaut/data/jdbc/mariadb/MariaBatchRecord.java | Test entity (record) for MariaDB batch insert scenarios. |
| data-jdbc/src/test/groovy/io/micronaut/data/jdbc/postgres/PostgresBatchInsertSpec.groovy | Ensures void/count inserts don’t mutate input ids (no generated-key requirement) on Postgres. |
| data-jdbc/src/test/groovy/io/micronaut/data/jdbc/h2/H2BatchInsertSpec.groovy | Ensures void/count inserts don’t mutate input ids (no generated-key requirement) on H2. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
data-jdbc/src/main/java/io/micronaut/data/jdbc/operations/DefaultJdbcRepositoryOperations.java:862
requiresGeneratedKeysis computed for the batch decision, but the per-row fallback path still buildsJdbcEntityOperationswithout any way to disable generated-key retrieval. For void/count insert methods (whererequiresGeneratedKeysis false), falling back (e.g. when JDBC metadata says batch updates aren’t supported) will still request generated keys and can mutate input entity IDs, which undermines the operation-aware generated-key handling introduced here.
if (!isSupportsBatchInsert(ctx, persistentEntity, storedQuery, requiresGeneratedKeys)) {
return operation.split().stream()
.map(persistOp -> {
JdbcEntityOperations<T> op = new JdbcEntityOperations<>(ctx, storedQuery, persistentEntity, persistOp.getEntity(), true);
op.persist();
|



Related to issue #3893
PR Summary
saveAllon the fallback path because batched generated-key behavior is driver-option dependent.Notes
saveAllfor entities with generated IDs intentionally uses the per-row fallback path so generated IDs are populated reliably.