{"record":{"id":"79920e9e1782d0a8","repo":"hibernate/hibernate-orm","slug":"sqm-insert-select-without-bulk-insertion-capable-i","errorCode":null,"errorMessage":"SQM INSERT-SELECT without bulk insertion capable identifier generator: \" + identifierGenerator","messagePattern":"SQM INSERT-SELECT without bulk insertion capable identifier generator: \" \\+ identifierGenerator","errorType":"exception","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java","lineNumber":1586,"sourceCode":"\t\t\tfinal var selectClause = querySpec.getSelectClause();\n\t\t\tif ( versionExpression != null ) {\n\t\t\t\tif ( versionSelection == null ) {\n\t\t\t\t\t// The position is irrelevant as this is only needed for insert\n\t\t\t\t\tversionSelection = new SqlSelectionImpl( versionExpression );\n\t\t\t\t}\n\t\t\t\tselectClause.addSqlSelection( versionSelection );\n\t\t\t}\n\t\t\tif ( discriminatorExpression != null ) {\n\t\t\t\tif ( discriminatorSelection == null ) {\n\t\t\t\t\t// The position is irrelevant as this is only needed for insert\n\t\t\t\t\tdiscriminatorSelection = new SqlSelectionImpl( discriminatorExpression );\n\t\t\t\t}\n\t\t\t\tselectClause.addSqlSelection( discriminatorSelection );\n\t\t\t}\n\t\t\tif ( identifierGenerator != null ) {\n\t\t\t\tif ( identifierSelection == null ) {\n\t\t\t\t\tif ( !( identifierGenerator instanceof BulkInsertionCapableIdentifierGenerator bulkInsertionCapableGenerator ) ) {\n\t\t\t\t\t\tthrow new SemanticException(\n\t\t\t\t\t\t\t\t\"SQM INSERT-SELECT without bulk insertion capable identifier generator: \" + identifierGenerator );\n\t\t\t\t\t}\n\t\t\t\t\tif ( identifierGenerator instanceof OptimizableGenerator optimizableGenerator ) {\n\t\t\t\t\t\tfinal var optimizer = optimizableGenerator.getOptimizer();\n\t\t\t\t\t\tif ( optimizer != null && optimizer.getIncrementSize() > 1\n\t\t\t\t\t\t\t\t|| !bulkInsertionCapableGenerator.supportsBulkInsertionIdentifierGeneration() ) {\n\t\t\t\t\t\t\t// This is a special case where we have a sequence with an optimizer\n\t\t\t\t\t\t\t// or a table based identifier generator\n\t\t\t\t\t\t\tif ( !sessionFactory.getJdbcServices().getDialect().supportsWindowFunctions() ) {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tidentifierSelection =\n\t\t\t\t\t\t\t\t\t\tnew SqlSelectionImpl( createRowNumberingExpression( querySpec, sessionFactory ) );\n\t\t\t\t\t\t\t\tselectClause.addSqlSelection( identifierSelection );\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}","sourceCodeStart":1568,"sourceCodeEnd":1604,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java#L1568-L1604","documentation":"For HQL INSERT ... SELECT where the id must be generated by the statement itself, Hibernate can only inline ids if the identifier generator implements BulkInsertionCapableIdentifierGenerator (so it can emit a select fragment like nextval(seq)). When the target entity's generator does not (a plain custom IdentifierGenerator, AUTO resolving to a non-capable generator, etc.), AdditionalInsertValues.applySelections throws SemanticException('SQM INSERT-SELECT without bulk insertion capable identifier generator: <generator>'). Pooled optimizers additionally require window-function support and take the row-numbering path instead.","triggerScenarios":"'insert into Employee (name) select p.name from Person p' where Employee's id uses a custom IdentifierGenerator (or one configured with GenerationType.AUTO that resolves to a non-bulk-capable generator) and the id column is omitted from the target list; assigning generators like ForeignGenerator or hand-rolled generators that never implemented BulkInsertionCapableIdentifierGenerator; insert-select worked for a sequence-based entity but fails for another entity in the same app","commonSituations":"Legacy Hibernate 5 apps upgrading - generator contracts were reworked (IdentifierGenerator split, BeforeExecutionGenerator/OnExecutionGenerator); custom id generators written pre-Hibernate 6; GenerationType.AUTO/IDENTITY mixes where identity is fine (db-side) but the app also tries insert-select on sequence-less entities; pooled/hi-lo optimizers on databases without window functions.","solutions":["Switch the entity id to a bulk-capable strategy: @GeneratedValue(strategy = GenerationType.SEQUENCE) with a plain sequence (no pooled optimizer), or UUID","Make your custom generator implement org.hibernate.id.BulkInsertionCapableIdentifierGenerator (and supportsBulkInsertionIdentifierGeneration() returning true with a valid select fragment)","Supply the id in the statement: include the id column and select a client-side generated value, or omit the entity from insert-select and persist individually","For pooled optimizers on a dialect without window functions, remove the optimizer (allocationSize=1) or move to a database that supports window functions"],"exampleFix":"// before: custom generator, not bulk capable\n@Id @GeneratedValue(generator = \"myGen\")\n@GenericGenerator(name = \"myGen\", type = MyLegacyGenerator.class)\nprivate Long id;\n\n// after\n@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = \"emp_seq\")\n@SequenceGenerator(name = \"emp_seq\", sequenceName = \"emp_seq\", allocationSize = 1)\nprivate Long id;","handlingStrategy":"validation","validationCode":"// Check generator capability before building the insert-select\nif (sessionFactory instanceof SessionFactoryImpl sfi) {\n    var generator = sfi.getMetamodel().getEntityDescriptor(Employee.class.getName())\n            .getGenerator();\n    boolean bulkCapable = generator instanceof BulkInsertionCapableIdentifierGenerator b\n            && b.supportsBulkInsertionIdentifierGeneration();\n    if (!bulkCapable) { /* use sequence strategy or per-row persist */ }\n}","typeGuard":"static boolean bulkInsertionCapable(SessionFactory sf, String entity) {\n    var g = sf.getMetamodel().getEntityDescriptor(entity).getGenerator();\n    return g instanceof BulkInsertionCapableIdentifierGenerator b\n        && b.supportsBulkInsertionIdentifierGeneration();\n}","tryCatchPattern":"catch (SemanticException e) { if (e.getMessage().contains(\"bulk insertion capable\")) { /* switch id strategy to SEQUENCE/UUID or include ids explicitly */ } else throw e; }","preventionTips":["Use SEQUENCE (allocationSize=1) or UUID ids for entities targeted by insert-select","Implement BulkInsertionCapableIdentifierGenerator in custom generators during Hibernate 6+ upgrades","Avoid pooled optimizers on databases without window functions when using insert-select"],"tags":["hibernate","hql","insert-select","identifier-generator","sequence","bulk-insert","id-generation"],"backgroundTag":"bulk-insert-identifier-generator","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}