diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 07e04313402b..ceb4445c2a71 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -25,7 +25,9 @@ import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.CompositeNestedGeneratedValueGenerator; import org.hibernate.id.OptimizableGenerator; +import org.hibernate.id.enhanced.LegacyNamingStrategy; import org.hibernate.id.enhanced.Optimizer; +import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.internal.util.NullnessUtil; import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.StandardStack; @@ -138,6 +140,7 @@ import org.hibernate.query.sqm.sql.internal.SqmMapEntryResult; import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; +import org.hibernate.query.sqm.sql.internal.StandardSqmTranslator; import org.hibernate.query.sqm.tree.SqmDmlStatement; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmStatement; @@ -441,6 +444,7 @@ import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.hibernate.boot.model.process.internal.InferredBasicValueResolver.resolveSqlTypeIndicators; +import static org.hibernate.cfg.MappingSettings.ID_DB_STRUCTURE_NAMING_STRATEGY; import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; import static org.hibernate.query.QueryLogging.QUERY_MESSAGE_LOGGER; @@ -1203,7 +1207,7 @@ public InsertStatement visitInsertSelectStatement(SqmInsertSelectStatement sq ); if ( hasJoins( rootTableGroup ) ) { - throw new SemanticException( "Not expecting multiple table references for an SQM INSERT-SELECT" ); + throw new HibernateException( "Not expecting multiple table references for an SQM INSERT-SELECT" ); } } finally { @@ -1438,8 +1442,9 @@ else if ( localName.equals( versionAttributeName ) ) { else if ( identifierGenerator != null ) { // When we have an identifier generator, we somehow must list the identifier column in the insert statement. final boolean addIdColumn; - if ( sqmStatement instanceof SqmInsertValuesStatement ) { - // For an InsertValuesStatement, we can just list the column, as we can inject a parameter in the VALUES clause. + if ( sqmStatement instanceof SqmInsertValuesStatement + || sqmStatement instanceof SqmInsertSelectStatement && this instanceof StandardSqmTranslator ) { + // For an InsertValuesStatement or an InsertSelectStatement, we can just list the column, as we can inject a parameter in the VALUES clause. addIdColumn = true; } else if ( !( identifierGenerator instanceof BulkInsertionCapableIdentifierGenerator bulkInsertionCapableGenerator ) ) { @@ -1551,6 +1556,14 @@ public boolean applySelections(QuerySpec querySpec, SessionFactoryImplementor se throw new SemanticException( "SQM INSERT-SELECT without bulk insertion capable identifier generator: " + identifierGenerator ); } + if ( identifierGenerator instanceof SequenceStyleGenerator && identifierGeneratorParameter == null + && LegacyNamingStrategy.STRATEGY_NAME.equals( + sessionFactory.getProperties().get( ID_DB_STRUCTURE_NAMING_STRATEGY ) ) ) { + identifierGeneratorParameter = new IdGeneratorParameter( identifierMapping, + (BeforeExecutionGenerator) identifierGenerator ); + selectClause.addSqlSelection( new SqlSelectionImpl( identifierGeneratorParameter ) ); + return false; + } if ( identifierGenerator instanceof OptimizableGenerator optimizableGenerator ) { final Optimizer optimizer = optimizableGenerator.getOptimizer(); if ( optimizer != null && optimizer.getIncrementSize() > 1 diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertSelectLegacyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertSelectLegacyTest.java new file mode 100644 index 000000000000..d3e58a0233ba --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertSelectLegacyTest.java @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.query.hql; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.id.enhanced.LegacyNamingStrategy; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@DomainModel( + annotatedClasses = { + InsertSelectLegacyTest.Person.class, + InsertSelectLegacyTest.Document.class + } +) +@ServiceRegistry( + settings = { + @Setting(name = AvailableSettings.SHOW_SQL, value = "true"), + @Setting(name = AvailableSettings.FORMAT_SQL, value = "true"), + @Setting(name = AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY, + value = LegacyNamingStrategy.STRATEGY_NAME) + } +) +@SessionFactory +public class InsertSelectLegacyTest { + + @Test + @JiraKey(value = "HHH-18835") + void testInsertSelect(SessionFactoryScope scope) { + + scope.inTransaction( session -> { + Person person = new Person(); + person.name = "Peter"; + session.persist( person ); + + session.createMutationQuery( + "insert into Document(name,owner) select concat(p.name,'s document'), p from Person p" ) + .executeUpdate(); + + Document document = session.createQuery( "select d from Document d", Document.class ).getSingleResult(); + assertNotNull( document ); + assertEquals( "Peters document", document.name ); + } ); + } + + @Entity(name = "Person") + public static class Person { + + @Id + @GeneratedValue + Long id; + + String name; + } + + @Entity(name = "Document") + public static class Document { + + @Id + @GeneratedValue + Long id; + + String name; + + @ManyToOne + Person owner; + } +}