hibernate/hibernate-orm · error · SemanticException
Not expecting multiple table references for an SQM INSERT-SE
Error message
Not expecting multiple table references for an SQM INSERT-SELECT
What it means
Translating an HQL INSERT ... SELECT, Hibernate builds a root table group for the insertion target and requires it to reference exactly one table. If that table group ends up with joins (secondary tables via @SecondaryTable, JOINED inheritance pieces, secondary table group joins triggered by the target's mapped attributes), the insert cannot be rendered as a single INSERT ... SELECT and visitInsertSelectStatement throws SemanticException('Not expecting multiple table references for an SQM INSERT-SELECT').
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java:1236
);
getFromClauseAccess().registerTableGroup( rootPath, rootTableGroup );
insertStatement = new InsertSelectStatement(
cteContainer,
(NamedTableReference) rootTableGroup.getPrimaryTableReference(),
entityDescriptor,
emptyList()
);
additionalInsertValues = visitInsertionTargetPaths(
(assigable, references) -> insertStatement.addTargetColumnReferences( references ),
sqmStatement,
entityDescriptor,
rootTableGroup
);
if ( hasJoins( rootTableGroup ) ) {
throw new SemanticException( "Not expecting multiple table references for an SQM INSERT-SELECT" );
}
}
finally {
popProcessingStateStack();
currentClauseStack.pop();
}
insertStatement.setSourceSelectStatement(
visitQueryPart( selectQueryPart )
);
insertStatement.getSourceSelectStatement().visitQuerySpecs(
querySpec -> {
final boolean appliedRowNumber =
additionalInsertValues.applySelections( querySpec, getSessionFactory() );
// Just make sure that if we get here, a row number will never be applied
// If this requires the special row number handling, it should use the mutation strategy
assert !appliedRowNumber;View on GitHub (pinned to fad1729dce)
Solutions
- Restructure the mapping: remove @SecondaryTable for the target entity or switch JOINED inheritance to SINGLE_TABLE (with discriminator) so one table suffices
- Write the insert with native SQL (session.createNativeMutationQuery) covering both tables explicitly
- Insert into a single-table staging/DTO entity and post-process, or perform per-row persists for the multi-table case
- Verify which mapping element creates the extra table reference (enable hibernate.show_sql / trace org.hibernate.SQL) and move the offending columns into the primary table
Example fix
-- before: Person has @SecondaryTable("person_details")
insert into Person (id, name, bio) select p.id, p.name, p.bio from Person p where ...
-- after: native SQL covering both tables
insert into person (id, name) select p.id, p.name from person p;
insert into person_details (id, bio) select p.id, p.bio from person p; Defensive patterns
Strategy: fallback
Validate before calling
// Only route insert-select to HQL when target is single-table EntityPersister persister = session.getEntityPersister(Person.class.getName(), null); boolean singleTable = persister.getPropertySpans() <= 1 || !persister.hasSubclasses(); // heuristic: check factory metadata for secondary tables before choosing HQL vs native SQL
Type guard
static boolean singleTableTarget(SessionFactory sf, String entityName) {
var descr = sf.getMetamodel().getEntityDescriptor(entityName);
return descr.getTableReferences() == null || descr.getPropertySpan() == descr.getTableReferences().size();
} Try / catch
catch (SemanticException e) { if (e.getMessage().contains("multiple table references")) { /* fall back to native multi-table INSERT or per-row persist */ } else throw e; } Prevention
- Reserve HQL insert-select for single-table entities
- After adding @SecondaryTable or switching inheritance strategy, retest all bulk inserts
- Maintain a mapping inventory of which entities are bulk-insert-safe
When it happens
Trigger: HQL 'insert into Person (id, name) select ... from Person p' where Person is mapped with @SecondaryTable or JOINED inheritance so createRootTableGroup produces joined table references; inserting into an entity whose target paths (visitInsertionTargetPaths) force joins to other tables; entities with @OneToMany join-table style secondary mappings referenced in the target column list.
Common situations: Bulk-copy/ETL jobs using HQL insert-select on entities with secondary tables; testing insert-select against a simpler entity worked, then pointing it at a richer mapped entity; inheritance refactoring (SINGLE_TABLE to JOINED) breaking existing insert-select statements; Hibernate 5 -> 6/7 migration where such statements previously rendered with different table-group logic.
Related errors
- Path '<navigablePath>' did not reference a known model part
- SQM INSERT-SELECT without bulk insertion capable identifier
- Couldn't find table reference
- Insert conflict 'do update' clause with constraint name is n
- field type not supported on Derby: " + unit
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/b3d5130440771a7e.
Report an issue: GitHub.