diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java index 66aac5b68bba..7b165775a200 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java @@ -6,10 +6,12 @@ import jakarta.persistence.TemporalType; import jakarta.persistence.Timeout; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.Timeouts; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.community.dialect.sequence.LegacyDB2SequenceSupport; +import org.hibernate.community.dialect.temptable.DB2LegacyLocalTemporaryTableStrategy; import org.hibernate.dialect.DB2GetObjectExtractor; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; @@ -33,6 +35,8 @@ import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.DB2SequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.dialect.temptable.DB2GlobalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.DB2StructJdbcType; import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate; import org.hibernate.dialect.unique.SkipNullableUniqueDelegate; @@ -1041,6 +1045,21 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } + @Override + public @Nullable TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + // Starting in DB2 9.7, "real" global temporary tables that can be shared between sessions + // are supported; (obviously) data is not shared between sessions. + return getDB2Version().isBefore( 9, 7 ) ? null : DB2GlobalTemporaryTableStrategy.INSTANCE; + } + + @Override + public @Nullable TemporaryTableStrategy getLocalTemporaryTableStrategy() { + // Prior to DB2 9.7, "real" global temporary tables that can be shared between sessions + // are *not* supported; even though the DB2 command says to declare a "global" temp table + // Hibernate treats it as a "local" temp table. + return getDB2Version().isBefore( 9, 7 ) ? DB2LegacyLocalTemporaryTableStrategy.INSTANCE : null; + } + @Override public boolean supportsCurrentTimestampSelection() { return true; diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java index f3db4eca8012..f886a1016e47 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyDialect.java @@ -32,8 +32,9 @@ import org.hibernate.dialect.lock.spi.LockingSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.community.dialect.temptable.DerbyLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.Size; @@ -52,10 +53,9 @@ import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; -import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.service.ServiceRegistry; @@ -975,46 +975,18 @@ protected void registerDefaultKeywords() { registerKeyword( "YEAR" ); } - /** - * {@inheritDoc} - *

- * From Derby docs: - *

-	 *     The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection.
-	 * 
- *

- * {@link DB2Dialect} returns a {@link GlobalTemporaryTableMutationStrategy} that - * will make temporary tables created at startup and hence unavailable for subsequent connections.
- * see HHH-10238. - */ @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> "session." + TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -1023,23 +995,28 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { } @Override - public String getTemporaryTableCreateOptions() { - return "not logged"; + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return DerbyLocalTemporaryTableStrategy.INSTANCE; } @Override - public boolean supportsTemporaryTablePrimaryKey() { - return false; + public String getTemporaryTableCreateOptions() { + return DerbyLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override public String getTemporaryTableCreateCommand() { - return "declare global temporary table"; + return DerbyLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return DerbyLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); + } + + @Override + public boolean supportsTemporaryTablePrimaryKey() { + return DerbyLocalTemporaryTableStrategy.INSTANCE.supportsTemporaryTablePrimaryKey(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java index 05d52946e343..241a88b84df9 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DerbyLegacyDialect.java @@ -33,8 +33,9 @@ import org.hibernate.dialect.pagination.AbstractLimitHandler; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.community.dialect.temptable.DerbyLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; @@ -51,10 +52,9 @@ import org.hibernate.query.common.TemporalUnit; import org.hibernate.query.sqm.CastType; import org.hibernate.query.sqm.IntervalType; -import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; -import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.service.ServiceRegistry; @@ -978,46 +978,18 @@ protected void registerDefaultKeywords() { registerKeyword( "YEAR" ); } - /** - * {@inheritDoc} - *

- * From Derby docs: - *

-	 *     The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection.
-	 * 
- * - * {@link DB2Dialect} returns a {@link GlobalTemporaryTableMutationStrategy} that - * will make temporary tables created at startup and hence unavailable for subsequent connections.
- * see HHH-10238. - */ @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> "session." + TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -1026,23 +998,28 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { } @Override - public String getTemporaryTableCreateOptions() { - return "not logged"; + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return DerbyLocalTemporaryTableStrategy.INSTANCE; } @Override - public boolean supportsTemporaryTablePrimaryKey() { - return false; + public String getTemporaryTableCreateOptions() { + return DerbyLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override public String getTemporaryTableCreateCommand() { - return "declare global temporary table"; + return DerbyLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return DerbyLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); + } + + @Override + public boolean supportsTemporaryTablePrimaryKey() { + return DerbyLocalTemporaryTableStrategy.INSTANCE.supportsTemporaryTablePrimaryKey(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java index a94e25fae86e..2368fd3e7e82 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java @@ -44,8 +44,9 @@ import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; @@ -1023,30 +1024,14 @@ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { return getVersion().isBefore( 2,1 ) ? super.getFallbackSqmMutationStrategy( entityDescriptor, runtimeModelCreationContext ) - : new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - entityDescriptor, - name -> TemporaryTable.ID_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + : new GlobalTemporaryTableMutationStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { return getVersion().isBefore( 2, 1 ) ? super.getFallbackSqmInsertStrategy( entityDescriptor, runtimeModelCreationContext ) - : new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - entityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + : new GlobalTemporaryTableInsertStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override @@ -1054,9 +1039,14 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.GLOBAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return StandardGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateOptions() { - return "on commit delete rows"; + return StandardGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } private final FirebirdIndexExporter indexExporter = new FirebirdIndexExporter( this ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java index f4346d7ce937..091295f07581 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java @@ -34,8 +34,9 @@ import org.hibernate.dialect.sequence.H2V1SequenceSupport; import org.hibernate.dialect.sequence.H2V2SequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.H2DurationIntervalSecondJdbcType; import org.hibernate.dialect.type.H2JsonArrayJdbcTypeConstructor; import org.hibernate.dialect.type.H2JsonJdbcType; @@ -787,30 +788,14 @@ public NullOrdering getNullOrdering() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - entityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - entityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override @@ -818,9 +803,14 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.LOCAL; } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return StandardLocalTemporaryTableStrategy.INSTANCE; + } + @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return StandardLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java index b348faf80eab..a213bca84000 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HANALegacyDialect.java @@ -34,8 +34,9 @@ import org.hibernate.dialect.sequence.HANASequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.HANASqlAstTranslator; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.HANAGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.BinaryStream; @@ -1965,30 +1966,14 @@ public int getMaxLobPrefetchSize() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - entityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - entityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override @@ -1996,19 +1981,24 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.GLOBAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return HANAGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateOptions() { - return "on commit delete rows"; + return HANAGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override public String getTemporaryTableCreateCommand() { - return "create global temporary row table"; + return HANAGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public String getTemporaryTableTruncateCommand() { - return "truncate table"; + return HANAGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableTruncateCommand(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java index dbd97c16d383..737bf0a109e6 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/HSQLLegacyDialect.java @@ -34,8 +34,10 @@ import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; import org.hibernate.dialect.sequence.HSQLSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.HSQLLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; @@ -648,28 +650,10 @@ public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( // can happen in the middle of a transaction if ( getVersion().isBefore( 2 ) ) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } else { - return new LocalTemporaryTableMutationStrategy( - // With HSQLDB 2.0, the table name is qualified with MODULE to assist the drop - // statement (in-case there is a global name beginning with HT_) - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> "MODULE." + TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } } @@ -688,28 +672,10 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( // can happen in the middle of a transaction if ( getVersion().isBefore( 2 ) ) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } else { - return new LocalTemporaryTableInsertStrategy( - // With HSQLDB 2.0, the table name is qualified with MODULE to assist the drop - // statement (in-case there is a global name beginning with HT_) - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> "MODULE." + TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } } @@ -718,21 +684,32 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return getVersion().isBefore( 2 ) ? TemporaryTableKind.GLOBAL : TemporaryTableKind.LOCAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return StandardGlobalTemporaryTableStrategy.INSTANCE; + } + + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return HSQLLocalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateCommand() { - return getVersion().isBefore( 2 ) ? super.getTemporaryTableCreateCommand() : "declare local temporary table"; + return (getVersion().isBefore( 2 ) ? StandardGlobalTemporaryTableStrategy.INSTANCE + : HSQLLocalTemporaryTableStrategy.INSTANCE).getTemporaryTableCreateCommand(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - // Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end - // of the session (by default, data is cleared at commit). - return getVersion().isBefore( 2 ) ? AfterUseAction.CLEAN : AfterUseAction.DROP; + return (getVersion().isBefore( 2 ) ? StandardGlobalTemporaryTableStrategy.INSTANCE + : HSQLLocalTemporaryTableStrategy.INSTANCE).getTemporaryTableAfterUseAction(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return getVersion().isBefore( 2 ) ? BeforeUseAction.NONE : BeforeUseAction.CREATE; + return (getVersion().isBefore( 2 ) ? StandardGlobalTemporaryTableStrategy.INSTANCE + : HSQLLocalTemporaryTableStrategy.INSTANCE).getTemporaryTableBeforeUseAction(); } // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index bdf69569c39a..57d5231c4e04 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -30,6 +30,8 @@ import org.hibernate.dialect.SelectItemReferenceStrategy; import org.hibernate.dialect.function.InsertSubstringOverlayEmulation; import org.hibernate.dialect.function.TrimFunction; +import org.hibernate.community.dialect.temptable.InformixLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; @@ -49,7 +51,6 @@ import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableKind; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.Size; @@ -846,30 +847,14 @@ public String getCatalogSeparator() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -877,24 +862,29 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.LOCAL; } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return InformixLocalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateOptions() { - return "with no log"; + return InformixLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override public String getTemporaryTableCreateCommand() { - return "create temp table"; + return InformixLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - return AfterUseAction.NONE; + return InformixLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableAfterUseAction(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return InformixLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java index 039d6dc1cae7..163323cf63e8 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java @@ -4,6 +4,8 @@ */ package org.hibernate.community.dialect; +import java.sql.Types; + import jakarta.persistence.TemporalType; import org.hibernate.LockOptions; import org.hibernate.boot.model.FunctionContributions; @@ -23,8 +25,9 @@ import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.ANSISequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.community.dialect.temptable.IngresGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -62,8 +65,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; -import java.sql.Types; - import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER; import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING; import static org.hibernate.sql.ast.internal.NonLockingClauseStrategy.NON_CLAUSE_STRATEGY; @@ -454,30 +455,14 @@ public boolean supportsCurrentTimestampSelection() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - name -> "session." + TemporaryTable.ID_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -485,14 +470,19 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.GLOBAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return IngresGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateOptions() { - return "on commit preserve rows with norecovery"; + return IngresGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override public String getTemporaryTableCreateCommand() { - return "declare global temporary table"; + return IngresGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } // union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java index 466adcbbfe39..3f5fe8b6de6e 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MaxDBDialect.java @@ -18,8 +18,9 @@ import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitLimitHandler; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.community.dialect.temptable.MaxDBLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -285,40 +286,29 @@ public boolean supportsOffsetInSubquery() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> "temp." + TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> "temp." + TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); + } + + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return MaxDBLocalTemporaryTableStrategy.INSTANCE; } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return MaxDBLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - return AfterUseAction.DROP; + return MaxDBLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableAfterUseAction(); } @Override @@ -328,7 +318,7 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { @Override public String getTemporaryTableCreateOptions() { - return "ignore rollback"; + return MaxDBLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java index a2a9c7480e64..130f86be7bef 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java @@ -34,8 +34,9 @@ import org.hibernate.dialect.pagination.LimitLimitHandler; import org.hibernate.dialect.sequence.NoSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.MySQLLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.MySQLCastingJsonArrayJdbcTypeConstructor; import org.hibernate.dialect.type.MySQLCastingJsonJdbcType; import org.hibernate.engine.jdbc.Size; @@ -1056,32 +1057,19 @@ public NullOrdering getNullOrdering() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); + } - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return MySQLLocalTemporaryTableStrategy.INSTANCE; } @Override @@ -1091,22 +1079,22 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { @Override public String getTemporaryTableCreateCommand() { - return "create temporary table if not exists"; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public String getTemporaryTableDropCommand() { - return "drop temporary table"; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableDropCommand(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - return AfterUseAction.DROP; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableAfterUseAction(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index 7b7a26e62969..91c59b8af375 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -29,6 +29,14 @@ import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.DmlTargetColumnQualifierSupport; +import org.hibernate.dialect.temptable.OracleLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; +import org.hibernate.dialect.type.OracleBooleanJdbcType; +import org.hibernate.dialect.type.OracleJdbcHelper; +import org.hibernate.dialect.type.OracleJsonArrayJdbcTypeConstructor; +import org.hibernate.dialect.type.OracleJsonJdbcType; +import org.hibernate.dialect.type.OracleReflectionStructJdbcType; import org.hibernate.dialect.OracleTypes; import org.hibernate.dialect.Replacer; import org.hibernate.dialect.TimeZoneSupport; @@ -46,13 +54,7 @@ import org.hibernate.dialect.pagination.Oracle12LimitHandler; import org.hibernate.dialect.sequence.OracleSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableKind; -import org.hibernate.dialect.type.OracleBooleanJdbcType; -import org.hibernate.dialect.type.OracleJdbcHelper; -import org.hibernate.dialect.type.OracleJsonArrayJdbcTypeConstructor; -import org.hibernate.dialect.type.OracleJsonJdbcType; -import org.hibernate.dialect.type.OracleReflectionStructJdbcType; import org.hibernate.dialect.type.OracleUserDefinedTypeExporter; import org.hibernate.dialect.type.OracleXmlJdbcType; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; @@ -120,18 +122,6 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; -import java.sql.CallableStatement; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Types; -import java.time.temporal.ChronoField; -import java.time.temporal.TemporalAccessor; -import java.util.Locale; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import static java.lang.String.join; import static java.util.regex.Pattern.CASE_INSENSITIVE; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -1229,34 +1219,28 @@ public boolean isEmptyStringTreatedAsNull() { return true; } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return OracleLocalTemporaryTableStrategy.INSTANCE; + } + + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return StandardGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -1266,7 +1250,7 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { @Override public String getTemporaryTableCreateOptions() { - return "on commit delete rows"; + return StandardGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } /** diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index 10a8bd9944f9..91debe50d7dd 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -43,6 +43,8 @@ import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; import org.hibernate.dialect.sequence.PostgreSQLSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.PgJdbcHelper; import org.hibernate.dialect.type.PostgreSQLArrayJdbcTypeConstructor; import org.hibernate.dialect.type.PostgreSQLCastingInetJdbcType; @@ -1025,6 +1027,11 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return StandardLocalTemporaryTableStrategy.INSTANCE; + } + @Override public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { return new StandardSqlAstTranslatorFactory() { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java index 1766ce16a18c..8f9a692afded 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java @@ -44,6 +44,8 @@ import org.hibernate.dialect.sequence.SQLServer16SequenceSupport; import org.hibernate.dialect.sequence.SQLServerSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.dialect.temptable.SQLServerLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.SQLServerCastingXmlArrayJdbcTypeConstructor; import org.hibernate.dialect.type.SQLServerCastingXmlJdbcType; import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate; @@ -1149,19 +1151,14 @@ public void appendDateTimeLiteral( } } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return SQLServerLocalTemporaryTableStrategy.INSTANCE; + } + @Override public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { - switch (sqlTypeCode) { - case Types.CHAR: - case Types.NCHAR: - case Types.VARCHAR: - case Types.NVARCHAR: - case Types.LONGVARCHAR: - case Types.LONGNVARCHAR: - return "collate database_default"; - default: - return ""; - } + return SQLServerLocalTemporaryTableStrategy.INSTANCE.getCreateTemporaryTableColumnAnnotation( sqlTypeCode ); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java index 404ee2eeaea0..a1ed90516f95 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SingleStoreDialect.java @@ -45,8 +45,9 @@ import org.hibernate.dialect.lock.spi.OuterJoinLockingType; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitLimitHandler; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.community.dialect.temptable.SingleStoreLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; @@ -1083,26 +1084,16 @@ public NullOrdering getNullOrdering() { @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( - EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - - return new LocalTemporaryTableMutationStrategy( TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), runtimeModelCreationContext.getSessionFactory() ); + EntityMappingType rootEntityDescriptor, + RuntimeModelCreationContext runtimeModelCreationContext) { + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( - EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - - return new LocalTemporaryTableInsertStrategy( TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), runtimeModelCreationContext.getSessionFactory() ); + EntityMappingType rootEntityDescriptor, + RuntimeModelCreationContext runtimeModelCreationContext) { + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -1110,26 +1101,29 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.LOCAL; } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return SingleStoreLocalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateCommand() { - return "create temporary table if not exists"; + return SingleStoreLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } - //SingleStore throws an error on drop temporary table if there are uncommited statements within transaction. - //Just 'drop table' statement causes implicit commit, so using 'delete from'. @Override public String getTemporaryTableDropCommand() { - return "delete from"; + return SingleStoreLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableDropCommand(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - return AfterUseAction.DROP; + return SingleStoreLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableAfterUseAction(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return SingleStoreLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java index 6a5f02aea8cb..fa76a017e3cd 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TeradataDialect.java @@ -22,8 +22,9 @@ import org.hibernate.dialect.lock.spi.LockingSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.TopLimitHandler; -import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; +import org.hibernate.community.dialect.temptable.TeradataGlobalTemporaryTableStrategy; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; @@ -327,30 +328,14 @@ public String getAddColumnString() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -358,9 +343,14 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.GLOBAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return TeradataGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateOptions() { - return "on commit preserve rows"; + return TeradataGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java index 9d9df8d5554c..4c5123f651df 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenDialect.java @@ -22,8 +22,9 @@ import org.hibernate.dialect.lock.spi.LockingSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.SequenceSupport; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -354,30 +355,14 @@ public boolean supportsCrossJoin() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - name -> TemporaryTable.ID_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -385,9 +370,14 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.GLOBAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return StandardGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateOptions() { - return "on commit delete rows"; + return StandardGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/DB2LegacyLocalTemporaryTableStrategy.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/DB2LegacyLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..b6b8ef9d18eb --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/DB2LegacyLocalTemporaryTableStrategy.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.temptable; + +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; + +/** + * Legacy DB2 specific local temporary table strategy. + */ +public class DB2LegacyLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final DB2LegacyLocalTemporaryTableStrategy INSTANCE = new DB2LegacyLocalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return "session." + desiredTableName; + } + + @Override + public String getTemporaryTableCreateOptions() { + return "not logged"; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "declare global temporary table"; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.DROP; + } +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/DerbyLocalTemporaryTableStrategy.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/DerbyLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..4d96312fa126 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/DerbyLocalTemporaryTableStrategy.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.temptable; + +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; + +/** + * Derby specific local temporary table strategy. + */ +public class DerbyLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final DerbyLocalTemporaryTableStrategy INSTANCE = new DerbyLocalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return "session." + desiredTableName; + } + + @Override + public String getTemporaryTableCreateOptions() { + return "not logged"; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "declare global temporary table"; + } + + @Override + public boolean supportsTemporaryTablePrimaryKey() { + return false; + } + +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/InformixLocalTemporaryTableStrategy.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/InformixLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..404f0b870d30 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/InformixLocalTemporaryTableStrategy.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.temptable; + +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; + +/** + * Informix specific local temporary table strategy. + */ +public class InformixLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final InformixLocalTemporaryTableStrategy INSTANCE = new InformixLocalTemporaryTableStrategy(); + + @Override + public String getTemporaryTableCreateOptions() { + return "with no log"; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "create temp table"; + } + +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/IngresGlobalTemporaryTableStrategy.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/IngresGlobalTemporaryTableStrategy.java new file mode 100644 index 000000000000..41f174e2c973 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/IngresGlobalTemporaryTableStrategy.java @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.temptable; + +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; + +/** + * Ingres specific global temporary table strategy. + */ +public class IngresGlobalTemporaryTableStrategy extends StandardGlobalTemporaryTableStrategy { + + public static final IngresGlobalTemporaryTableStrategy INSTANCE = new IngresGlobalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return "session." + desiredTableName; + } + + @Override + public String getTemporaryTableCreateOptions() { + return "on commit preserve rows with norecovery"; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "declare global temporary table"; + } +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/MaxDBLocalTemporaryTableStrategy.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/MaxDBLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..287508a9b805 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/MaxDBLocalTemporaryTableStrategy.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.temptable; + +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; + +/** + * MaxDB specific local temporary table strategy. + */ +public class MaxDBLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final MaxDBLocalTemporaryTableStrategy INSTANCE = new MaxDBLocalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return "temp." + desiredTableName; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.DROP; + } + + @Override + public String getTemporaryTableCreateOptions() { + return "ignore rollback"; + } +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/SingleStoreLocalTemporaryTableStrategy.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/SingleStoreLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..f40391922a6a --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/SingleStoreLocalTemporaryTableStrategy.java @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.temptable; + +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; + +/** + * SingleStore specific local temporary table strategy. + */ +public class SingleStoreLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final SingleStoreLocalTemporaryTableStrategy INSTANCE = new SingleStoreLocalTemporaryTableStrategy(); + + @Override + public String getTemporaryTableCreateCommand() { + return "create temporary table if not exists"; + } + + //SingleStore throws an error on drop temporary table if there are uncommited statements within transaction. + //Just 'drop table' statement causes implicit commit, so using 'delete from'. + @Override + public String getTemporaryTableDropCommand() { + return "delete from"; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.DROP; + } +} diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/TeradataGlobalTemporaryTableStrategy.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/TeradataGlobalTemporaryTableStrategy.java new file mode 100644 index 000000000000..0cf2e1a75a65 --- /dev/null +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/temptable/TeradataGlobalTemporaryTableStrategy.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.community.dialect.temptable; + +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; + +/** + * Teradata specific global temporary table strategy. + */ +public class TeradataGlobalTemporaryTableStrategy extends StandardGlobalTemporaryTableStrategy { + + public static final TeradataGlobalTemporaryTableStrategy INSTANCE = new TeradataGlobalTemporaryTableStrategy(); + + @Override + public String getTemporaryTableCreateOptions() { + return "on commit preserve rows"; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index f59777d834fa..db3031ebeb36 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -29,6 +29,8 @@ import org.hibernate.SessionEventListener; import org.hibernate.SessionFactoryObserver; import org.hibernate.context.spi.TenantSchemaMapper; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.type.TimeZoneStorageStrategy; import org.hibernate.annotations.CacheLayout; import org.hibernate.boot.SchemaAutoTooling; @@ -179,6 +181,8 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { private final HqlTranslator hqlTranslator; private final SqmMultiTableMutationStrategy sqmMultiTableMutationStrategy; private final SqmMultiTableInsertStrategy sqmMultiTableInsertStrategy; + private final Constructor sqmMultiTableMutationStrategyConstructor; + private final Constructor sqmMultiTableInsertStrategyConstructor; private final SqmTranslatorFactory sqmTranslatorFactory; private final Boolean useOfJdbcNamedParametersEnabled; private boolean namedQueryStartupCheckingEnabled; @@ -388,10 +392,14 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo extractPropertyValue( QUERY_MULTI_TABLE_MUTATION_STRATEGY, settings ); sqmMultiTableMutationStrategy = resolveSqmMutationStrategy( sqmMutationStrategyImplName, serviceRegistry, strategySelector ); + sqmMultiTableMutationStrategyConstructor = + resolveSqmMutationStrategyConstructor( sqmMutationStrategyImplName, strategySelector ); final String sqmInsertStrategyImplName = extractPropertyValue( QUERY_MULTI_TABLE_INSERT_STRATEGY, settings ); sqmMultiTableInsertStrategy = resolveSqmInsertStrategy( sqmInsertStrategyImplName, serviceRegistry, strategySelector ); + sqmMultiTableInsertStrategyConstructor = + resolveSqmInsertStrategyConstructor( sqmInsertStrategyImplName, strategySelector ); useOfJdbcNamedParametersEnabled = configurationService.getSetting( CALLABLE_NAMED_PARAMS_ENABLED, BOOLEAN, true ); @@ -609,6 +617,7 @@ private SqmMultiTableMutationStrategy resolveSqmMutationStrategy( strategyClass -> { Constructor dialectConstructor = null; Constructor emptyConstructor = null; + Constructor entityBasedConstructor = null; // todo (6.0) : formalize the allowed constructor parameterizations for ( var declaredConstructor : strategyClass.getDeclaredConstructors() ) { final var parameterTypes = declaredConstructor.getParameterTypes(); @@ -622,31 +631,59 @@ private SqmMultiTableMutationStrategy resolveSqmMutationStrategy( else if ( parameterTypes.length == 0 ) { emptyConstructor = constructor; } + else if ( parameterTypes.length == 2 && parameterTypes[0] == EntityMappingType.class && parameterTypes[1] == RuntimeModelCreationContext.class ) { + entityBasedConstructor = (Constructor) declaredConstructor; + } } - try { - if ( dialectConstructor != null ) { - return dialectConstructor.newInstance( - serviceRegistry.requireService( JdbcServices.class ).getDialect() - ); + if ( entityBasedConstructor == null ) { + try { + if ( dialectConstructor != null ) { + return dialectConstructor.newInstance( + serviceRegistry.requireService( JdbcServices.class ).getDialect() + ); + } + else if ( emptyConstructor != null ) { + return emptyConstructor.newInstance(); + } } - else if ( emptyConstructor != null ) { - return emptyConstructor.newInstance(); + catch (Exception e) { + throw new StrategySelectionException( + "Could not instantiate named strategy class [" + + strategyClass.getName() + "]", + e + ); } + throw new IllegalArgumentException( + "Cannot instantiate the class [" + strategyClass.getName() + "] because it does not have a constructor that accepts a dialect or an empty constructor" ); } - catch (Exception e) { - throw new StrategySelectionException( - "Could not instantiate named strategy class [" + strategyClass.getName() + "]", - e - ); + else { + return null; } - throw new IllegalArgumentException( "Cannot instantiate the class [" - + strategyClass.getName() - + "] because it does not have a constructor that accepts a dialect or an empty constructor" ); } ); } + @SuppressWarnings("unchecked") + private Constructor resolveSqmMutationStrategyConstructor( + String strategyName, + StrategySelector strategySelector) { + if ( strategyName == null ) { + return null; + } + + Class strategyClass = + strategySelector.selectStrategyImplementor( SqmMultiTableMutationStrategy.class, strategyName ); + for ( Constructor declaredConstructor : strategyClass.getDeclaredConstructors() ) { + final Class[] parameterTypes = declaredConstructor.getParameterTypes(); + if ( parameterTypes.length == 2 && parameterTypes[0] == EntityMappingType.class && parameterTypes[1] == RuntimeModelCreationContext.class ) { + return (Constructor) declaredConstructor; + } + } + + return null; + } + @SuppressWarnings("unchecked") private SqmMultiTableInsertStrategy resolveSqmInsertStrategy( String strategyName, @@ -663,6 +700,7 @@ private SqmMultiTableInsertStrategy resolveSqmInsertStrategy( strategyClass -> { Constructor dialectConstructor = null; Constructor emptyConstructor = null; + Constructor entityBasedConstructor = null; // todo (6.0) : formalize the allowed constructor parameterizations for ( var declaredConstructor : strategyClass.getDeclaredConstructors() ) { final var parameterTypes = declaredConstructor.getParameterTypes(); @@ -676,31 +714,59 @@ private SqmMultiTableInsertStrategy resolveSqmInsertStrategy( else if ( parameterTypes.length == 0 ) { emptyConstructor = constructor; } + else if ( parameterTypes.length == 2 && parameterTypes[0] == EntityMappingType.class && parameterTypes[1] == RuntimeModelCreationContext.class ) { + entityBasedConstructor = (Constructor) declaredConstructor; + } } - try { - if ( dialectConstructor != null ) { - return dialectConstructor.newInstance( - serviceRegistry.requireService( JdbcServices.class ).getDialect() - ); + if ( entityBasedConstructor == null ) { + try { + if ( dialectConstructor != null ) { + return dialectConstructor.newInstance( + serviceRegistry.requireService( JdbcServices.class ).getDialect() + ); + } + else if ( emptyConstructor != null ) { + return emptyConstructor.newInstance(); + } } - else if ( emptyConstructor != null ) { - return emptyConstructor.newInstance(); + catch (Exception e) { + throw new StrategySelectionException( + "Could not instantiate named strategy class [" + + strategyClass.getName() + "]", + e + ); } + throw new IllegalArgumentException( + "Cannot instantiate the class [" + strategyClass.getName() + "] because it does not have a constructor that accepts a dialect or an empty constructor" ); } - catch (Exception e) { - throw new StrategySelectionException( - "Could not instantiate named strategy class [" + strategyClass.getName() + "]", - e - ); + else { + return null; } - throw new IllegalArgumentException( "Cannot instantiate the class [" - + strategyClass.getName() - + "] because it does not have a constructor that accepts a dialect or an empty constructor" ); } ); } + @SuppressWarnings("unchecked") + private Constructor resolveSqmInsertStrategyConstructor( + String strategyName, + StrategySelector strategySelector) { + if ( strategyName == null ) { + return null; + } + + Class strategyClass = + strategySelector.selectStrategyImplementor( SqmMultiTableInsertStrategy.class, strategyName ); + for ( Constructor declaredConstructor : strategyClass.getDeclaredConstructors() ) { + final Class[] parameterTypes = declaredConstructor.getParameterTypes(); + if ( parameterTypes.length == 2 && parameterTypes[0] == EntityMappingType.class && parameterTypes[1] == RuntimeModelCreationContext.class ) { + return (Constructor) declaredConstructor; + } + } + + return null; + } + private HqlTranslator resolveHqlTranslator( String producerName, StandardServiceRegistry serviceRegistry, @@ -921,6 +987,38 @@ public SqmMultiTableInsertStrategy getCustomSqmMultiTableInsertStrategy() { return sqmMultiTableInsertStrategy; } + @Override + public SqmMultiTableMutationStrategy resolveCustomSqmMultiTableMutationStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext creationContext) { + if ( sqmMultiTableMutationStrategyConstructor != null ) { + try { + return sqmMultiTableMutationStrategyConstructor.newInstance( rootEntityDescriptor, creationContext ); + } + catch (Exception e) { + throw new StrategySelectionException( + String.format( "Could not instantiate named strategy class [%s]", sqmMultiTableMutationStrategyConstructor.getDeclaringClass().getName() ), + e + ); + } + } + return null; + } + + @Override + public SqmMultiTableInsertStrategy resolveCustomSqmMultiTableInsertStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext creationContext) { + if ( sqmMultiTableInsertStrategyConstructor != null ) { + try { + return sqmMultiTableInsertStrategyConstructor.newInstance( rootEntityDescriptor, creationContext ); + } + catch (Exception e) { + throw new StrategySelectionException( + String.format( "Could not instantiate named strategy class [%s]", sqmMultiTableInsertStrategyConstructor.getDeclaringClass().getName() ), + e + ); + } + } + return null; + } + @Override public boolean isUseOfJdbcNamedParametersEnabled() { return useOfJdbcNamedParametersEnabled; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java index b9e69451a41b..1ea1fd57bd25 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Database.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.TreeMap; +import org.checkerframework.checker.nullness.qual.Nullable; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; import org.hibernate.boot.spi.MetadataBuildingOptions; @@ -132,6 +133,10 @@ public Namespace.Name getPhysicalImplicitNamespaceName() { return physicalImplicitNamespaceName; } + public @Nullable Namespace findNamespace(Identifier catalogName, Identifier schemaName) { + return namespaceMap.get( new Namespace.Name( catalogName, schemaName ) ); + } + public Namespace locateNamespace(Identifier catalogName, Identifier schemaName) { final Namespace.Name name = new Namespace.Name( catalogName, schemaName ); final Namespace namespace = namespaceMap.get( name ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java index 23320bb203db..2031523a93f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedNameParser.java @@ -135,8 +135,8 @@ else if ( tokens.length == 2 ) { name = tokens[1]; } else if ( tokens.length == 3 ) { - schemaName = tokens[0]; - catalogName = tokens[1]; + catalogName = tokens[0]; + schemaName = tokens[1]; name = tokens[2]; } else { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java index 3ec439e03428..016b41807cca 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java @@ -18,6 +18,8 @@ import org.hibernate.LockOptions; import org.hibernate.SessionFactoryObserver; import org.hibernate.context.spi.TenantSchemaMapper; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.type.TimeZoneStorageStrategy; import org.hibernate.annotations.CacheLayout; import org.hibernate.boot.SchemaAutoTooling; @@ -145,6 +147,16 @@ public SqmMultiTableInsertStrategy getCustomSqmMultiTableInsertStrategy() { return delegate.getCustomSqmMultiTableInsertStrategy(); } + @Override + public SqmMultiTableMutationStrategy resolveCustomSqmMultiTableMutationStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext creationContext) { + return delegate.resolveCustomSqmMultiTableMutationStrategy( rootEntityDescriptor, creationContext ); + } + + @Override + public SqmMultiTableInsertStrategy resolveCustomSqmMultiTableInsertStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext creationContext) { + return delegate.resolveCustomSqmMultiTableInsertStrategy( rootEntityDescriptor, creationContext ); + } + @Override public StatementInspector getStatementInspector() { return delegate.getStatementInspector(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java index 08cd1553025f..8e1d2dcbcdf7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -9,20 +9,21 @@ import org.hibernate.boot.model.FunctionContributions; import org.hibernate.dialect.function.CaseLeastGreatestEmulation; import org.hibernate.dialect.function.CastingConcatFunction; -import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.TransactSQLStrFunction; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; +import org.hibernate.dialect.temptable.TransactSQLLocalTemporaryTableStrategy; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.identity.AbstractTransactSQLIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; -import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableKind; -import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.sqm.TrimSpec; -import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; -import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.ast.SqlAstNodeRenderingMode; @@ -295,32 +296,21 @@ public boolean requiresCastForConcatenatingNonStrings() { @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( - EntityMappingType entityDescriptor, + EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - entityDescriptor, - basename -> '#' + TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( - EntityMappingType entityDescriptor, + EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - entityDescriptor, - name -> '#' + TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); + } + + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return TransactSQLLocalTemporaryTableStrategy.INSTANCE; } @Override @@ -330,18 +320,17 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { @Override public String getTemporaryTableCreateCommand() { - return "create table"; + return TransactSQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - // sql-server, at least needed this dropped after use; strange! - return AfterUseAction.DROP; + return TransactSQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableAfterUseAction(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return TransactSQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index 908c6d38e392..b879a94a7f3c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -27,6 +27,8 @@ import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.DB2SqlAstTranslator; import org.hibernate.dialect.sql.ast.PostgreSQLSqlAstTranslator; +import org.hibernate.dialect.temptable.DB2GlobalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.DB2StructJdbcType; import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate; import org.hibernate.dialect.unique.UniqueDelegate; @@ -848,6 +850,11 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return DB2GlobalTemporaryTableStrategy.INSTANCE; + } + @Override public boolean supportsIsTrue() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 10a862d2775e..1a2e5d1a0832 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -55,10 +55,12 @@ import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.sequence.NoSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; +import org.hibernate.dialect.temptable.LegacyTemporaryTableStrategy; +import org.hibernate.dialect.temptable.PersistentTemporaryTableStrategy; import org.hibernate.dialect.temptable.StandardTemporaryTableExporter; -import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableExporter; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.unique.AlterTableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.LobCreator; @@ -2190,7 +2192,9 @@ public String getSelectGUIDString() { * Does this database have some sort of support for temporary tables? * * @return true by default, since most do + * @deprecated Use {@link #getLocalTemporaryTableStrategy()} and {@link #getGlobalTemporaryTableStrategy()} to check instead */ + @Deprecated(forRemoval = true, since = "7.1") public boolean supportsTemporaryTables() { // Most databases do return true; @@ -2200,7 +2204,9 @@ public boolean supportsTemporaryTables() { * Does this database support primary keys for temporary tables? * * @return true by default, since most do + * @deprecated Moved to {@link TemporaryTableStrategy#supportsTemporaryTablePrimaryKey()} */ + @Deprecated(forRemoval = true, since = "7.1") public boolean supportsTemporaryTablePrimaryKey() { // Most databases do return true; @@ -3190,15 +3196,7 @@ public boolean requiresColumnListInCreateView() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new PersistentTableMutationStrategy( - TemporaryTable.createIdTable( - entityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new PersistentTableMutationStrategy( entityDescriptor, runtimeModelCreationContext ); } /** @@ -3210,15 +3208,7 @@ public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new PersistentTableInsertStrategy( - TemporaryTable.createEntityTable( - entityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new PersistentTableInsertStrategy( entityDescriptor, runtimeModelCreationContext ); } // UDT support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3899,9 +3889,41 @@ public TemporaryTableExporter getTemporaryTableExporter() { return temporaryTableExporter; } + /** + * The strategy to use for persistent temporary tables. + * + * @since 7.1 + */ + public TemporaryTableStrategy getPersistentTemporaryTableStrategy() { + return getSupportedTemporaryTableKind() == TemporaryTableKind.PERSISTENT + ? new LegacyTemporaryTableStrategy( this ) + : PersistentTemporaryTableStrategy.INSTANCE; + } + + /** + * The strategy to use for local temporary tables. + * + * @since 7.1 + */ + public @Nullable TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return getSupportedTemporaryTableKind() == TemporaryTableKind.LOCAL ? new LegacyTemporaryTableStrategy( this ) + : null; + } + + /** + * The strategy to use for global temporary tables. + * + * @since 7.1 + */ + public @Nullable TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return getSupportedTemporaryTableKind() == TemporaryTableKind.GLOBAL ? new LegacyTemporaryTableStrategy( this ) + : null; + } + /** * The kind of temporary tables that are supported on this database. */ + @Deprecated(forRemoval = true, since = "7.1") public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.PERSISTENT; } @@ -3911,6 +3933,7 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { * create a temporary table, specifying dialect-specific options, or * {@code null} if there are no options to specify. */ + @Deprecated(forRemoval = true, since = "7.1") public String getTemporaryTableCreateOptions() { return null; } @@ -3918,6 +3941,7 @@ public String getTemporaryTableCreateOptions() { /** * The command to create a temporary table. */ + @Deprecated(forRemoval = true, since = "7.1") public String getTemporaryTableCreateCommand() { return switch ( getSupportedTemporaryTableKind() ) { case PERSISTENT -> "create table"; @@ -3929,6 +3953,7 @@ public String getTemporaryTableCreateCommand() { /** * The command to drop a temporary table. */ + @Deprecated(forRemoval = true, since = "7.1") public String getTemporaryTableDropCommand() { return "drop table"; } @@ -3936,6 +3961,7 @@ public String getTemporaryTableDropCommand() { /** * The command to truncate a temporary table. */ + @Deprecated(forRemoval = true, since = "7.1") public String getTemporaryTableTruncateCommand() { return "delete from"; } @@ -3946,6 +3972,7 @@ public String getTemporaryTableTruncateCommand() { * @param sqlTypeCode The SQL type code * @return The annotation to be appended, for example, {@code COLLATE DATABASE_DEFAULT} in SQL Server */ + @Deprecated(forRemoval = true, since = "7.1") public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { return ""; } @@ -3964,6 +3991,7 @@ public TempTableDdlTransactionHandling getTemporaryTableDdlTransactionHandling() /** * The action to take after finishing use of a temporary table. */ + @Deprecated(forRemoval = true, since = "7.1") public AfterUseAction getTemporaryTableAfterUseAction() { return AfterUseAction.CLEAN; } @@ -3971,6 +3999,7 @@ public AfterUseAction getTemporaryTableAfterUseAction() { /** * The action to take before beginning use of a temporary table. */ + @Deprecated(forRemoval = true, since = "7.1") public BeforeUseAction getTemporaryTableBeforeUseAction() { return BeforeUseAction.NONE; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index d81d2841b7eb..ef2f0a9256ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -24,8 +24,10 @@ import org.hibernate.dialect.sequence.H2V2SequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.H2SqlAstTranslator; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.H2GlobalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.H2DurationIntervalSecondJdbcType; import org.hibernate.dialect.type.H2JsonArrayJdbcTypeConstructor; import org.hibernate.dialect.type.H2JsonJdbcType; @@ -774,35 +776,19 @@ public NullOrdering getNullOrdering() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - entityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - entityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override public String getTemporaryTableCreateOptions() { - return "TRANSACTIONAL"; + return H2GlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override @@ -810,6 +796,16 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.GLOBAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return H2GlobalTemporaryTableStrategy.INSTANCE; + } + + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return StandardLocalTemporaryTableStrategy.INSTANCE; + } + @Override public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { return EXTRACTOR; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java index 924a85e8a9fb..613ea3390a85 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java @@ -28,8 +28,9 @@ import org.hibernate.dialect.sequence.HANASequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.HANASqlAstTranslator; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.HANAGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.BinaryStream; @@ -1963,30 +1964,14 @@ public int getMaxLobPrefetchSize() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - entityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - entityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( entityDescriptor, runtimeModelCreationContext ); } @Override @@ -1994,19 +1979,24 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.GLOBAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return HANAGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateOptions() { - return "on commit delete rows"; + return HANAGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } @Override public String getTemporaryTableCreateCommand() { - return "create global temporary row table"; + return HANAGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public String getTemporaryTableTruncateCommand() { - return "truncate table"; + return HANAGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableTruncateCommand(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index da684f1336fa..a97bd8778da0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -19,8 +19,10 @@ import org.hibernate.dialect.sequence.HSQLSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.HSQLSqlAstTranslator; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.HSQLLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; @@ -524,54 +526,14 @@ public NullOrdering getNullOrdering() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - - // Hibernate uses this information for temporary tables that it uses for its own operations - // therefore the appropriate strategy is taken with different versions of HSQLDB - - // All versions of HSQLDB support GLOBAL TEMPORARY tables where the table - // definition is shared by all users but data is private to the session - // HSQLDB 2.0 also supports session-based LOCAL TEMPORARY tables where - // the definition and data is private to the session and table declaration - // can happen in the middle of a transaction - - return new LocalTemporaryTableMutationStrategy( - // With HSQLDB 2.0, the table name is qualified with SESSION to assist the drop - // statement (in-case there is a global name beginning with HT_) - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> "session." + TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - - // Hibernate uses this information for temporary tables that it uses for its own operations - // therefore the appropriate strategy is taken with different versions of HSQLDB - - // All versions of HSQLDB support GLOBAL TEMPORARY tables where the table - // definition is shared by all users but data is private to the session - // HSQLDB 2.0 also supports session-based LOCAL TEMPORARY tables where - // the definition and data is private to the session and table declaration - // can happen in the middle of a transaction - - return new LocalTemporaryTableInsertStrategy( - // With HSQLDB 2.0, the table name is qualified with SESSION to assist the drop - // statement (in-case there is a global name beginning with HT_) - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -579,19 +541,29 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { return TemporaryTableKind.LOCAL; } + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return StandardGlobalTemporaryTableStrategy.INSTANCE; + } + + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return HSQLLocalTemporaryTableStrategy.INSTANCE; + } + @Override public String getTemporaryTableCreateCommand() { - return "declare local temporary table"; + return HSQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - return AfterUseAction.DROP; + return HSQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableAfterUseAction(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return HSQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 5ab74110eb8f..2ccd40a23ecf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -25,8 +25,9 @@ import org.hibernate.dialect.sequence.NoSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.MySQLSqlAstTranslator; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.MySQLLocalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.MySQLCastingJsonArrayJdbcTypeConstructor; import org.hibernate.dialect.type.MySQLCastingJsonJdbcType; import org.hibernate.engine.jdbc.Size; @@ -1136,30 +1137,19 @@ public NullOrdering getNullOrdering() { public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new LocalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new LocalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); + } + + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return MySQLLocalTemporaryTableStrategy.INSTANCE; } @Override @@ -1169,22 +1159,22 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { @Override public String getTemporaryTableCreateCommand() { - return "create temporary table if not exists"; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateCommand(); } @Override public String getTemporaryTableDropCommand() { - return "drop temporary table"; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableDropCommand(); } @Override public AfterUseAction getTemporaryTableAfterUseAction() { - return AfterUseAction.DROP; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableAfterUseAction(); } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return BeforeUseAction.CREATE; + return MySQLLocalTemporaryTableStrategy.INSTANCE.getTemporaryTableBeforeUseAction(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index cea98f552e71..e9e75adcc0ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -27,8 +27,10 @@ import org.hibernate.dialect.sequence.OracleSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.OracleSqlAstTranslator; -import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.OracleLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.StandardGlobalTemporaryTableStrategy; import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.OracleBooleanJdbcType; import org.hibernate.dialect.type.OracleEnumJdbcType; import org.hibernate.dialect.type.OracleJdbcHelper; @@ -1304,34 +1306,28 @@ public boolean isEmptyStringTreatedAsNull() { return true; } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return OracleLocalTemporaryTableStrategy.INSTANCE; + } + + @Override + public TemporaryTableStrategy getGlobalTemporaryTableStrategy() { + return StandardGlobalTemporaryTableStrategy.INSTANCE; + } + @Override public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableMutationStrategy( - TemporaryTable.createIdTable( - rootEntityDescriptor, - basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { - return new GlobalTemporaryTableInsertStrategy( - TemporaryTable.createEntityTable( - rootEntityDescriptor, - name -> TemporaryTable.ENTITY_TABLE_PREFIX + name, - this, - runtimeModelCreationContext - ), - runtimeModelCreationContext.getSessionFactory() - ); + return new GlobalTemporaryTableInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } @Override @@ -1341,7 +1337,7 @@ public TemporaryTableKind getSupportedTemporaryTableKind() { @Override public String getTemporaryTableCreateOptions() { - return "on commit delete rows"; + return StandardGlobalTemporaryTableStrategy.INSTANCE.getTemporaryTableCreateOptions(); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 2ccacf34db3b..54fd00b75940 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -30,6 +30,8 @@ import org.hibernate.dialect.sequence.PostgreSQLSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.PostgreSQLSqlAstTranslator; +import org.hibernate.dialect.temptable.StandardLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.PgJdbcHelper; import org.hibernate.dialect.type.PostgreSQLArrayJdbcTypeConstructor; import org.hibernate.dialect.type.PostgreSQLCastingInetJdbcType; @@ -997,6 +999,11 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy( return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext ); } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return StandardLocalTemporaryTableStrategy.INSTANCE; + } + @Override public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { return new StandardSqlAstTranslatorFactory() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 65032316329c..f89f53f5d2bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -32,6 +32,8 @@ import org.hibernate.dialect.sequence.SQLServerSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.sql.ast.SQLServerSqlAstTranslator; +import org.hibernate.dialect.temptable.SQLServerLocalTemporaryTableStrategy; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.dialect.type.SQLServerCastingXmlArrayJdbcTypeConstructor; import org.hibernate.dialect.type.SQLServerCastingXmlJdbcType; import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate; @@ -1068,13 +1070,14 @@ public void appendDateTimeLiteral( } } + @Override + public TemporaryTableStrategy getLocalTemporaryTableStrategy() { + return SQLServerLocalTemporaryTableStrategy.INSTANCE; + } + @Override public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { - return switch (sqlTypeCode) { - case Types.CHAR, Types.NCHAR, Types.VARCHAR, Types.NVARCHAR, Types.LONGVARCHAR, Types.LONGNVARCHAR -> - "collate database_default"; - default -> ""; - }; + return SQLServerLocalTemporaryTableStrategy.INSTANCE.getCreateTemporaryTableColumnAnnotation( sqlTypeCode ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/DB2GlobalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/DB2GlobalTemporaryTableStrategy.java new file mode 100644 index 000000000000..5055a096f449 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/DB2GlobalTemporaryTableStrategy.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +/** + * DB2 specific global temporary table strategy. + */ +public class DB2GlobalTemporaryTableStrategy extends StandardGlobalTemporaryTableStrategy { + + public static final DB2GlobalTemporaryTableStrategy INSTANCE = new DB2GlobalTemporaryTableStrategy(); + + @Override + public String getTemporaryTableCreateOptions() { + return "not logged"; + } + + @Override + public boolean supportsTemporaryTablePrimaryKey() { + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/H2GlobalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/H2GlobalTemporaryTableStrategy.java new file mode 100644 index 000000000000..c75bdf6daf2f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/H2GlobalTemporaryTableStrategy.java @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +/** + * H2 specific global temporary table strategy. + */ +public class H2GlobalTemporaryTableStrategy extends StandardGlobalTemporaryTableStrategy { + + public static final H2GlobalTemporaryTableStrategy INSTANCE = new H2GlobalTemporaryTableStrategy(); + + @Override + public String getTemporaryTableCreateOptions() { + return "transactional"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/HANAGlobalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/HANAGlobalTemporaryTableStrategy.java new file mode 100644 index 000000000000..02439e99421e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/HANAGlobalTemporaryTableStrategy.java @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +/** + * HANA specific global temporary table strategy. + */ +public class HANAGlobalTemporaryTableStrategy extends StandardGlobalTemporaryTableStrategy { + + @Override + public String getTemporaryTableCreateCommand() { + return "create global temporary row table"; + } + + @Override + public String getTemporaryTableTruncateCommand() { + return "truncate table"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/HSQLLocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/HSQLLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..9e7db3d823a3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/HSQLLocalTemporaryTableStrategy.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; + +/** + * HSQL specific local temporary table strategy. + */ +public class HSQLLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final HSQLLocalTemporaryTableStrategy INSTANCE = new HSQLLocalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + // With HSQLDB 2.0, the table name is qualified with session to assist the drop + // statement (in-case there is a global name beginning with HT_) + return "session." + desiredTableName; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "declare local temporary table"; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.DROP; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/LegacyTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/LegacyTemporaryTableStrategy.java new file mode 100644 index 000000000000..cf9c74218409 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/LegacyTemporaryTableStrategy.java @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.hibernate.dialect.Dialect; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; + +/** + * Legacy strategy that delegates to deprecated Dialect methods. + */ +@Deprecated(forRemoval = true, since = "7.1") +public class LegacyTemporaryTableStrategy implements TemporaryTableStrategy { + + private final Dialect dialect; + + public LegacyTemporaryTableStrategy(Dialect dialect) { + this.dialect = dialect; + } + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return desiredTableName; + } + + @Override + public TemporaryTableKind getTemporaryTableKind() { + return dialect.getSupportedTemporaryTableKind(); + } + + @Override + public String getTemporaryTableCreateOptions() { + return dialect.getTemporaryTableCreateOptions(); + } + + @Override + public String getTemporaryTableCreateCommand() { + return dialect.getTemporaryTableCreateCommand(); + } + + @Override + public String getTemporaryTableDropCommand() { + return dialect.getTemporaryTableDropCommand(); + } + + @Override + public String getTemporaryTableTruncateCommand() { + return dialect.getTemporaryTableTruncateCommand(); + } + + @Override + public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { + return dialect.getCreateTemporaryTableColumnAnnotation( sqlTypeCode ); + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return dialect.getTemporaryTableAfterUseAction(); + } + + @Override + public BeforeUseAction getTemporaryTableBeforeUseAction() { + return dialect.getTemporaryTableBeforeUseAction(); + } + + @Override + public boolean supportsTemporaryTablePrimaryKey() { + return dialect.supportsTemporaryTablePrimaryKey(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/MySQLLocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/MySQLLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..37c60f0dfb2e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/MySQLLocalTemporaryTableStrategy.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; + +/** + * MySQL specific local temporary table strategy. + */ +public class MySQLLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final MySQLLocalTemporaryTableStrategy INSTANCE = new MySQLLocalTemporaryTableStrategy(); + + @Override + public String getTemporaryTableCreateCommand() { + return "create temporary table if not exists"; + } + + @Override + public String getTemporaryTableDropCommand() { + return "drop temporary table"; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.DROP; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/OracleLocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/OracleLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..ff2c43816861 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/OracleLocalTemporaryTableStrategy.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +/** + * Strategy to interact with Oracle private temporary tables that were introduced in Oracle 18c. + */ +public class OracleLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final OracleLocalTemporaryTableStrategy INSTANCE = new OracleLocalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return "ora$ptt_" + desiredTableName; + } + + @Override + public String getTemporaryTableCreateOptions() { + return "on commit drop definition"; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "create private temporary table"; + } + + @Override + public boolean supportsTemporaryTablePrimaryKey() { + return false; + } + + @Override + public boolean supportsTemporaryTableNullConstraint() { + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/PersistentTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/PersistentTemporaryTableStrategy.java new file mode 100644 index 000000000000..e3ec78f2edb1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/PersistentTemporaryTableStrategy.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; + +/** + * Strategy to interact with persistent temporary tables. + */ +public class PersistentTemporaryTableStrategy implements TemporaryTableStrategy { + + public static final PersistentTemporaryTableStrategy INSTANCE = new PersistentTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return desiredTableName; + } + + @Override + public TemporaryTableKind getTemporaryTableKind() { + return TemporaryTableKind.PERSISTENT; + } + + @Override + public @Nullable String getTemporaryTableCreateOptions() { + return null; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "create table"; + } + + @Override + public String getTemporaryTableDropCommand() { + return "drop table"; + } + + @Override + public String getTemporaryTableTruncateCommand() { + return "delete from"; + } + + @Override + public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { + return ""; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.CLEAN; + } + + @Override + public BeforeUseAction getTemporaryTableBeforeUseAction() { + return BeforeUseAction.NONE; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/SQLServerLocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/SQLServerLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..c101b76590a5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/SQLServerLocalTemporaryTableStrategy.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.hibernate.type.SqlTypes; + +/** + * SQL Server specific local temporary table strategy. + */ +public class SQLServerLocalTemporaryTableStrategy extends TransactSQLLocalTemporaryTableStrategy { + + public static final SQLServerLocalTemporaryTableStrategy INSTANCE = new SQLServerLocalTemporaryTableStrategy(); + + @Override + public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { + return switch ( sqlTypeCode ) { + case SqlTypes.CHAR, SqlTypes.VARCHAR, SqlTypes.CLOB, SqlTypes.NCHAR, SqlTypes.NVARCHAR, SqlTypes.NCLOB -> + "collate database_default"; + default -> ""; + }; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardGlobalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardGlobalTemporaryTableStrategy.java new file mode 100644 index 000000000000..103c8792023e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardGlobalTemporaryTableStrategy.java @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; + +/** + * Strategy to interact with global temporary tables. + */ +public class StandardGlobalTemporaryTableStrategy implements TemporaryTableStrategy { + + public static final StandardGlobalTemporaryTableStrategy INSTANCE = new StandardGlobalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return desiredTableName; + } + + @Override + public TemporaryTableKind getTemporaryTableKind() { + return TemporaryTableKind.GLOBAL; + } + + @Override + public String getTemporaryTableCreateOptions() { + return "on commit delete rows"; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "create global temporary table"; + } + + @Override + public String getTemporaryTableDropCommand() { + return "drop table"; + } + + @Override + public String getTemporaryTableTruncateCommand() { + return "delete from"; + } + + @Override + public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { + return ""; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.CLEAN; + } + + @Override + public BeforeUseAction getTemporaryTableBeforeUseAction() { + return BeforeUseAction.NONE; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardLocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..a2bf19ca9bc0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardLocalTemporaryTableStrategy.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; + +/** + * Strategy to interact with local temporary tables. + */ +public class StandardLocalTemporaryTableStrategy implements TemporaryTableStrategy { + + public static final StandardLocalTemporaryTableStrategy INSTANCE = new StandardLocalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return desiredTableName; + } + + @Override + public TemporaryTableKind getTemporaryTableKind() { + return TemporaryTableKind.LOCAL; + } + + @Override + public @Nullable String getTemporaryTableCreateOptions() { + return null; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "create local temporary table"; + } + + @Override + public String getTemporaryTableDropCommand() { + return "drop table"; + } + + @Override + public String getTemporaryTableTruncateCommand() { + return "delete from"; + } + + @Override + public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) { + return ""; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + return AfterUseAction.NONE; + } + + @Override + public BeforeUseAction getTemporaryTableBeforeUseAction() { + return BeforeUseAction.CREATE; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardTemporaryTableExporter.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardTemporaryTableExporter.java index cd1c7aa588a2..6eb3a86eff5c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardTemporaryTableExporter.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/StandardTemporaryTableExporter.java @@ -20,25 +20,59 @@ public StandardTemporaryTableExporter(Dialect dialect) { this.dialect = dialect; } + @Deprecated(forRemoval = true, since = "7.1") protected String getCreateCommand() { return dialect.getTemporaryTableCreateCommand(); } + protected String getCreateCommand(TemporaryTableStrategy temporaryTableStrategy) { + return temporaryTableStrategy.getTemporaryTableCreateCommand(); + } + + @Deprecated(forRemoval = true, since = "7.1") protected String getCreateOptions() { return dialect.getTemporaryTableCreateOptions(); } + protected String getCreateOptions(TemporaryTableStrategy temporaryTableStrategy) { + return temporaryTableStrategy.getTemporaryTableCreateOptions(); + } + + @Deprecated(forRemoval = true, since = "7.1") protected String getDropCommand() { return dialect.getTemporaryTableDropCommand(); } + protected String getDropCommand(TemporaryTableStrategy temporaryTableStrategy) { + return temporaryTableStrategy.getTemporaryTableDropCommand(); + } + + @Deprecated(forRemoval = true, since = "7.1") protected String getTruncateTableCommand() { return dialect.getTemporaryTableTruncateCommand(); } + protected String getTruncateTableCommand(TemporaryTableStrategy temporaryTableStrategy) { + return temporaryTableStrategy.getTemporaryTableTruncateCommand(); + } + + private TemporaryTableStrategy getDefaultTemporaryTableStrategy(TemporaryTable temporaryTable) { + final TemporaryTableStrategy temporaryTableStrategy = switch ( temporaryTable.getTemporaryTableKind() ) { + case LOCAL -> dialect.getLocalTemporaryTableStrategy(); + case GLOBAL -> dialect.getGlobalTemporaryTableStrategy(); + case PERSISTENT -> dialect.getPersistentTemporaryTableStrategy(); + }; + if ( temporaryTableStrategy == null ) { + throw new IllegalStateException( + "Dialect returns null TemporaryTableStrategy for temporary table " + temporaryTable.getQualifiedTableName() + " of type " + temporaryTable.getTemporaryTableKind() ); + } + return temporaryTableStrategy; + } + @Override public String getSqlCreateCommand(TemporaryTable temporaryTable) { - final StringBuilder buffer = new StringBuilder( getCreateCommand() ).append( ' ' ); + final TemporaryTableStrategy temporaryTableStrategy = getDefaultTemporaryTableStrategy( temporaryTable ); + final StringBuilder buffer = new StringBuilder( getCreateCommand( temporaryTableStrategy ) ).append( ' ' ); buffer.append( temporaryTable.getQualifiedTableName() ); buffer.append( '(' ); @@ -49,23 +83,25 @@ public String getSqlCreateCommand(TemporaryTable temporaryTable) { buffer.append( databaseTypeName ); - final String columnAnnotation = dialect.getCreateTemporaryTableColumnAnnotation( sqlTypeCode ); + final String columnAnnotation = temporaryTableStrategy.getCreateTemporaryTableColumnAnnotation( sqlTypeCode ); if ( !columnAnnotation.isEmpty() ) { buffer.append( ' ' ).append( columnAnnotation ); } - if ( column.isNullable() ) { - final String nullColumnString = dialect.getNullColumnString( databaseTypeName ); - if ( !databaseTypeName.contains( nullColumnString ) ) { - buffer.append( nullColumnString ); + if ( temporaryTableStrategy.supportsTemporaryTableNullConstraint() ) { + if ( column.isNullable() ) { + final String nullColumnString = dialect.getNullColumnString( databaseTypeName ); + if ( !databaseTypeName.contains( nullColumnString ) ) { + buffer.append( nullColumnString ); + } + } + else { + buffer.append( " not null" ); } - } - else { - buffer.append( " not null" ); } buffer.append( ", " ); } - if ( dialect.supportsTemporaryTablePrimaryKey() ) { + if ( temporaryTableStrategy.supportsTemporaryTablePrimaryKey() ) { buffer.append( "primary key (" ); for ( TemporaryTableColumn column : temporaryTable.getColumnsForExport() ) { if ( column.isPrimaryKey() ) { @@ -81,7 +117,7 @@ public String getSqlCreateCommand(TemporaryTable temporaryTable) { } buffer.append( ')' ); - final String createOptions = getCreateOptions(); + final String createOptions = getCreateOptions( temporaryTableStrategy ); if ( createOptions != null ) { buffer.append( ' ' ).append( createOptions ); } @@ -90,24 +126,26 @@ public String getSqlCreateCommand(TemporaryTable temporaryTable) { } @Override - public String getSqlDropCommand(TemporaryTable idTable) { - return getDropCommand() + " " + idTable.getQualifiedTableName(); + public String getSqlDropCommand(TemporaryTable temporaryTable) { + final TemporaryTableStrategy temporaryTableStrategy = getDefaultTemporaryTableStrategy( temporaryTable ); + return getDropCommand( temporaryTableStrategy ) + " " + temporaryTable.getQualifiedTableName(); } @Override public String getSqlTruncateCommand( - TemporaryTable idTable, + TemporaryTable temporaryTable, Function sessionUidAccess, SharedSessionContractImplementor session) { - if ( idTable.getSessionUidColumn() != null ) { + final TemporaryTableStrategy temporaryTableStrategy = getDefaultTemporaryTableStrategy( temporaryTable ); + if ( temporaryTable.getSessionUidColumn() != null ) { final ParameterMarkerStrategy parameterMarkerStrategy = session.getSessionFactory().getParameterMarkerStrategy(); - return getTruncateTableCommand() + " " + idTable.getQualifiedTableName() - + " where " + idTable.getSessionUidColumn().getColumnName() + " = " + return getTruncateTableCommand( temporaryTableStrategy ) + " " + temporaryTable.getQualifiedTableName() + + " where " + temporaryTable.getSessionUidColumn().getColumnName() + " = " + parameterMarkerStrategy.createMarker( 1, null ); } else { - return getTruncateTableCommand() + " " + idTable.getQualifiedTableName(); + return getTruncateTableCommand( temporaryTableStrategy ) + " " + temporaryTable.getQualifiedTableName(); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTable.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTable.java index 63b98b5dff24..dc1911c198f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTable.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTable.java @@ -5,47 +5,51 @@ package org.hibernate.dialect.temptable; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.UUID; -import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; +import org.hibernate.boot.Metadata; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.relational.Exportable; import org.hibernate.boot.model.relational.QualifiedNameParser; import org.hibernate.boot.model.relational.QualifiedTableName; +import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.Size; import org.hibernate.generator.Generator; -import org.hibernate.id.OptimizableGenerator; -import org.hibernate.id.enhanced.Optimizer; +import org.hibernate.generator.OnExecutionGenerator; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.mapping.Collection; import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; import org.hibernate.mapping.Contributable; +import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.Selectable; -import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.SingleTableSubclass; +import org.hibernate.mapping.Value; +import org.hibernate.metamodel.mapping.Association; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.AttributeMappingsList; +import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; -import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.SingleTableEntityPersister; +import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; import org.hibernate.type.BasicType; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.spi.TypeConfiguration; -import static org.hibernate.boot.model.internal.BinderHelper.findPropertyByName; /** * @author Steve Ebersole @@ -57,11 +61,13 @@ public class TemporaryTable implements Exportable, Contributable { public static final String ENTITY_TABLE_PREFIX = "HTE_"; public static final String DEFAULT_ALIAS = "temptable_"; public static final String ENTITY_TABLE_IDENTITY_COLUMN = "HTE_IDENTITY"; + public static final String ENTITY_ROW_NUMBER_COLUMN = "rn_"; private static final CoreMessageLogger LOG = CoreLogging.messageLogger( TemporaryTable.class ); - private final EntityMappingType entityDescriptor; + private final String contributor; private final String qualifiedTableName; + private final TemporaryTableKind temporaryTableKind; private final TemporaryTableSessionUidColumn sessionUidColumn; private final List columns; @@ -70,60 +76,44 @@ public class TemporaryTable implements Exportable, Contributable { private final Dialect dialect; private TemporaryTable( - EntityMappingType entityDescriptor, + PersistentClass persistentClass, Function temporaryTableNameAdjuster, + TemporaryTableKind temporaryTableKind, Dialect dialect, RuntimeModelCreationContext creationContext, Function> columnInitializer) { - this.entityDescriptor = entityDescriptor; - final EntityPersister entityPersister = entityDescriptor.getEntityPersister(); - final EntityPersister rootEntityPersister = entityDescriptor.getRootEntityDescriptor().getEntityPersister(); - final String persisterQuerySpace = entityPersister.getSynchronizedQuerySpaces()[0]; - final QualifiedNameParser.NameParts nameParts = QualifiedNameParser.INSTANCE.parse( persisterQuerySpace ); - // The table name might be a sub-query, which is inappropriate for a temporary table name - final String tableBaseName; - if ( rootEntityPersister != entityPersister - && rootEntityPersister instanceof SingleTableEntityPersister singleTableEntityPersister ) { + this.contributor = persistentClass.getContributor(); + final Identifier tableNameIdentifier; + if ( persistentClass instanceof SingleTableSubclass ) { // In this case, the descriptor is a subclass of a single table inheritance. // To avoid name collisions, we suffix the table name with the subclass number - tableBaseName = nameParts.getObjectName().getText() + ArrayHelper.indexOf( - singleTableEntityPersister.getSubclassClosure(), - entityPersister.getEntityName() - ); - } - else { - tableBaseName = nameParts.getObjectName().getText(); - } - final QualifiedNameParser.NameParts adjustedNameParts = QualifiedNameParser.INSTANCE.parse( - temporaryTableNameAdjuster.apply( tableBaseName ) - ); - final String temporaryTableName = adjustedNameParts.getObjectName().getText(); - final Identifier tableNameIdentifier; - if ( temporaryTableName.length() > dialect.getMaxIdentifierLength() ) { tableNameIdentifier = new Identifier( - temporaryTableName.substring( 0, dialect.getMaxIdentifierLength() ), - nameParts.getObjectName().isQuoted() + persistentClass.getTable().getNameIdentifier().getText() + persistentClass.getSubclassId(), + persistentClass.getTable().getNameIdentifier().isQuoted() ); } else { - tableNameIdentifier = new Identifier( temporaryTableName, nameParts.getObjectName().isQuoted() ); + tableNameIdentifier = persistentClass.getTable().getNameIdentifier(); } + // Have to parse the adjusted name, since it could be prepended by a schema + final QualifiedNameParser.NameParts nameParts = QualifiedNameParser.INSTANCE + .parse( temporaryTableNameAdjuster.apply( tableNameIdentifier.getText() ) ); + final Identifier catalogIdentifier = nameParts.getCatalogName() != null ? nameParts.getCatalogName() + : persistentClass.getTable().getCatalogIdentifier(); + final Identifier schemaIdentifier = nameParts.getSchemaName() != null ? nameParts.getSchemaName() + : persistentClass.getTable().getSchemaIdentifier(); + final String adjustedName = nameParts.getObjectName().getText(); + final Identifier temporaryTableNameIdentifier = new Identifier( + adjustedName.substring( 0, Math.min( dialect.getMaxIdentifierLength(), adjustedName.length() ) ), + tableNameIdentifier.isQuoted() + ); this.qualifiedTableName = creationContext.getSqlStringGenerationContext().format( - new QualifiedTableName( - adjustedNameParts.getCatalogName() != null - ? adjustedNameParts.getCatalogName() - : nameParts.getCatalogName(), - adjustedNameParts.getSchemaName() != null - ? adjustedNameParts.getSchemaName() - : nameParts.getSchemaName(), - tableNameIdentifier - ) + new QualifiedTableName( catalogIdentifier, schemaIdentifier, temporaryTableNameIdentifier ) ); + this.temporaryTableKind = temporaryTableKind; this.dialect = dialect; - if ( dialect.getSupportedTemporaryTableKind() == TemporaryTableKind.PERSISTENT ) { - final TypeConfiguration typeConfiguration = entityPersister - .getFactory() - .getTypeConfiguration(); + if ( temporaryTableKind == TemporaryTableKind.PERSISTENT ) { + final TypeConfiguration typeConfiguration = creationContext.getTypeConfiguration(); final BasicType uuidType = typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.UUID_CHAR ); @@ -165,85 +155,84 @@ private TemporaryTable( } } + @Deprecated(forRemoval = true, since = "7.1") public static TemporaryTable createIdTable( EntityMappingType entityDescriptor, Function temporaryTableNameAdjuster, Dialect dialect, RuntimeModelCreationContext runtimeModelCreationContext) { + return createIdTable( + runtimeModelCreationContext.getBootModel() + .getEntityBinding( entityDescriptor.getEntityName() ), + temporaryTableNameAdjuster, + dialect.getSupportedTemporaryTableKind(), + dialect, + runtimeModelCreationContext + ); + } + + @Deprecated(forRemoval = true, since = "7.1") + public static TemporaryTable createEntityTable( + EntityMappingType entityDescriptor, + Function temporaryTableNameAdjuster, + Dialect dialect, + RuntimeModelCreationContext runtimeModelCreationContext) { + return createIdTable( + runtimeModelCreationContext.getBootModel() + .getEntityBinding( entityDescriptor.getEntityName() ), + temporaryTableNameAdjuster, + dialect.getSupportedTemporaryTableKind(), + dialect, + runtimeModelCreationContext + ); + } + + public static TemporaryTable createIdTable( + PersistentClass persistentClass, + Function temporaryTableNameAdjuster, + TemporaryTableKind temporaryTableKind, + Dialect dialect, + RuntimeModelCreationContext runtimeModelCreationContext) { return new TemporaryTable( - entityDescriptor, + persistentClass, temporaryTableNameAdjuster, + temporaryTableKind, dialect, runtimeModelCreationContext, temporaryTable -> { + final MetadataImplementor metadata = runtimeModelCreationContext.getMetadata(); final List columns = new ArrayList<>(); - final PersistentClass entityBinding = runtimeModelCreationContext.getBootModel() - .getEntityBinding( entityDescriptor.getEntityName() ); - final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - int idIdx = 0; - for ( Column column : entityBinding.getKey().getColumns() ) { - final JdbcMapping jdbcMapping = identifierMapping.getJdbcMapping( idIdx++ ); + for ( Column column : persistentClass.getKey().getColumns() ) { columns.add( new TemporaryTableColumn( temporaryTable, column.getText( dialect ), - jdbcMapping, - column.getSqlType( - runtimeModelCreationContext.getMetadata() - ), - column.getColumnSize( - dialect, - runtimeModelCreationContext.getMetadata() - ), + column.getType(), + column.getSqlType( metadata ), + column.getColumnSize( dialect, metadata ), column.isNullable(), true ) ); } - visitPluralAttributes( entityDescriptor, (pluralAttribute, attributeName) -> { - if ( pluralAttribute.getSeparateCollectionTable() != null ) { - // Ensure that the FK target columns are available - final ForeignKeyDescriptor keyDescriptor = pluralAttribute.getKeyDescriptor(); - if ( keyDescriptor == null ) { - // This is expected to happen when processing a - // PostInitCallbackEntry because the callbacks - // are not ordered. The exception is caught in - // MappingModelCreationProcess.executePostInitCallbacks() - // and the callback is re-queued. - throw new IllegalStateException( "Not yet ready: " + pluralAttribute ); - } - final ModelPart fkTarget = keyDescriptor.getTargetPart(); - if ( !fkTarget.isEntityIdentifierMapping() ) { - final PersistentClass declaringClass = runtimeModelCreationContext.getBootModel() - .getEntityBinding( pluralAttribute.findContainingEntityMapping().getEntityName() ); - final Property property = findPropertyByName( declaringClass, attributeName ); - assert property != null; - final Collection collection = (Collection) property.getValue(); - final Iterator columnIterator = collection.getKey().getSelectables().iterator(); - fkTarget.forEachSelectable( - (columnIndex, selection) -> { - final Selectable selectable = columnIterator.next(); - if ( selectable instanceof Column column ) { - columns.add( - new TemporaryTableColumn( - temporaryTable, - column.getText( dialect ), - selection.getJdbcMapping(), - column.getSqlType( - runtimeModelCreationContext.getMetadata() - ), - column.getColumnSize( - dialect, - runtimeModelCreationContext.getMetadata() - ), - column.isNullable() - ) - ); - } - } - ); + visitPluralAttributes( persistentClass.getPropertyClosure(), collection -> { + if ( collection.getCollectionTable() != null && collection.getReferencedPropertyName() != null ) { + final KeyValue collectionKey = collection.getKey(); + for ( Selectable selectable : collectionKey.getSelectables() ) { + if ( selectable instanceof Column column ) { + columns.add( + new TemporaryTableColumn( + temporaryTable, + column.getText( dialect ), + column.getType(), + column.getSqlType( metadata ), + column.getColumnSize( dialect, metadata ), + column.isNullable() + ) + ); + } } } } ); @@ -252,85 +241,52 @@ public static TemporaryTable createIdTable( ); } - private static void visitPluralAttributes( - EntityMappingType entityDescriptor, - BiConsumer consumer) { - entityDescriptor.visitSubTypeAttributeMappings( - attribute -> { - if ( attribute instanceof PluralAttributeMapping pluralAttributeMapping ) { - consumer.accept( pluralAttributeMapping, attribute.getAttributeName() ); - } - else if ( attribute instanceof EmbeddedAttributeMapping embeddedAttributeMapping ) { - visitPluralAttributes( - embeddedAttributeMapping, - attribute.getAttributeName(), - consumer - ); - } - } - ); - } - - private static void visitPluralAttributes( - EmbeddedAttributeMapping attributeMapping, - String attributeName, - BiConsumer consumer) { - attributeMapping.visitSubParts( - modelPart -> { - if ( modelPart instanceof PluralAttributeMapping pluralAttribute ) { - consumer.accept( pluralAttribute, attributeName + "." + pluralAttribute.getAttributeName() ); - } - else if ( modelPart instanceof EmbeddedAttributeMapping embeddedAttribute ) { - visitPluralAttributes( - embeddedAttribute, - attributeName + "." + embeddedAttribute.getAttributeName(), - consumer - ); - } - }, - null - ); + private static void visitPluralAttributes(List properties, Consumer consumer) { + for ( Property property : properties ) { + final Value value = property.getValue(); + if ( value instanceof Collection collection ) { + consumer.accept( collection ); + } + else if ( value instanceof Component component ) { + visitPluralAttributes( component.getProperties(), consumer ); + } + } } public static TemporaryTable createEntityTable( - EntityMappingType entityDescriptor, + PersistentClass persistentClass, Function temporaryTableNameAdjuster, + TemporaryTableKind temporaryTableKind, Dialect dialect, RuntimeModelCreationContext runtimeModelCreationContext) { return new TemporaryTable( - entityDescriptor, + persistentClass, temporaryTableNameAdjuster, + temporaryTableKind, dialect, runtimeModelCreationContext, temporaryTable -> { + final MetadataImplementor metadata = runtimeModelCreationContext.getMetadata(); final List columns = new ArrayList<>(); - final PersistentClass entityBinding = runtimeModelCreationContext.getBootModel() - .getEntityBinding( entityDescriptor.getEntityName() ); - - final Generator identifierGenerator = entityDescriptor.getEntityPersister().getGenerator(); - final boolean identityColumn = identifierGenerator.generatedOnExecution(); - final boolean hasOptimizer; + final List rootKeyColumns = persistentClass.getRootClass().getKey().getColumns(); + final boolean identityColumn = rootKeyColumns.size() == 1 && rootKeyColumns.get( 0 ).isIdentity(); + final boolean isExternallyGenerated; if ( identityColumn ) { - hasOptimizer = false; - final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - int idIdx = 0; - for ( Column column : entityBinding.getKey().getColumns() ) { - final JdbcMapping jdbcMapping = identifierMapping.getJdbcMapping( idIdx++ ); + isExternallyGenerated = false; + for ( Column column : persistentClass.getKey().getColumns() ) { String sqlTypeName = ""; if ( dialect.getIdentityColumnSupport().hasDataTypeInIdentityColumn() ) { - sqlTypeName = column.getSqlType( runtimeModelCreationContext.getMetadata() ) + " "; + sqlTypeName = column.getSqlType( metadata ) + " "; } - sqlTypeName = sqlTypeName + dialect.getIdentityColumnSupport().getIdentityColumnString( column.getSqlTypeCode( runtimeModelCreationContext.getMetadata() ) ); + sqlTypeName = sqlTypeName + dialect.getIdentityColumnSupport() + .getIdentityColumnString( column.getSqlTypeCode( metadata ) ); columns.add( new TemporaryTableColumn( temporaryTable, ENTITY_TABLE_IDENTITY_COLUMN, - jdbcMapping, + column.getType(), sqlTypeName, - column.getColumnSize( - dialect, - runtimeModelCreationContext.getMetadata() - ), + column.getColumnSize( dialect, metadata ), // Always report as nullable as the identity column string usually includes the not null constraint true,//column.isNullable() true @@ -339,94 +295,68 @@ public static TemporaryTable createEntityTable( } } else { - if ( identifierGenerator instanceof OptimizableGenerator optimizableGenerator ) { - final Optimizer optimizer = optimizableGenerator.getOptimizer(); - hasOptimizer = optimizer != null && optimizer.getIncrementSize() > 1; - } - else { - hasOptimizer = false; - } + // This is a bit fishy, because for the generator to exist in this map, + // the EntityPersister already has to be built. Currently, we have + // no other way to understand what generators do until we have a boot + // model representation of the generator information, so this will have + // to do + final Generator identifierGenerator = runtimeModelCreationContext.getGenerators() + .get( persistentClass.getRootClass().getEntityName() ); + assert identifierGenerator != null; + + isExternallyGenerated = !(identifierGenerator instanceof OnExecutionGenerator generator + && generator.generatedOnExecution()); } - final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - int idIdx = 0; - for ( Column column : entityBinding.getKey().getColumns() ) { - final JdbcMapping jdbcMapping = identifierMapping.getJdbcMapping( idIdx++ ); - columns.add( - new TemporaryTableColumn( - temporaryTable, - column.getText( dialect ), - jdbcMapping, - column.getSqlType( - runtimeModelCreationContext.getMetadata() - ), - column.getColumnSize( - dialect, - runtimeModelCreationContext.getMetadata() - ), - // We have to set the identity column after the root table insert - column.isNullable() || identityColumn || hasOptimizer, - !identityColumn && !hasOptimizer - ) - ); + final Property identifierProperty = persistentClass.getIdentifierProperty(); + final String idName; + if ( identifierProperty != null ) { + idName = identifierProperty.getName(); } + else { + idName = "id"; + } + forEachTemporaryTableColumn( metadata, temporaryTable, idName, persistentClass.getIdentifier(), temporaryTableColumn -> { + columns.add( new TemporaryTableColumn( + temporaryTableColumn.getContainingTable(), + temporaryTableColumn.getColumnName(), + temporaryTableColumn.getJdbcMapping(), + temporaryTableColumn.getSqlTypeDefinition(), + temporaryTableColumn.getSize(), + // We have to set the identity column after the root table insert + identityColumn || isExternallyGenerated, + !identityColumn && !isExternallyGenerated + ) ); + }); - final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping(); - if ( entityBinding.getDiscriminator() != null && !discriminatorMapping.isFormula() ) { - final Column discriminator = entityBinding.getDiscriminator().getColumns().get(0); - columns.add( - new TemporaryTableColumn( - temporaryTable, - discriminator.getText( dialect ), - discriminatorMapping.getJdbcMapping(), - discriminator.getSqlType( - runtimeModelCreationContext.getMetadata() - ), - discriminator.getColumnSize( - dialect, - runtimeModelCreationContext.getMetadata() - ), - // We have to set the identity column after the root table insert - discriminator.isNullable() - ) - ); + final Value discriminator = persistentClass.getDiscriminator(); + if ( discriminator != null && !discriminator.getSelectables().get( 0 ).isFormula() ) { + forEachTemporaryTableColumn( metadata, temporaryTable, "class", discriminator, temporaryTableColumn -> { + columns.add( new TemporaryTableColumn( + temporaryTableColumn.getContainingTable(), + temporaryTableColumn.getColumnName(), + temporaryTableColumn.getJdbcMapping(), + temporaryTableColumn.getSqlTypeDefinition(), + temporaryTableColumn.getSize(), + // We have to set the identity column after the root table insert + discriminator.isNullable() + ) ); + } ); } // Collect all columns for all entity subtype attributes - entityDescriptor.visitSubTypeAttributeMappings( - attribute -> { - if ( !( attribute instanceof PluralAttributeMapping ) ) { - final PersistentClass declaringClass = runtimeModelCreationContext.getBootModel() - .getEntityBinding( attribute.findContainingEntityMapping().getEntityName() ); - final SimpleValue value = (SimpleValue) declaringClass.getProperty( attribute.getAttributeName() ).getValue(); - final Iterator columnIterator = value.getVirtualSelectables().iterator(); - attribute.forEachSelectable( - (columnIndex, selection) -> { - final Selectable selectable = columnIterator.next(); - if ( selectable instanceof Column column ) { - columns.add( - new TemporaryTableColumn( - temporaryTable, - selectable.getText( dialect ), - selection.getJdbcMapping(), - column.getSqlType( - runtimeModelCreationContext.getMetadata() - ), - column.getColumnSize( - dialect, - runtimeModelCreationContext.getMetadata() - ), - // Treat regular temporary table columns as nullable for simplicity - true - ) - ); - } - } - ); - } - } - ); - if ( hasOptimizer ) { - final TypeConfiguration typeConfiguration = runtimeModelCreationContext.getTypeConfiguration(); + for ( Property property : persistentClass.getPropertyClosure() ) { + if ( !property.isSynthetic() ) { + forEachTemporaryTableColumn( + metadata, + temporaryTable, + property.getName(), + property.getValue(), + columns::add + ); + } + } + if ( isExternallyGenerated ) { + final TypeConfiguration typeConfiguration = metadata.getTypeConfiguration(); // We add a special row number column that we can use to identify and join rows final BasicType integerBasicType = typeConfiguration.getBasicTypeForJavaType( Integer.class ); final String rowNumberType; @@ -455,10 +385,10 @@ else if ( dialect.getIdentityColumnSupport().supportsIdentityColumns() ) { ), integerBasicType ) + " " + dialect.getIdentityColumnSupport() - .getIdentityColumnString( integerBasicType.getJdbcType().getDdlTypeCode() ); + .getIdentityColumnString( integerBasicType.getJdbcType().getDdlTypeCode() ); } else { - LOG.multiTableInsertNotAvailable( entityBinding.getEntityName() ); + LOG.multiTableInsertNotAvailable( persistentClass.getEntityName() ); rowNumberType = typeConfiguration.getDdlTypeRegistry().getTypeName( integerBasicType.getJdbcType().getDdlTypeCode(), dialect.getSizeStrategy().resolveSize( @@ -474,7 +404,7 @@ else if ( dialect.getIdentityColumnSupport().supportsIdentityColumns() ) { columns.add( new TemporaryTableColumn( temporaryTable, - "rn_", + ENTITY_ROW_NUMBER_COLUMN, integerBasicType, rowNumberType, Size.nil(), @@ -488,14 +418,122 @@ else if ( dialect.getIdentityColumnSupport().supportsIdentityColumns() ) { ); } - public EntityMappingType getEntityDescriptor() { - return entityDescriptor; + private static void forEachTemporaryTableColumn(Metadata metadata, TemporaryTable temporaryTable, String prefix, Value value, Consumer consumer) { + final Dialect dialect = metadata.getDatabase().getDialect(); + SqmMutationStrategyHelper.forEachSelectableMapping( prefix, value, (columnName, selectable) -> { + consumer.accept( + new TemporaryTableColumn( + temporaryTable, + columnName, + selectable.getType(), + selectable.getSqlType( metadata ), + selectable.getColumnSize( dialect, metadata ), + // Treat regular temporary table columns as nullable for simplicity + true + ) + ); + } ); + } + + public List findTemporaryTableColumns(EntityPersister entityDescriptor, ModelPart modelPart) { + final int offset = determineModelPartStartIndex( entityDescriptor, modelPart ); + if ( offset == -1 ) { + throw new IllegalStateException( "Couldn't find matching temporary table columns for: " + modelPart ); + } + final int end = offset + modelPart.getJdbcTypeCount(); + // Find a matching cte table column and set that at the current index + return getColumns().subList( offset, end ); + } + + private static int determineModelPartStartIndex(EntityPersister entityDescriptor, ModelPart modelPart) { + boolean hasIdentity = entityDescriptor.getGenerator().generatedOnExecution(); + // Entity with an identity column get HTE_IDENTITY as first column in the temporary table that we skip + int offset = hasIdentity ? 1 : 0; + final int idResult = determineIdStartIndex( offset, entityDescriptor, modelPart ); + if ( idResult <= 0 ) { + return -idResult; + } + offset = idResult; + final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping(); + if ( discriminatorMapping != null && discriminatorMapping.hasPhysicalColumn() && !discriminatorMapping.isFormula() ) { + if ( modelPart == discriminatorMapping ) { + return offset; + } + offset += discriminatorMapping.getJdbcTypeCount(); + } + final AttributeMappingsList attributeMappings = entityDescriptor.getAttributeMappings(); + for ( int i = 0; i < attributeMappings.size(); i++ ) { + AttributeMapping attribute = attributeMappings.get( i ); + if ( !( attribute instanceof PluralAttributeMapping ) ) { + final int result = determineModelPartStartIndex( offset, attribute, modelPart ); + if ( result <= 0 ) { + return -result; + } + offset = result; + } + } + return -1; + } + + private static int determineIdStartIndex(int offset, EntityPersister entityDescriptor, ModelPart modelPart) { + final int originalOffset = offset; + do { + final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); + final int result = determineModelPartStartIndex( originalOffset, identifierMapping, modelPart ); + offset = result; + if ( result <= 0 ) { + break; + } + entityDescriptor = (EntityPersister) entityDescriptor.getSuperMappingType(); + } while ( entityDescriptor != null ); + + return offset; + } + + private static int determineModelPartStartIndex(int offset, ModelPart modelPart, ModelPart modelPartToFind) { + if ( modelPart == modelPartToFind ) { + return -offset; + } + if ( modelPart instanceof EntityValuedModelPart entityValuedModelPart ) { + final ModelPart keyPart = + modelPart instanceof Association association + ? association.getForeignKeyDescriptor() + : entityValuedModelPart.getEntityMappingType().getIdentifierMapping(); + return determineModelPartStartIndex( offset, keyPart, modelPartToFind ); + } + else if ( modelPart instanceof EmbeddableValuedModelPart embeddablePart ) { + final AttributeMappingsList attributeMappings = + embeddablePart.getEmbeddableTypeDescriptor().getAttributeMappings(); + for ( int i = 0; i < attributeMappings.size(); i++ ) { + final AttributeMapping mapping = attributeMappings.get( i ); + final int result = determineModelPartStartIndex( offset, mapping, modelPartToFind ); + if ( result <= 0 ) { + return result; + } + offset = result; + } + return offset; + } + else if ( modelPart instanceof BasicValuedModelPart basicModelPart ) { + return offset + (basicModelPart.isInsertable() ? modelPart.getJdbcTypeCount() : 0); + } + return offset + modelPart.getJdbcTypeCount(); + } + + public boolean isRowNumberGenerated() { + // Only assign a value for the rowNumber column if it isn't using an identity insert + return !dialect.supportsWindowFunctions() + && dialect.getIdentityColumnSupport().supportsIdentityColumns(); } public String getQualifiedTableName() { return qualifiedTableName; } + public TemporaryTableKind getTemporaryTableKind() { + return temporaryTableKind; + } + public List getColumns() { return columns; } @@ -514,7 +552,7 @@ public String getTableExpression() { @Override public String getContributor() { - return entityDescriptor.getContributor(); + return contributor; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTableHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTableHelper.java index 2d8ab178515d..bff3a62d126c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTableHelper.java @@ -20,6 +20,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.jdbc.AbstractReturningWork; import org.hibernate.jdbc.AbstractWork; /** @@ -33,7 +34,7 @@ public class TemporaryTableHelper { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Creation - public static class TemporaryTableCreationWork extends AbstractWork { + public static class TemporaryTableCreationWork extends AbstractReturningWork { private final TemporaryTable temporaryTable; private final TemporaryTableExporter exporter; private final SessionFactoryImplementor sessionFactory; @@ -58,7 +59,7 @@ public TemporaryTableCreationWork( } @Override - public void execute(Connection connection) { + public Boolean execute(Connection connection) { final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); try { @@ -68,6 +69,7 @@ public void execute(Connection connection) { try (Statement statement = connection.createStatement()) { statement.executeUpdate( creationCommand ); jdbcServices.getSqlExceptionHelper().handleAndClearWarnings( statement, WARNING_HANDLER ); + return Boolean.TRUE; } catch (SQLException e) { log.debugf( @@ -81,6 +83,7 @@ public void execute(Connection connection) { catch( Exception e ) { log.debugf( "Error creating temporary table(s) : %s", e.getMessage() ); } + return Boolean.FALSE; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTableStrategy.java new file mode 100644 index 000000000000..22af2dce0179 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TemporaryTableStrategy.java @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; +import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; + +/** + * Defines how to interact with a certain temporary table kind. + * + * @since 7.1 + */ +public interface TemporaryTableStrategy { + + /** + * Returns an adjusted table name that can be used for temporary tables. + */ + String adjustTemporaryTableName(String desiredTableName); + + /** + * The kind of temporary tables that are supported on this database. + */ + TemporaryTableKind getTemporaryTableKind(); + + /** + * An arbitrary SQL fragment appended to the end of the statement to + * create a temporary table, specifying dialect-specific options, or + * {@code null} if there are no options to specify. + */ + @Nullable String getTemporaryTableCreateOptions(); + + /** + * The command to create a temporary table. + */ + String getTemporaryTableCreateCommand(); + + /** + * The command to drop a temporary table. + */ + String getTemporaryTableDropCommand(); + + /** + * The command to truncate a temporary table. + */ + String getTemporaryTableTruncateCommand(); + + /** + * Annotation to be appended to the end of each COLUMN clause for temporary tables. + * + * @param sqlTypeCode The SQL type code + * @return The annotation to be appended, for example, {@code COLLATE DATABASE_DEFAULT} in SQL Server + */ + String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode); + + /** + * The action to take after finishing use of a temporary table. + */ + AfterUseAction getTemporaryTableAfterUseAction(); + + /** + * The action to take before beginning use of a temporary table. + */ + BeforeUseAction getTemporaryTableBeforeUseAction(); + + /** + * Does this database support primary keys for temporary tables for this strategy? + * + * @return true by default, since most do + */ + default boolean supportsTemporaryTablePrimaryKey() { + return true; + } + + /** + * Does this database support null constraints for temporary table columns for this strategy? + * + * @return true by default, since most do + */ + default boolean supportsTemporaryTableNullConstraint() { + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TransactSQLLocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TransactSQLLocalTemporaryTableStrategy.java new file mode 100644 index 000000000000..65d13b5dd7e2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/temptable/TransactSQLLocalTemporaryTableStrategy.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.dialect.temptable; + +import org.hibernate.query.sqm.mutation.spi.AfterUseAction; + +/** + * Transact-SQL specific local temporary table strategy. + */ +public class TransactSQLLocalTemporaryTableStrategy extends StandardLocalTemporaryTableStrategy { + + public static final TransactSQLLocalTemporaryTableStrategy INSTANCE = new TransactSQLLocalTemporaryTableStrategy(); + + @Override + public String adjustTemporaryTableName(String desiredTableName) { + return '#' + desiredTableName; + } + + @Override + public String getTemporaryTableCreateCommand() { + return "create table"; + } + + @Override + public AfterUseAction getTemporaryTableAfterUseAction() { + // sql-server, at least needed this dropped after use; strange! + return AfterUseAction.DROP; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java index 2f7c3a2503ae..85bd2f068ba4 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/id/insert/AbstractReturningDelegate.java @@ -62,6 +62,8 @@ public GeneratedValues performMutation( @Override public final GeneratedValues performInsertReturning(String sql, SharedSessionContractImplementor session, Binder binder) { + session.getJdbcServices().getSqlStatementLogger().logStatement( sql ); + try { // prepare and execute the insert final var insert = prepareStatement( sql, session ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeModelCreationContext.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeModelCreationContext.java index 318eeec63878..d05bc5a22aad 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeModelCreationContext.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/RuntimeModelCreationContext.java @@ -37,7 +37,8 @@ public interface RuntimeModelCreationContext { MappingMetamodelImplementor getDomainModel(); default TypeConfiguration getTypeConfiguration() { - return getBootstrapContext().getTypeConfiguration(); + return getBootstrapContext() == null ? getSessionFactory().getTypeConfiguration() + : getBootstrapContext().getTypeConfiguration(); } default JavaTypeRegistry getJavaTypeRegistry() { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 58f1959ccc4e..cb3d8a6ac61f 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -4823,7 +4823,8 @@ private void inheritSupertypeSpecialAttributeMappings() { } private void prepareMultiTableMutationStrategy(MappingModelCreationProcess creationProcess) { - if ( hasMultipleTables() ) { + // No need for multi-table mutation strategy for subselect entity since update/delete don't make sense + if ( !isSubselect() && hasMultipleTables() ) { creationProcess.registerInitializationCallback( "Entity(" + getEntityName() + ") `sqmMultiTableMutationStrategy` interpretation", () -> { @@ -4842,7 +4843,8 @@ private void prepareMultiTableMutationStrategy(MappingModelCreationProcess creat } private void prepareMultiTableInsertStrategy(MappingModelCreationProcess creationProcess) { - if ( hasMultipleTables() || generatorNeedsMultiTableInsert() ) { + // No need for multi-table insert strategy for subselect entity since insert doesn't make sense + if ( !isSubselect() && ( hasMultipleTables() || generatorNeedsMultiTableInsert() ) ) { creationProcess.registerInitializationCallback( "Entity(" + getEntityName() + ") `sqmMultiTableInsertStrategy` interpretation", () -> { @@ -4860,6 +4862,11 @@ private void prepareMultiTableInsertStrategy(MappingModelCreationProcess creatio } } + private boolean isSubselect() { + // For the lack of a + return getRootTableName().charAt( 0 ) == '('; + } + private boolean generatorNeedsMultiTableInsert() { final Generator generator = getGenerator(); if ( generator instanceof BulkInsertionCapableIdentifierGenerator diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java index 73ff0f42b562..24d2089a780e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngineOptions.java @@ -7,6 +7,8 @@ import java.util.Map; import org.hibernate.jpa.spi.JpaCompliance; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.criteria.ValueHandlingMode; import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; @@ -75,6 +77,22 @@ public interface QueryEngineOptions { */ SqmMultiTableInsertStrategy getCustomSqmMultiTableInsertStrategy(); + /** + * Contract for handling SQM trees representing mutation (UPDATE or DELETE) queries + * where the target of the mutation is a multi-table entity. + * + * @see org.hibernate.cfg.QuerySettings#QUERY_MULTI_TABLE_MUTATION_STRATEGY + */ + SqmMultiTableMutationStrategy resolveCustomSqmMultiTableMutationStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext creationContext); + + /** + * Contract for handling SQM trees representing insertion (INSERT) queries where the + * target of the mutation is a multi-table entity. + * + * @see org.hibernate.cfg.QuerySettings#QUERY_MULTI_TABLE_INSERT_STRATEGY + */ + SqmMultiTableInsertStrategy resolveCustomSqmMultiTableInsertStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext creationContext); + /** * @see org.hibernate.cfg.JpaComplianceSettings */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMultiTableMutationStrategyProviderStandard.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMultiTableMutationStrategyProviderStandard.java index 2b39ce021d1b..49d39c559633 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMultiTableMutationStrategyProviderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMultiTableMutationStrategyProviderStandard.java @@ -32,7 +32,11 @@ public SqmMultiTableMutationStrategy createMutationStrategy( if ( specifiedStrategy != null ) { return specifiedStrategy; } - + final SqmMultiTableMutationStrategy specifiedEntityBaseStrategy = + options.resolveCustomSqmMultiTableMutationStrategy( rootEntityDescriptor, creationContext ); + if ( specifiedEntityBaseStrategy != null ) { + return specifiedEntityBaseStrategy; + } return creationContext.getDialect().getFallbackSqmMutationStrategy( rootEntityDescriptor, creationContext ); } @@ -47,6 +51,11 @@ public SqmMultiTableInsertStrategy createInsertStrategy( if ( specifiedStrategy != null ) { return specifiedStrategy; } + final SqmMultiTableInsertStrategy specifiedEntityBaseStrategy = + options.resolveCustomSqmMultiTableInsertStrategy( rootEntityDescriptor, creationContext ); + if ( specifiedEntityBaseStrategy != null ) { + return specifiedEntityBaseStrategy; + } return creationContext.getDialect().getFallbackSqmInsertStrategy( rootEntityDescriptor, creationContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java index 01a425698a74..8270e925b2b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmMutationStrategyHelper.java @@ -4,14 +4,44 @@ */ package org.hibernate.query.sqm.mutation.internal; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.mapping.Any; +import org.hibernate.mapping.BasicValue; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.DependantValue; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.OneToOne; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.Selectable; +import org.hibernate.mapping.ToOne; +import org.hibernate.mapping.Value; +import org.hibernate.metamodel.mapping.Association; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.AttributeMappingsList; +import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.EntityValuedModelPart; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.SelectableMapping; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.TableReference; @@ -177,4 +207,161 @@ private static void cleanUpCollectionTable( ); } } + + public static boolean isId(JdbcMappingContainer type) { + return type instanceof EntityIdentifierMapping || type instanceof AttributeMapping attributeMapping + && isPartOfId( attributeMapping ); + } + + public static boolean isPartOfId(AttributeMapping attributeMapping) { + return attributeMapping.getDeclaringType() instanceof EmbeddableMappingType embeddableMappingType + && (embeddableMappingType.getEmbeddedValueMapping().isEntityIdentifierMapping() + || isId( embeddableMappingType.getEmbeddedValueMapping() )); + } + + public static void forEachSelectableMapping(String prefix, ModelPart modelPart, BiConsumer consumer) { + if ( modelPart instanceof BasicValuedModelPart basicModelPart ) { + if ( basicModelPart.isInsertable() ) { + consumer.accept( prefix, basicModelPart ); + } + } + else if ( modelPart instanceof EntityValuedModelPart entityPart ) { + final Association association = (Association) modelPart; + if ( association.getForeignKeyDescriptor() == null ) { + // This is expected to happen when processing a + // PostInitCallbackEntry because the callbacks + // are not ordered. The exception is caught in + // MappingModelCreationProcess.executePostInitCallbacks() + // and the callback is re-queued. + throw new IllegalStateException( "ForeignKeyDescriptor not ready for [" + association.getPartName() + "] on entity: " + modelPart.findContainingEntityMapping().getEntityName() ); + } + if ( association.getSideNature() != ForeignKeyDescriptor.Nature.KEY ) { + // Inverse one-to-one receives no column + return; + } + if ( association instanceof ToOneAttributeMapping toOneMapping ) { + final EntityPersister declaringEntityPersister = toOneMapping.findContainingEntityMapping() + .getEntityPersister(); + final int tableIndex = findTableIndex( + declaringEntityPersister, + toOneMapping.getIdentifyingColumnsTableExpression() + ); + if ( declaringEntityPersister.isInverseTable( tableIndex ) ) { + // Actually, this is like ForeignKeyDescriptor.Nature.TARGET, + // but for some reason it isn't + return; + } + } + final ValuedModelPart targetPart = association.getForeignKeyDescriptor().getTargetPart(); + final String newPrefix = targetPart instanceof AttributeMapping attributeMapping + ? prefix + "_" + attributeMapping.getAttributeName() + : prefix; + forEachSelectableMapping( + newPrefix, + association.getForeignKeyDescriptor().getKeyPart(), + consumer + ); + } + else if ( modelPart instanceof DiscriminatedAssociationModelPart discriminatedPart ) { + final String newPrefix = prefix + "_" + discriminatedPart.getPartName() + "_"; + forEachSelectableMapping( + newPrefix + "discriminator", + discriminatedPart.getDiscriminatorPart(), + consumer + ); + forEachSelectableMapping( + newPrefix + "key", + discriminatedPart.getKeyPart(), + consumer + ); + } + else { + final EmbeddableValuedModelPart embeddablePart = ( EmbeddableValuedModelPart ) modelPart; + final AttributeMappingsList attributeMappings = embeddablePart.getEmbeddableTypeDescriptor().getAttributeMappings(); + if ( attributeMappings.size() == 0 ) { + throw new IllegalStateException( "AttributeMappingsList not read yet on embeddable: " + embeddablePart ); + } + for ( int i = 0; i < attributeMappings.size(); i++ ) { + AttributeMapping mapping = attributeMappings.get( i ); + if ( !( mapping instanceof PluralAttributeMapping ) ) { + final String newPrefix = modelPart.isVirtual() ? mapping.getAttributeName() + : prefix + "_" + mapping.getAttributeName(); + forEachSelectableMapping( newPrefix, mapping, consumer ); + } + } + } + } + + public static void forEachSelectableMapping(String prefix, Value value, BiConsumer consumer) { + if ( value instanceof BasicValue || value instanceof Any.MetaValue || value instanceof Any.KeyValue ) { + assert value.getSelectables().size() == 1; + final Selectable selectable = value.getSelectables().get( 0 ); + if ( selectable instanceof Column column && value.isColumnInsertable( 0 ) ) { + consumer.accept( prefix, column ); + } + } + else if ( value instanceof DependantValue dependantValue ) { + forEachSelectableMapping( prefix, dependantValue.getWrappedValue(), consumer ); + } + else if ( value instanceof ToOne toOne ) { + if ( toOne instanceof OneToOne oneToOne && oneToOne.getMappedByProperty() != null ) { + // Inverse to-one receives no column + return; + } + final PersistentClass targetEntity = toOne.getBuildingContext().getMetadataCollector() + .getEntityBinding( toOne.getReferencedEntityName() ); + final String targetAttributeName; + final Value targetValue; + if ( toOne.isReferenceToPrimaryKey() ) { + targetValue = targetEntity.getIdentifier(); + targetAttributeName = targetEntity.getIdentifierProperty() != null + ? targetEntity.getIdentifierProperty().getName() : "id"; + } + else { + targetAttributeName = toOne.getReferencedPropertyName(); + targetValue = targetEntity.getReferencedProperty( toOne.getReferencedPropertyName() ).getValue(); + } + forEachSelectableMapping( + prefix + "_" + targetAttributeName, + targetValue, + consumer + ); + } + else if ( value instanceof Any any ) { + forEachSelectableMapping( + prefix + "_discriminator", + any.getDiscriminatorDescriptor() != null ? any.getDiscriminatorDescriptor() : any.getMetaMapping(), + consumer + ); + forEachSelectableMapping( + prefix + "_key", + any.getKeyDescriptor() != null ? any.getKeyDescriptor() : any.getKeyMapping(), + consumer + ); + } + else if ( value instanceof Component component) { + final int propertySpan = component.getPropertySpan(); + for ( int i = 0; i < propertySpan; i++ ) { + final Property property = component.getProperty( i ); + final String newPrefix = component.isEmbedded() ? property.getName() : prefix + "_" + property.getName(); + forEachSelectableMapping( newPrefix, property.getValue(), consumer ); + } + } + else if ( value instanceof OneToMany || value instanceof Collection ) { + // No-op + } + else { + throw new UnsupportedOperationException( "Unsupported value type: " + value.getClass() ); + } + } + + private static int findTableIndex(EntityPersister declaringEntityPersister, String tableExpression) { + final String[] tableNames = declaringEntityPersister.getTableNames(); + for ( int i = 0; i < tableNames.length; i++ ) { + if ( tableExpression.equals( tableNames[i] ) ) { + return i; + } + } + throw new IllegalStateException( "Couldn't find table index for [" + tableExpression + "] in: " + declaringEntityPersister.getEntityName() ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java index ac673b6c8d41..7a0fb736a600 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java @@ -4,26 +4,17 @@ */ package org.hibernate.query.sqm.mutation.internal.cte; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - import org.hibernate.boot.model.naming.Identifier; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.generator.Generator; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.enhanced.Optimizer; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.Stack; import org.hibernate.metamodel.mapping.BasicValuedMapping; -import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.SqlExpressible; @@ -42,6 +33,7 @@ import org.hibernate.query.sqm.mutation.internal.InsertHandler; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.SqmInsertStrategyHelper; +import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; @@ -104,9 +96,17 @@ import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; -import org.hibernate.generator.Generator; import org.hibernate.type.BasicType; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + /** * * @author Christian Beikov @@ -200,16 +200,7 @@ public int execute(DomainQueryExecutionContext executionContext) { final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = sqmConverter.visitInsertionTargetPaths( (assignable, columnReferences) -> { final SqmPathInterpretation pathInterpretation = (SqmPathInterpretation) assignable; - final int offset = CteTable.determineModelPartStartIndex( - entityDescriptor, - pathInterpretation.getExpressionType() - ); - if ( offset == -1 ) { - throw new IllegalStateException( "Couldn't find matching cte column for: " + ( (Expression) assignable ).getExpressionType() ); - } - final int end = offset + pathInterpretation.getExpressionType().getJdbcTypeCount(); - // Find a matching cte table column and set that at the current index - final List columns = cteTable.getCteColumns().subList( offset, end ); + final List columns = cteTable.findCteColumns( pathInterpretation.getExpressionType() ); targetPathCteColumns.addAll( columns ); targetPathColumns.add( new AbstractMap.SimpleEntry<>( @@ -294,6 +285,17 @@ public int execute(DomainQueryExecutionContext executionContext) { ); } } + if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + 0, + SqmInsertStrategyHelper.createRowNumberingExpression( + querySpec, + sessionFactory + ) + ) + ); + } final ValuesTableGroup valuesTableGroup = new ValuesTableGroup( navigablePath, entityDescriptor.getEntityPersister(), @@ -706,8 +708,7 @@ protected String addDmlCtes( final ConflictClause conflictClause = sqmConverter.visitConflictClause( sqmStatement.getConflictClause() ); final int tableSpan = persister.getTableSpan(); - final String[] rootKeyColumns = persister.getKeyColumns( 0 ); - final List keyCteColumns = queryCte.getCteTable().getCteColumns().subList( 0, rootKeyColumns.length ); + final List keyCteColumns = queryCte.getCteTable().findCteColumns( persister.getIdentifierMapping() ); for ( int tableIndex = 0; tableIndex < tableSpan; tableIndex++ ) { final String tableExpression = persister.getTableName( tableIndex ); final TableReference updatingTableReference = updatingTableGroup.getTableReference( @@ -918,7 +919,7 @@ protected String addDmlCtes( new SqlSelectionImpl( new ColumnReference( "e", - rootKeyColumns[j], + keyCteColumns.get( j ).getColumnExpression(), false, null, null @@ -938,7 +939,7 @@ protected String addDmlCtes( for ( Map.Entry, Assignment> entry : assignmentList ) { final Assignment assignment = entry.getValue(); // Skip the id mapping here as we handled that already - if ( assignment.getAssignedValue().getExpressionType() instanceof EntityIdentifierMapping ) { + if ( SqmMutationStrategyHelper.isId( assignment.getAssignedValue().getExpressionType() ) ) { continue; } final List assignmentReferences = assignment.getAssignable().getColumnReferences(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertStrategy.java index 4242fc4a1296..f8c461586b17 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertStrategy.java @@ -8,6 +8,8 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.SingleTableSubclass; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; @@ -126,23 +128,26 @@ public CteInsertStrategy( ); } - // The table name might be a sub-query, which is inappropriate for a temporary table name - final String originalTableName = rootDescriptor.getEntityPersister().getSynchronizedQuerySpaces()[0]; - final String name; - if ( Identifier.isQuoted( originalTableName ) ) { - name = dialect.quote( TemporaryTable.ENTITY_TABLE_PREFIX + Identifier.unQuote( originalTableName ) ); - } - else { - name = TemporaryTable.ENTITY_TABLE_PREFIX + originalTableName; - } - final String qualifiedTableName; - if ( name.length() > dialect.getMaxIdentifierLength() ) { - qualifiedTableName = name.substring( 0, dialect.getMaxIdentifierLength() ); + final PersistentClass persistentClass = runtimeModelCreationContext.getMetadata() + .getEntityBinding( rootDescriptor.getEntityName() ); + final Identifier tableNameIdentifier; + if ( persistentClass instanceof SingleTableSubclass ) { + // In this case, the descriptor is a subclass of a single table inheritance. + // To avoid name collisions, we suffix the table name with the subclass number + tableNameIdentifier = new Identifier( + persistentClass.getTable().getNameIdentifier().getText() + persistentClass.getSubclassId(), + persistentClass.getTable().getNameIdentifier().isQuoted() + ); } else { - qualifiedTableName = name; + tableNameIdentifier = persistentClass.getTable().getNameIdentifier(); } - this.entityCteTable = CteTable.createEntityTable( qualifiedTableName, rootDescriptor ); + final String cteName = TemporaryTable.ENTITY_TABLE_PREFIX + tableNameIdentifier.getText(); + final String qualifiedCteName = new Identifier( + cteName.substring( 0, Math.min( dialect.getMaxIdentifierLength(), cteName.length() ) ), + tableNameIdentifier.isQuoted() + ).render( dialect ); + this.entityCteTable = CteTable.createEntityTable( qualifiedCteName, persistentClass ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteMutationStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteMutationStrategy.java index 42e813ad61c9..11727bdb2e68 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteMutationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteMutationStrategy.java @@ -83,7 +83,8 @@ public CteMutationStrategy( ); } - this.idCteTable = CteTable.createIdTable( ID_TABLE_NAME, rootDescriptor ); + this.idCteTable = CteTable.createIdTable( ID_TABLE_NAME, + runtimeModelCreationContext.getMetadata().getEntityBinding( rootDescriptor.getEntityName() ) ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/AbstractDeleteExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/AbstractDeleteExecutionDelegate.java index 539f698fd099..20e27d00055d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/AbstractDeleteExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/AbstractDeleteExecutionDelegate.java @@ -7,6 +7,7 @@ import java.util.function.Function; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -24,7 +25,8 @@ public abstract class AbstractDeleteExecutionDelegate implements TableBasedDeleteHandler.ExecutionDelegate { private final EntityMappingType entityDescriptor; private final TemporaryTable idTable; - private final AfterUseAction afterUseAction; + private final TemporaryTableStrategy temporaryTableStrategy; + private final boolean forceDropAfterUse; private final SqmDeleteStatement sqmDelete; private final DomainParameterXref domainParameterXref; private final SessionFactoryImplementor sessionFactory; @@ -35,7 +37,8 @@ public abstract class AbstractDeleteExecutionDelegate implements TableBasedDelet public AbstractDeleteExecutionDelegate( EntityMappingType entityDescriptor, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, QueryOptions queryOptions, @@ -45,7 +48,8 @@ public AbstractDeleteExecutionDelegate( SessionFactoryImplementor sessionFactory) { this.entityDescriptor = entityDescriptor; this.idTable = idTable; - this.afterUseAction = afterUseAction; + this.temporaryTableStrategy = temporaryTableStrategy; + this.forceDropAfterUse = forceDropAfterUse; this.sqmDelete = sqmDelete; this.domainParameterXref = domainParameterXref; this.sessionFactory = sessionFactory; @@ -71,8 +75,12 @@ public TemporaryTable getIdTable() { return idTable; } + public TemporaryTableStrategy getTemporaryTableStrategy() { + return temporaryTableStrategy; + } + public AfterUseAction getAfterUseAction() { - return afterUseAction; + return forceDropAfterUse ? AfterUseAction.DROP : temporaryTableStrategy.getTemporaryTableAfterUseAction(); } public SqmDeleteStatement getSqmDelete() { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java index 8459f100879d..1908c5b2a7d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java @@ -12,7 +12,10 @@ import org.hibernate.dialect.temptable.TemporaryTableHelper; import org.hibernate.dialect.temptable.TemporaryTableHelper.TemporaryTableCreationWork; import org.hibernate.dialect.temptable.TemporaryTableHelper.TemporaryTableDropWork; +import org.hibernate.dialect.temptable.TemporaryTableSessionUidColumn; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.BasicValuedMapping; @@ -23,6 +26,7 @@ import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.mutation.spi.BeforeUseAction; import org.hibernate.spi.NavigablePath; +import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.QueryLiteral; @@ -39,6 +43,9 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.internal.SqlSelectionImpl; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.UUID; import java.util.function.Function; @@ -198,7 +205,7 @@ public static QuerySpec createIdTableSelectQuerySpec( querySpec.getFromClause().addRoot( idTableGroup ); - applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart ); + applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart, entityDescriptor ); applyIdTableRestrictions( querySpec, idTableReference, idTable, sessionUidAccess, executionContext ); return querySpec; @@ -208,9 +215,10 @@ private static void applyIdTableSelections( QuerySpec querySpec, TableReference tableReference, TemporaryTable idTable, - ModelPart fkModelPart) { + ModelPart fkModelPart, + EntityMappingType entityDescriptor) { if ( fkModelPart == null ) { - final int size = idTable.getEntityDescriptor().getIdentifierMapping().getJdbcTypeCount(); + final int size = entityDescriptor.getIdentifierMapping().getJdbcTypeCount(); for ( int i = 0; i < size; i++ ) { final var temporaryTableColumn = idTable.getColumns().get( i ); if ( temporaryTableColumn != idTable.getSessionUidColumn() ) { @@ -275,26 +283,114 @@ private static void applyIdTableRestrictions( } } + @Deprecated(forRemoval = true, since = "7.1") public static void performBeforeTemporaryTableUseActions( TemporaryTable temporaryTable, ExecutionContext executionContext) { + performBeforeTemporaryTableUseActions( + temporaryTable, + executionContext.getSession().getDialect().getTemporaryTableBeforeUseAction(), + executionContext + ); + } + + public static boolean performBeforeTemporaryTableUseActions( + TemporaryTable temporaryTable, + TemporaryTableStrategy temporaryTableStrategy, + ExecutionContext executionContext) { + return performBeforeTemporaryTableUseActions( + temporaryTable, + temporaryTableStrategy.getTemporaryTableBeforeUseAction(), + executionContext + ); + } + + private static boolean performBeforeTemporaryTableUseActions( + TemporaryTable temporaryTable, + BeforeUseAction beforeUseAction, + ExecutionContext executionContext) { final var factory = executionContext.getSession().getFactory(); final Dialect dialect = factory.getJdbcServices().getDialect(); - if ( dialect.getTemporaryTableBeforeUseAction() == BeforeUseAction.CREATE ) { + if ( beforeUseAction == BeforeUseAction.CREATE ) { final var temporaryTableCreationWork = new TemporaryTableCreationWork( temporaryTable, factory ); final var ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling(); if ( ddlTransactionHandling == NONE ) { - executionContext.getSession().doWork( temporaryTableCreationWork ); + return executionContext.getSession().doReturningWork( temporaryTableCreationWork ); } else { final var isolationDelegate = executionContext.getSession().getJdbcCoordinator().getJdbcSessionOwner() .getTransactionCoordinator().createIsolationDelegate(); - isolationDelegate.delegateWork( temporaryTableCreationWork, + return isolationDelegate.delegateWork( temporaryTableCreationWork, ddlTransactionHandling == ISOLATE_AND_TRANSACT ); } } + else { + return false; + } + } + + public static int[] loadInsertedRowNumbers( + TemporaryTable temporaryTable, + Function sessionUidAccess, + int rows, + ExecutionContext executionContext) { + final TemporaryTableSessionUidColumn sessionUidColumn = temporaryTable.getSessionUidColumn(); + + final TemporaryTableColumn rowNumberColumn = temporaryTable.getColumns() + .get( temporaryTable.getColumns().size() - (sessionUidColumn == null ? 1 : 2 ) ); + assert rowNumberColumn != null; + + final SharedSessionContractImplementor session = executionContext.getSession(); + final SimpleSelect simpleSelect = new SimpleSelect( session.getFactory() ) + .setTableName( temporaryTable.getQualifiedTableName() ) + .addColumn( rowNumberColumn.getColumnName() ); + if ( sessionUidColumn != null ) { + simpleSelect.addRestriction( sessionUidColumn.getColumnName() ); + } + final String sqlSelect = simpleSelect.toStatementString(); + + final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator(); + PreparedStatement preparedStatement = null; + try { + preparedStatement = jdbcCoordinator.getStatementPreparer().prepareStatement( sqlSelect ); + if ( sessionUidColumn != null ) { + //noinspection unchecked + sessionUidColumn.getJdbcMapping().getJdbcValueBinder().bind( + preparedStatement, + UUID.fromString( sessionUidAccess.apply( session ) ), + 1, + session + ); + } + final ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( preparedStatement, sqlSelect ); + final int[] rowNumbers = new int[rows]; + try { + int rowIndex = 0; + while (resultSet.next()) { + rowNumbers[rowIndex++] = resultSet.getInt( 1 ); + } + return rowNumbers; + } + catch ( IndexOutOfBoundsException e ) { + throw new IllegalArgumentException( "Expected " + rows + " to be inserted but found more", e ); + } + } + catch( SQLException ex ) { + throw new IllegalStateException( ex ); + } + finally { + if ( preparedStatement != null ) { + try { + jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( preparedStatement ); + } + catch( Throwable ignore ) { + // ignore + } + jdbcCoordinator.afterStatementExecution(); + } + } } public static void performAfterTemporaryTableUseActions( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableInsertStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableInsertStrategy.java index 6283932b4b10..5be44881bd5d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableInsertStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableInsertStrategy.java @@ -5,7 +5,11 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; @@ -18,6 +22,31 @@ */ public class GlobalTemporaryTableInsertStrategy extends GlobalTemporaryTableStrategy implements SqmMultiTableInsertStrategy { + public GlobalTemporaryTableInsertStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { + this( + rootEntityDescriptor, + requireGlobalTemporaryTableStrategy( runtimeModelCreationContext.getDialect() ), + runtimeModelCreationContext + ); + } + + private GlobalTemporaryTableInsertStrategy( + EntityMappingType rootEntityDescriptor, + TemporaryTableStrategy temporaryTableStrategy, + RuntimeModelCreationContext runtimeModelCreationContext) { + this( + TemporaryTable.createEntityTable( + runtimeModelCreationContext.getMetadata() + .getEntityBinding( rootEntityDescriptor.getEntityName() ), + basename -> temporaryTableStrategy.adjustTemporaryTableName( TemporaryTable.ENTITY_TABLE_PREFIX + basename ), + TemporaryTableKind.GLOBAL, + runtimeModelCreationContext.getDialect(), + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + public GlobalTemporaryTableInsertStrategy( TemporaryTable entityTable, SessionFactoryImplementor sessionFactory) { @@ -33,7 +62,8 @@ public int executeInsert( sqmInsertStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, // generally a global temp table should already track a Connection-specific uid, // but just in case a particular env needs it... session -> session.getSessionIdentifier().toString(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableMutationStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableMutationStrategy.java index b0aa56938da6..0f9c136edebc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableMutationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableMutationStrategy.java @@ -5,7 +5,11 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; @@ -19,6 +23,31 @@ */ public class GlobalTemporaryTableMutationStrategy extends GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrategy { + public GlobalTemporaryTableMutationStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { + this( + rootEntityDescriptor, + requireGlobalTemporaryTableStrategy( runtimeModelCreationContext.getDialect() ), + runtimeModelCreationContext + ); + } + + private GlobalTemporaryTableMutationStrategy( + EntityMappingType rootEntityDescriptor, + TemporaryTableStrategy temporaryTableStrategy, + RuntimeModelCreationContext runtimeModelCreationContext) { + this( + TemporaryTable.createIdTable( + runtimeModelCreationContext.getBootModel() + .getEntityBinding( rootEntityDescriptor.getEntityName() ), + basename -> temporaryTableStrategy.adjustTemporaryTableName( TemporaryTable.ID_TABLE_PREFIX + basename ), + TemporaryTableKind.GLOBAL, + runtimeModelCreationContext.getDialect(), + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + public GlobalTemporaryTableMutationStrategy( TemporaryTable idTable, SessionFactoryImplementor sessionFactory) { @@ -34,7 +63,8 @@ public int executeUpdate( sqmUpdate, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, // generally a global temp table should already track a Connection-specific uid, // but just in case a particular env needs it... session -> session.getSessionIdentifier().toString(), @@ -51,7 +81,8 @@ public int executeDelete( sqmDelete, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, // generally a global temp table should already track a Connection-specific uid, // but just in case a particular env needs it... session -> session.getSessionIdentifier().toString(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableStrategy.java index c0dbf6734bac..c3734f794558 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/GlobalTemporaryTableStrategy.java @@ -6,14 +6,16 @@ import java.sql.Connection; import java.sql.SQLException; +import java.util.Objects; +import org.hibernate.dialect.Dialect; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableHelper; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.query.sqm.mutation.spi.AfterUseAction; @@ -21,6 +23,8 @@ import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER; +import static org.hibernate.internal.util.NullnessUtil.castNonNull; + /** * Strategy based on ANSI SQL's definition of a "global temporary table". * @@ -43,15 +47,21 @@ public class GlobalTemporaryTableStrategy { public GlobalTemporaryTableStrategy(TemporaryTable temporaryTable, SessionFactoryImplementor sessionFactory) { this.temporaryTable = temporaryTable; this.sessionFactory = sessionFactory; + final TemporaryTableStrategy temporaryTableStrategy = requireGlobalTemporaryTableStrategy( sessionFactory.getJdbcServices().getDialect() ); - if ( sessionFactory.getJdbcServices().getDialect().getTemporaryTableAfterUseAction() == AfterUseAction.DROP ) { + if ( temporaryTableStrategy.getTemporaryTableAfterUseAction() == AfterUseAction.DROP ) { throw new IllegalArgumentException( "Global-temp ID tables cannot use AfterUseAction.DROP : " + temporaryTable.getTableExpression() ); } } - public EntityMappingType getEntityDescriptor() { - return temporaryTable.getEntityDescriptor(); + protected static TemporaryTableStrategy requireGlobalTemporaryTableStrategy(Dialect dialect) { + return Objects.requireNonNull( dialect.getGlobalTemporaryTableStrategy(), + "Dialect does not define a global temporary table strategy: " + dialect.getClass().getSimpleName() ); + } + + public TemporaryTableStrategy getTemporaryTableStrategy() { + return castNonNull( sessionFactory.getJdbcServices().getDialect().getGlobalTemporaryTableStrategy() ); } public void prepare(MappingModelCreationProcess mappingModelCreationProcess, JdbcConnectionAccess connectionAccess) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java index 915a21405563..5229921cac90 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java @@ -13,9 +13,11 @@ import java.util.Map; import java.util.UUID; import java.util.function.Function; +import java.util.stream.IntStream; import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableColumn; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -32,6 +34,7 @@ import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.MappingModelExpressible; @@ -48,6 +51,7 @@ import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; +import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.JdbcParameter; @@ -86,7 +90,8 @@ */ public class InsertExecutionDelegate implements TableBasedInsertHandler.ExecutionDelegate { private final TemporaryTable entityTable; - private final AfterUseAction afterUseAction; + private final TemporaryTableStrategy temporaryTableStrategy; + private final boolean forceDropAfterUse; private final Function sessionUidAccess; private final TableGroup updatingTableGroup; private final InsertSelectStatement insertStatement; @@ -97,24 +102,28 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio private final JdbcParameterBindings jdbcParameterBindings; private final JdbcParameter sessionUidParameter; - private final Map> assignmentsByTable; + private final boolean assignsId; + private final Map> assignmentsByTable; private final SessionFactoryImplementor sessionFactory; public InsertExecutionDelegate( MultiTableSqmMutationConverter sqmConverter, TemporaryTable entityTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup insertingTableGroup, Map tableReferenceByAlias, List assignments, + boolean assignsId, InsertSelectStatement insertStatement, ConflictClause conflictClause, JdbcParameter sessionUidParameter, DomainQueryExecutionContext executionContext) { this.entityTable = entityTable; - this.afterUseAction = afterUseAction; + this.temporaryTableStrategy = temporaryTableStrategy; + this.forceDropAfterUse = forceDropAfterUse; this.sessionUidAccess = sessionUidAccess; this.updatingTableGroup = insertingTableGroup; this.conflictClause = conflictClause; @@ -165,9 +174,19 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter new ArrayList<>() ) - .add( assignment ); + assignmentsByTable.computeIfAbsent( + assignmentTableReference == null ? null : assignmentTableReference.getTableId(), + k -> new ArrayList<>() ).add( assignment ); } + + this.assignsId = assignsId; + } + + private List getPrimaryKeyTableColumns(EntityPersister entityPersister, TemporaryTable entityTable) { + final boolean identityColumn = entityPersister.getGenerator().generatedOnExecution(); + final int startIndex = identityColumn ? 1 : 0; + final int endIndex = startIndex + entityPersister.getIdentifierMapping().getJdbcTypeCount(); + return entityTable.getColumns().subList( startIndex, endIndex ); } @Override @@ -175,8 +194,9 @@ public int execute(ExecutionContext executionContext) { // NOTE: we could get rid of using a temporary table if the expressions in Values are "stable". // But that is a non-trivial optimization that requires more effort // as we need to split out individual inserts if we have a non-bulk capable optimizer - ExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( + final boolean createdTable = ExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( entityTable, + temporaryTableStrategy, executionContext ); @@ -202,6 +222,7 @@ public int execute(ExecutionContext executionContext) { final int insertedRows = insertRootTable( persister.getTableName( 0 ), rows, + createdTable, persister.getKeyColumns( 0 ), executionContext ); @@ -246,7 +267,7 @@ public int execute(ExecutionContext executionContext) { ExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions( entityTable, sessionUidAccess, - afterUseAction, + forceDropAfterUse ? AfterUseAction.DROP : temporaryTableStrategy.getTemporaryTableAfterUseAction(), executionContext ); } @@ -283,6 +304,7 @@ private NamedTableReference resolveUnionTableReference(TableReference tableRefer private int insertRootTable( String tableExpression, int rows, + boolean rowNumberStartsAtOne, String[] keyColumns, ExecutionContext executionContext) { final TableReference updatingTableReference = updatingTableGroup.getTableReference( @@ -293,7 +315,7 @@ private int insertRootTable( final EntityPersister entityPersister = entityDescriptor.getEntityPersister(); final Generator generator = entityPersister.getGenerator(); - final List assignments = assignmentsByTable.get( updatingTableReference ); + final List assignments = assignmentsByTable.get( tableExpression ); if ( ( assignments == null || assignments.isEmpty() ) && !generator.generatedOnExecution() && ( !( generator instanceof BulkInsertionCapableIdentifierGenerator ) @@ -317,7 +339,7 @@ private int insertRootTable( entityDescriptor ); querySpec.getFromClause().addRoot( temporaryTableGroup ); - if ( insertStatement.getValuesList().size() == 1 ) { + if ( insertStatement.getValuesList().size() == 1 && conflictClause != null ) { // Potentially apply a limit 1 to allow the use of the conflict clause emulation querySpec.setFetchClauseExpression( new QueryLiteral<>( @@ -330,29 +352,11 @@ private int insertRootTable( final InsertSelectStatement insertStatement = new InsertSelectStatement( dmlTableReference ); insertStatement.setConflictClause( conflictClause ); insertStatement.setSourceSelectStatement( querySpec ); - if ( assignments != null ) { - for ( Assignment assignment : assignments ) { - final Assignable assignable = assignment.getAssignable(); - insertStatement.addTargetColumnReferences( assignable.getColumnReferences() ); - for ( ColumnReference columnReference : assignable.getColumnReferences() ) { - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - new ColumnReference( - temporaryTableReference.getIdentificationVariable(), - columnReference.getColumnExpression(), - false, - null, - columnReference.getJdbcMapping() - ) - ) - ); - } - } - } + applyAssignments( assignments, insertStatement, temporaryTableReference ); final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); final Map entityTableToRootIdentity; final SharedSessionContractImplementor session = executionContext.getSession(); - if ( generator.generatedOnExecution() ) { + if ( !assignsId && generator.generatedOnExecution() ) { final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping(); final QuerySpec idSelectQuerySpec = new QuerySpec( true ); @@ -372,6 +376,20 @@ private int insertRootTable( SortDirection.ASCENDING ) ); + if ( entityTable.getSessionUidColumn() != null ) { + final TemporaryTableColumn sessionUidColumn = entityTable.getSessionUidColumn(); + idSelectQuerySpec.applyPredicate( new ComparisonPredicate( + new ColumnReference( + temporaryTableReference, + sessionUidColumn.getColumnName(), + false, + null, + sessionUidColumn.getJdbcMapping() + ), + ComparisonOperator.EQUAL, + sessionUidParameter + ) ); + } final SelectStatement selectStatement = new SelectStatement( idSelectQuerySpec, Collections.singletonList( @@ -392,7 +410,7 @@ private int insertRootTable( .translate( null, executionContext.getQueryOptions() ); final List list = jdbcServices.getJdbcSelectExecutor().list( jdbcSelect, - JdbcParameterBindings.NO_BINDINGS, + jdbcParameterBindings, executionContext, null, null, @@ -417,30 +435,19 @@ private int insertRootTable( // if the target paths don't already contain the id, and we need identifier generation, // then we load update rows from the temporary table with the generated identifiers, // to then insert into the target tables in once statement - if ( needsIdentifierGeneration( generator ) - && insertStatement.getTargetColumns().stream() + if ( insertStatement.getTargetColumns().stream() .noneMatch( c -> keyColumns[0].equals( c.getColumnExpression() ) ) ) { - final BasicEntityIdentifierMapping identifierMapping = - (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping(); - final JdbcParameter rowNumber = new JdbcParameterImpl( identifierMapping.getJdbcMapping() ); - final JdbcParameter rootIdentity = new JdbcParameterImpl( identifierMapping.getJdbcMapping() ); - final List temporaryTableAssignments = new ArrayList<>( 1 ); - final ColumnReference idColumnReference = new ColumnReference( (String) null, identifierMapping ); - temporaryTableAssignments.add( new Assignment( idColumnReference, rootIdentity ) ); - final TemporaryTableColumn rowNumberColumn; + final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); + final List primaryKeyTableColumns = + getPrimaryKeyTableColumns( entityPersister, entityTable ); + final TemporaryTableColumn sessionUidColumn; final Predicate sessionUidPredicate; if ( entityTable.getSessionUidColumn() == null ) { - rowNumberColumn = entityTable.getColumns().get( - entityTable.getColumns().size() - 1 - ); sessionUidColumn = null; sessionUidPredicate = null; } else { - rowNumberColumn = entityTable.getColumns().get( - entityTable.getColumns().size() - 2 - ); sessionUidColumn = entityTable.getSessionUidColumn(); sessionUidPredicate = new ComparisonPredicate( new ColumnReference( @@ -454,97 +461,131 @@ private int insertRootTable( sessionUidParameter ); } - final UpdateStatement updateStatement = new UpdateStatement( - temporaryTableReference, - temporaryTableAssignments, - Predicate.combinePredicates( - new ComparisonPredicate( - new ColumnReference( - (String) null, - rowNumberColumn.getColumnName(), - false, - null, - rowNumberColumn.getJdbcMapping() + if ( needsIdentifierGeneration( generator ) ) { + final BasicEntityIdentifierMapping basicIdentifierMapping = (BasicEntityIdentifierMapping) identifierMapping; + final JdbcParameter rootIdentity = new JdbcParameterImpl( basicIdentifierMapping.getJdbcMapping() ); + final List temporaryTableAssignments = new ArrayList<>( 1 ); + final ColumnReference idColumnReference = new ColumnReference( (String) null, basicIdentifierMapping ); + temporaryTableAssignments.add( new Assignment( idColumnReference, rootIdentity ) ); + + final JdbcParameter rowNumber = new JdbcParameterImpl( basicIdentifierMapping.getJdbcMapping() ); + final int rowNumberIndex = + entityTable.getColumns().size() - (entityTable.getSessionUidColumn() == null ? 1 : 2); + final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get( rowNumberIndex ); + + final UpdateStatement updateStatement = new UpdateStatement( + temporaryTableReference, + temporaryTableAssignments, + Predicate.combinePredicates( + new ComparisonPredicate( + new ColumnReference( + (String) null, + rowNumberColumn.getColumnName(), + false, + null, + rowNumberColumn.getJdbcMapping() + ), + ComparisonOperator.EQUAL, + rowNumber ), - ComparisonOperator.EQUAL, - rowNumber - ), - sessionUidPredicate - ) - ); - - final JdbcOperationQueryMutation jdbcUpdate = jdbcServices.getJdbcEnvironment() - .getSqlAstTranslatorFactory() - .buildMutationTranslator( sessionFactory, updateStatement ) - .translate( null, executionContext.getQueryOptions() ); - final JdbcParameterBindings updateBindings = new JdbcParameterBindingsImpl( 2 ); - if ( sessionUidColumn != null ) { - updateBindings.addBinding( - sessionUidParameter, - new JdbcParameterBindingImpl( - sessionUidColumn.getJdbcMapping(), - UUID.fromString( sessionUidAccess.apply(session) ) + sessionUidPredicate ) ); + + final JdbcOperationQueryMutation jdbcUpdate = jdbcServices.getJdbcEnvironment() + .getSqlAstTranslatorFactory() + .buildMutationTranslator( sessionFactory, updateStatement ) + .translate( null, executionContext.getQueryOptions() ); + final JdbcParameterBindings updateBindings = new JdbcParameterBindingsImpl( 2 ); + if ( sessionUidColumn != null ) { + updateBindings.addBinding( + sessionUidParameter, + new JdbcParameterBindingImpl( + sessionUidColumn.getJdbcMapping(), + UUID.fromString( sessionUidAccess.apply( session ) ) + ) + ); + } + + final BeforeExecutionGenerator beforeExecutionGenerator = (BeforeExecutionGenerator) generator; + final IntStream rowNumberStream = !rowNumberStartsAtOne + ? IntStream.of( ExecuteWithTemporaryTableHelper.loadInsertedRowNumbers( entityTable, sessionUidAccess, rows, executionContext ) ) + : IntStream.range( 1, rows + 1 ); + rowNumberStream.forEach( rowNumberValue -> { + updateBindings.addBinding( + rowNumber, + new JdbcParameterBindingImpl( + rowNumberColumn.getJdbcMapping(), + rowNumberValue + ) + ); + updateBindings.addBinding( + rootIdentity, + new JdbcParameterBindingImpl( + basicIdentifierMapping.getJdbcMapping(), + beforeExecutionGenerator.generate( session, null, null, INSERT ) + ) + ); + final int updateCount = jdbcServices.getJdbcMutationExecutor().execute( + jdbcUpdate, + updateBindings, + sql -> session + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext + ); + assert updateCount == 1; + } ); } - final BeforeExecutionGenerator beforeExecutionGenerator = (BeforeExecutionGenerator) generator; - for ( int i = 0; i < rows; i++ ) { - updateBindings.addBinding( - rowNumber, - new JdbcParameterBindingImpl( - rowNumberColumn.getJdbcMapping(), - i + 1 + identifierMapping.forEachSelectable( 0, (selectionIndex, selectableMapping) -> { + insertStatement.addTargetColumnReferences( + new ColumnReference( + (String) null, + keyColumns[selectionIndex], + false, + null, + selectableMapping.getJdbcMapping() ) ); - updateBindings.addBinding( - rootIdentity, - new JdbcParameterBindingImpl( - identifierMapping.getJdbcMapping(), - beforeExecutionGenerator.generate( session, null, null, INSERT ) + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + new ColumnReference( + temporaryTableReference.getIdentificationVariable(), + primaryKeyTableColumns.get( selectionIndex ).getColumnName(), + false, + null, + selectableMapping.getJdbcMapping() + ) ) ); - jdbcServices.getJdbcMutationExecutor().execute( - jdbcUpdate, - updateBindings, - sql -> session - .getJdbcCoordinator() - .getStatementPreparer() - .prepareStatement( sql ), - (integer, preparedStatement) -> {}, - executionContext - ); - } - - insertStatement.addTargetColumnReferences( - new ColumnReference( - (String) null, - keyColumns[0], - false, - null, - identifierMapping.getJdbcMapping() - ) - ); - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - new ColumnReference( - temporaryTableReference.getIdentificationVariable(), - idColumnReference.getColumnExpression(), - false, - null, - idColumnReference.getJdbcMapping() - ) - ) - ); + } ); } } + if ( entityTable.getSessionUidColumn() != null ) { + final TemporaryTableColumn sessionUidColumn = entityTable.getSessionUidColumn(); + querySpec.applyPredicate( new ComparisonPredicate( + new ColumnReference( + temporaryTableReference, + sessionUidColumn.getColumnName(), + false, + null, + sessionUidColumn.getJdbcMapping() + ), + ComparisonOperator.EQUAL, + sessionUidParameter + ) ); + } final JdbcOperationQueryMutation jdbcInsert = jdbcServices.getJdbcEnvironment() .getSqlAstTranslatorFactory() .buildMutationTranslator( sessionFactory, insertStatement ) .translate( null, executionContext.getQueryOptions() ); - if ( generator.generatedOnExecution() ) { + if ( !assignsId && generator.generatedOnExecution() ) { final GeneratedValuesMutationDelegate insertDelegate = entityDescriptor.getEntityPersister().getInsertDelegate(); // todo 7.0 : InsertGeneratedIdentifierDelegate will be removed once we're going to handle // generated values within the jdbc insert operaetion itself @@ -561,6 +602,10 @@ private int insertRootTable( @Override public void bindValues(PreparedStatement ps) throws SQLException { jdbcValueBinder.bind( ps, entry.getKey(), 1, session ); + if ( sessionUidParameter != null ) { + sessionUidParameter.getParameterBinder() + .bindParameterValue( ps, 2, jdbcParameterBindings, executionContext ); + } } @Override public Object getEntity() { @@ -572,12 +617,22 @@ public Object getEntity() { entry.setValue( rootIdentity ); } + final List primaryKeyTableColumns = + getPrimaryKeyTableColumns( entityPersister, entityTable ); + assert primaryKeyTableColumns.size() == 1; + final JdbcParameter entityIdentity = new JdbcParameterImpl( identifierMapping.getJdbcMapping() ); final JdbcParameter rootIdentity = new JdbcParameterImpl( identifierMapping.getJdbcMapping() ); final List temporaryTableAssignments = new ArrayList<>( 1 ); temporaryTableAssignments.add( new Assignment( - new ColumnReference( (String) null, identifierMapping ), + new ColumnReference( + (String) null, + primaryKeyTableColumns.get( 0 ).getColumnName(), + false, + null, + primaryKeyTableColumns.get( 0 ).getJdbcMapping() + ), rootIdentity ) ); @@ -625,7 +680,7 @@ public Object getEntity() { else { return jdbcServices.getJdbcMutationExecutor().execute( jdbcInsert, - JdbcParameterBindings.NO_BINDINGS, + jdbcParameterBindings, sql -> session .getJdbcCoordinator() .getStatementPreparer() @@ -638,7 +693,7 @@ public Object getEntity() { } private boolean needsIdentifierGeneration(Generator identifierGenerator) { - if (identifierGenerator instanceof OptimizableGenerator) { + if ( !assignsId && identifierGenerator instanceof OptimizableGenerator ) { // If the generator uses an optimizer or is not bulk insertion capable, // we have to generate identifiers for the new rows, as that couldn't // have been done via a SQL expression @@ -664,7 +719,7 @@ private void insertTable( true ); - final List assignments = assignmentsByTable.get( updatingTableReference ); + final List assignments = assignmentsByTable.get( tableExpression ); if ( nullableTable && ( assignments == null || assignments.isEmpty() ) ) { // no assignments for this table - skip it return; @@ -685,61 +740,49 @@ private void insertTable( querySpec.getFromClause().addRoot( temporaryTableGroup ); final InsertSelectStatement insertStatement = new InsertSelectStatement( dmlTargetTableReference ); insertStatement.setSourceSelectStatement( querySpec ); - if ( assignments != null && !assignments.isEmpty() ) { - for ( Assignment assignment : assignments ) { - insertStatement.addTargetColumnReferences( assignment.getAssignable().getColumnReferences() ); - for ( ColumnReference columnReference : assignment.getAssignable().getColumnReferences() ) { - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - new ColumnReference( - temporaryTableReference.getIdentificationVariable(), - columnReference.getColumnExpression(), - false, - null, - columnReference.getJdbcMapping() - ) - ) - ); - } - } - } - final String targetKeyColumnName = keyColumns[0]; - final EntityPersister entityPersister = entityDescriptor.getEntityPersister(); - final Generator identifierGenerator = entityPersister.getGenerator(); - final boolean needsKeyInsert; - if ( identifierGenerator.generatedOnExecution() ) { - needsKeyInsert = true; - } - else if ( identifierGenerator instanceof OptimizableGenerator ) { - final Optimizer optimizer = ( (OptimizableGenerator) identifierGenerator ).getOptimizer(); - // If the generator uses an optimizer, we have to generate the identifiers for the new rows - needsKeyInsert = optimizer != null && optimizer.getIncrementSize() > 1; - } - else { - needsKeyInsert = true; - } - if ( needsKeyInsert && insertStatement.getTargetColumns() + applyAssignments( assignments, insertStatement, temporaryTableReference ); + if ( insertStatement.getTargetColumns() .stream() - .noneMatch( c -> targetKeyColumnName.equals( c.getColumnExpression() ) ) ) { - final BasicEntityIdentifierMapping identifierMapping = - (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping(); - insertStatement.addTargetColumnReferences( + .noneMatch( c -> keyColumns[0].equals( c.getColumnExpression() ) ) ) { + final List primaryKeyTableColumns = + getPrimaryKeyTableColumns( entityDescriptor.getEntityPersister(), entityTable ); + entityDescriptor.getIdentifierMapping().forEachSelectable( 0, (selectionIndex, selectableMapping) -> { + insertStatement.addTargetColumnReferences( + new ColumnReference( + (String) null, + keyColumns[selectionIndex], + false, + null, + selectableMapping.getJdbcMapping() + ) + ); + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + new ColumnReference( + temporaryTableReference.getIdentificationVariable(), + primaryKeyTableColumns.get( selectionIndex ).getColumnName(), + false, + null, + selectableMapping.getJdbcMapping() + ) + ) + ); + } ); + } + + if ( entityTable.getSessionUidColumn() != null ) { + final TemporaryTableColumn sessionUidColumn = entityTable.getSessionUidColumn(); + querySpec.applyPredicate( new ComparisonPredicate( new ColumnReference( - dmlTargetTableReference.getIdentificationVariable(), - targetKeyColumnName, + temporaryTableReference, + sessionUidColumn.getColumnName(), false, null, - identifierMapping.getJdbcMapping() - ) - ); - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - new ColumnReference( - temporaryTableReference.getIdentificationVariable(), - identifierMapping - ) - ) - ); + sessionUidColumn.getJdbcMapping() + ), + ComparisonOperator.EQUAL, + sessionUidParameter + ) ); } final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); final JdbcOperationQueryMutation jdbcInsert = jdbcServices.getJdbcEnvironment() @@ -749,7 +792,7 @@ else if ( identifierGenerator instanceof OptimizableGenerator ) { jdbcServices.getJdbcMutationExecutor().execute( jdbcInsert, - JdbcParameterBindings.NO_BINDINGS, + jdbcParameterBindings, sql -> executionContext.getSession() .getJdbcCoordinator() .getStatementPreparer() @@ -759,4 +802,30 @@ else if ( identifierGenerator instanceof OptimizableGenerator ) { executionContext ); } + + private void applyAssignments(List assignments, InsertSelectStatement insertStatement, NamedTableReference temporaryTableReference) { + if ( assignments != null && !assignments.isEmpty() ) { + for ( Assignment assignment : assignments ) { + final Assignable assignable = assignment.getAssignable(); + insertStatement.addTargetColumnReferences( assignable.getColumnReferences() ); + final List columns = entityTable.findTemporaryTableColumns( + entityDescriptor.getEntityPersister(), + ((SqmPathInterpretation) assignable).getExpressionType() + ); + for ( TemporaryTableColumn temporaryTableColumn : columns ) { + insertStatement.getSourceSelectStatement().getFirstQuerySpec().getSelectClause().addSqlSelection( + new SqlSelectionImpl( + new ColumnReference( + temporaryTableReference.getIdentificationVariable(), + temporaryTableColumn.getColumnName(), + false, + null, + temporaryTableColumn.getJdbcMapping() + ) + ) + ); + } + } + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableInsertStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableInsertStrategy.java index f81dcce16128..f9241d38cd04 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableInsertStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableInsertStrategy.java @@ -5,10 +5,13 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; @@ -19,6 +22,31 @@ */ public class LocalTemporaryTableInsertStrategy extends LocalTemporaryTableStrategy implements SqmMultiTableInsertStrategy { + public LocalTemporaryTableInsertStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { + this( + rootEntityDescriptor, + requireLocalTemporaryTableStrategy( runtimeModelCreationContext.getDialect() ), + runtimeModelCreationContext + ); + } + + private LocalTemporaryTableInsertStrategy( + EntityMappingType rootEntityDescriptor, + TemporaryTableStrategy temporaryTableStrategy, + RuntimeModelCreationContext runtimeModelCreationContext) { + this( + TemporaryTable.createEntityTable( + runtimeModelCreationContext.getMetadata() + .getEntityBinding( rootEntityDescriptor.getEntityName() ), + basename -> temporaryTableStrategy.adjustTemporaryTableName( TemporaryTable.ENTITY_TABLE_PREFIX + basename ), + TemporaryTableKind.LOCAL, + runtimeModelCreationContext.getDialect(), + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + public LocalTemporaryTableInsertStrategy( TemporaryTable entityTable, SessionFactoryImplementor sessionFactory) { @@ -34,9 +62,8 @@ public int executeInsert( sqmInsertStatement, domainParameterXref, getTemporaryTable(), - isDropIdTables() - ? AfterUseAction.DROP - : getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + isDropIdTables(), session -> { throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); }, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableMutationStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableMutationStrategy.java index 9f65733fc26b..d8818f66da52 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableMutationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableMutationStrategy.java @@ -5,10 +5,13 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; @@ -20,6 +23,31 @@ */ public class LocalTemporaryTableMutationStrategy extends LocalTemporaryTableStrategy implements SqmMultiTableMutationStrategy { + public LocalTemporaryTableMutationStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { + this( + rootEntityDescriptor, + requireLocalTemporaryTableStrategy( runtimeModelCreationContext.getDialect() ), + runtimeModelCreationContext + ); + } + + private LocalTemporaryTableMutationStrategy( + EntityMappingType rootEntityDescriptor, + TemporaryTableStrategy temporaryTableStrategy, + RuntimeModelCreationContext runtimeModelCreationContext) { + this( + TemporaryTable.createIdTable( + runtimeModelCreationContext.getBootModel() + .getEntityBinding( rootEntityDescriptor.getEntityName() ), + basename -> temporaryTableStrategy.adjustTemporaryTableName( TemporaryTable.ID_TABLE_PREFIX + basename ), + TemporaryTableKind.LOCAL, + runtimeModelCreationContext.getDialect(), + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + public LocalTemporaryTableMutationStrategy( TemporaryTable idTable, SessionFactoryImplementor sessionFactory) { @@ -35,9 +63,8 @@ public int executeUpdate( sqmUpdate, domainParameterXref, getTemporaryTable(), - isDropIdTables() - ? AfterUseAction.DROP - : getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + isDropIdTables(), session -> { throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); }, @@ -54,9 +81,8 @@ public int executeDelete( sqmDelete, domainParameterXref, getTemporaryTable(), - isDropIdTables() - ? AfterUseAction.DROP - : getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + isDropIdTables(), session -> { throw new UnsupportedOperationException( "Unexpected call to access Session uid" ); }, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableStrategy.java index 85bd6dc72c22..1fc639dfd59c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/LocalTemporaryTableStrategy.java @@ -4,14 +4,19 @@ */ package org.hibernate.query.sqm.mutation.internal.temptable; +import org.hibernate.dialect.Dialect; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import java.util.Objects; + +import static org.hibernate.internal.util.NullnessUtil.castNonNull; + /** * Strategy based on ANSI SQL's definition of a "local temporary table" (local to each db session). * @@ -32,6 +37,15 @@ public LocalTemporaryTableStrategy(TemporaryTable temporaryTable, SessionFactory this.sessionFactory = sessionFactory; } + protected static TemporaryTableStrategy requireLocalTemporaryTableStrategy(Dialect dialect) { + return Objects.requireNonNull( dialect.getLocalTemporaryTableStrategy(), + "Dialect does not define a local temporary table strategy: " + dialect.getClass().getSimpleName() ); + } + + public TemporaryTableStrategy getTemporaryTableStrategy() { + return castNonNull( sessionFactory.getJdbcServices().getDialect().getLocalTemporaryTableStrategy() ); + } + public void prepare(MappingModelCreationProcess mappingModelCreationProcess, JdbcConnectionAccess connectionAccess) { final ConfigurationService configService = mappingModelCreationProcess.getCreationContext() @@ -48,10 +62,6 @@ public TemporaryTable getTemporaryTable() { return temporaryTable; } - public EntityMappingType getEntityDescriptor() { - return getTemporaryTable().getEntityDescriptor(); - } - public boolean isDropIdTables() { return dropIdTables; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableInsertStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableInsertStrategy.java index 4853e7f95013..2c980a2887aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableInsertStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableInsertStrategy.java @@ -5,12 +5,17 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; + /** * This is a strategy that mimics temporary tables for databases which do not support * temporary tables. It follows a pattern similar to the ANSI SQL definition of global @@ -20,6 +25,31 @@ */ public class PersistentTableInsertStrategy extends PersistentTableStrategy implements SqmMultiTableInsertStrategy { + public PersistentTableInsertStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { + this( + rootEntityDescriptor, + runtimeModelCreationContext.getDialect().getPersistentTemporaryTableStrategy(), + runtimeModelCreationContext + ); + } + + private PersistentTableInsertStrategy( + EntityMappingType rootEntityDescriptor, + TemporaryTableStrategy temporaryTableStrategy, + RuntimeModelCreationContext runtimeModelCreationContext) { + this( + TemporaryTable.createEntityTable( + runtimeModelCreationContext.getMetadata() + .getEntityBinding( rootEntityDescriptor.getEntityName() ), + basename -> temporaryTableStrategy.adjustTemporaryTableName( TemporaryTable.ENTITY_TABLE_PREFIX + basename ), + TemporaryTableKind.PERSISTENT, + runtimeModelCreationContext.getDialect(), + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + public PersistentTableInsertStrategy( TemporaryTable entityTable, SessionFactoryImplementor sessionFactory) { @@ -35,7 +65,8 @@ public int executeInsert( sqmInsertStatement, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, session -> session.getSessionIdentifier().toString(), getSessionFactory() ).execute( context ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableMutationStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableMutationStrategy.java index 502283f761b8..7c1d8312fc03 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableMutationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableMutationStrategy.java @@ -5,13 +5,18 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableKind; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; + /** * This is a strategy that mimics temporary tables for databases which do not support * temporary tables. It follows a pattern similar to the ANSI SQL definition of global @@ -21,6 +26,31 @@ */ public class PersistentTableMutationStrategy extends PersistentTableStrategy implements SqmMultiTableMutationStrategy { + public PersistentTableMutationStrategy(EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) { + this( + rootEntityDescriptor, + runtimeModelCreationContext.getDialect().getPersistentTemporaryTableStrategy(), + runtimeModelCreationContext + ); + } + + private PersistentTableMutationStrategy( + EntityMappingType rootEntityDescriptor, + TemporaryTableStrategy temporaryTableStrategy, + RuntimeModelCreationContext runtimeModelCreationContext) { + this( + TemporaryTable.createIdTable( + runtimeModelCreationContext.getBootModel() + .getEntityBinding( rootEntityDescriptor.getEntityName() ), + basename -> temporaryTableStrategy.adjustTemporaryTableName( TemporaryTable.ID_TABLE_PREFIX + basename ), + TemporaryTableKind.PERSISTENT, + runtimeModelCreationContext.getDialect(), + runtimeModelCreationContext + ), + runtimeModelCreationContext.getSessionFactory() + ); + } + public PersistentTableMutationStrategy( TemporaryTable idTable, SessionFactoryImplementor sessionFactory) { @@ -36,7 +66,8 @@ public int executeUpdate( sqmUpdate, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, session -> session.getSessionIdentifier().toString(), getSessionFactory() ).execute( context ); @@ -51,7 +82,8 @@ public int executeDelete( sqmDelete, domainParameterXref, getTemporaryTable(), - getSessionFactory().getJdbcServices().getDialect().getTemporaryTableAfterUseAction(), + getTemporaryTableStrategy(), + false, session -> session.getSessionIdentifier().toString(), getSessionFactory() ).execute( context ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableStrategy.java index a25984bec40b..6675438f58ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/PersistentTableStrategy.java @@ -9,11 +9,11 @@ import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableHelper; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.query.sqm.mutation.spi.AfterUseAction; @@ -21,6 +21,8 @@ import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER; +import static org.hibernate.internal.util.NullnessUtil.castNonNull; + /** * This is a strategy that mimics temporary tables for databases which do not support * temporary tables. It follows a pattern similar to the ANSI SQL definition of global @@ -54,13 +56,13 @@ public PersistentTableStrategy( this.temporaryTable = temporaryTable; this.sessionFactory = sessionFactory; - if ( sessionFactory.getJdbcServices().getDialect().getTemporaryTableAfterUseAction() == AfterUseAction.DROP ) { + if ( sessionFactory.getJdbcServices().getDialect().getPersistentTemporaryTableStrategy().getTemporaryTableAfterUseAction() == AfterUseAction.DROP ) { throw new IllegalArgumentException( "Persistent ID tables cannot use AfterUseAction.DROP : " + temporaryTable.getTableExpression() ); } } - public EntityMappingType getEntityDescriptor() { - return getTemporaryTable().getEntityDescriptor(); + public TemporaryTableStrategy getTemporaryTableStrategy() { + return castNonNull( sessionFactory.getJdbcServices().getDialect().getPersistentTemporaryTableStrategy() ); } public void prepare( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java index fafe02327b44..268c3015276c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java @@ -12,6 +12,7 @@ import java.util.function.Supplier; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -31,7 +32,6 @@ import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; import org.hibernate.query.sqm.mutation.internal.TableKeyExpressionCollector; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; @@ -64,7 +64,8 @@ public class RestrictedDeleteExecutionDelegate extends AbstractDeleteExecutionDe public RestrictedDeleteExecutionDelegate( EntityMappingType entityDescriptor, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, SqmDeleteStatement sqmDelete, DomainParameterXref domainParameterXref, QueryOptions queryOptions, @@ -75,7 +76,8 @@ public RestrictedDeleteExecutionDelegate( super( entityDescriptor, idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, @@ -442,6 +444,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter sqmDelete, DomainParameterXref domainParameterXref, QueryOptions queryOptions, @@ -71,7 +72,8 @@ public SoftDeleteExecutionDelegate( super( entityDescriptor, idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sqmDelete, domainParameterXref, queryOptions, @@ -183,6 +185,7 @@ private int performDeleteWithIdTable( SqmJdbcExecutionContextAdapter executionContext) { ExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions( getIdTable(), + getTemporaryTableStrategy(), executionContext ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedDeleteHandler.java index bee7aeecd05d..0745d197f29c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedDeleteHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedDeleteHandler.java @@ -7,6 +7,7 @@ import java.util.function.Function; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.DomainQueryExecutionContext; @@ -31,7 +32,8 @@ public interface ExecutionDelegate { } private final TemporaryTable idTable; - private final AfterUseAction afterUseAction; + private final TemporaryTableStrategy temporaryTableStrategy; + private final boolean forceDropAfterUse; private final Function sessionUidAccess; private final DomainParameterXref domainParameterXref; @@ -40,14 +42,16 @@ public TableBasedDeleteHandler( SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, SessionFactoryImplementor sessionFactory) { super( sqmDeleteStatement, sessionFactory ); this.idTable = idTable; this.domainParameterXref = domainParameterXref; - this.afterUseAction = afterUseAction; + this.temporaryTableStrategy = temporaryTableStrategy; + this.forceDropAfterUse = forceDropAfterUse; this.sessionUidAccess = sessionUidAccess; } @@ -68,7 +72,8 @@ protected ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executio return new SoftDeleteExecutionDelegate( getEntityDescriptor(), idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, getSqmDeleteOrUpdateStatement(), domainParameterXref, executionContext.getQueryOptions(), @@ -82,7 +87,8 @@ protected ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executio return new RestrictedDeleteExecutionDelegate( getEntityDescriptor(), idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, getSqmDeleteOrUpdateStatement(), domainParameterXref, executionContext.getQueryOptions(), @@ -99,7 +105,15 @@ protected TemporaryTable getIdTable() { } protected AfterUseAction getAfterUseAction() { - return afterUseAction; + return forceDropAfterUse ? AfterUseAction.DROP : temporaryTableStrategy.getTemporaryTableAfterUseAction(); + } + + protected TemporaryTableStrategy getTemporaryTableStrategy() { + return temporaryTableStrategy; + } + + protected boolean isForceDropAfterUse() { + return forceDropAfterUse; } protected Function getSessionUidAccess() { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java index 2201c90f03fe..bef567667987 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java @@ -13,11 +13,10 @@ import org.hibernate.dialect.temptable.TemporaryTable; import org.hibernate.dialect.temptable.TemporaryTableColumn; import org.hibernate.dialect.temptable.TemporaryTableSessionUidColumn; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.generator.Generator; -import org.hibernate.id.OptimizableGenerator; -import org.hibernate.id.enhanced.Optimizer; +import org.hibernate.generator.OnExecutionGenerator; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.DomainQueryExecutionContext; @@ -26,8 +25,8 @@ import org.hibernate.query.sqm.mutation.internal.InsertHandler; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.SqmInsertStrategyHelper; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; +import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertStatement; import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement; @@ -52,6 +51,8 @@ import org.jboss.logging.Logger; +import static org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper.isId; + /** * @author Christian Beikov */ @@ -66,7 +67,8 @@ public interface ExecutionDelegate { private final SessionFactoryImplementor sessionFactory; private final TemporaryTable entityTable; - private final AfterUseAction afterUseAction; + private final TemporaryTableStrategy temporaryTableStrategy; + private final boolean forceDropAfterUse; private final Function sessionUidAccess; private final DomainParameterXref domainParameterXref; private final JdbcParameter sessionUidParameter; @@ -75,11 +77,13 @@ public TableBasedInsertHandler( SqmInsertStatement sqmInsert, DomainParameterXref domainParameterXref, TemporaryTable entityTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, SessionFactoryImplementor sessionFactory) { this.sqmInsertStatement = sqmInsert; - this.afterUseAction = afterUseAction; + this.temporaryTableStrategy = temporaryTableStrategy; + this.forceDropAfterUse = forceDropAfterUse; this.sessionFactory = sessionFactory; this.entityTable = entityTable; this.sessionUidAccess = sessionUidAccess; @@ -142,9 +146,18 @@ protected ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executio final InsertSelectStatement insertStatement = new InsertSelectStatement( entityTableReference ); final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = converterDelegate.visitInsertionTargetPaths( - (assigable, columnReferences) -> { - insertStatement.addTargetColumnReferences( columnReferences ); - targetPathColumns.add( new Assignment( assigable, (Expression) assigable ) ); + (assignable, columnReferences) -> { + final SqmPathInterpretation pathInterpretation = (SqmPathInterpretation) assignable; + final List columns = + entityTable.findTemporaryTableColumns( entityDescriptor, pathInterpretation.getExpressionType() ); + for ( TemporaryTableColumn column : columns ) { + insertStatement.addTargetColumnReference( new ColumnReference( + entityTableReference, + column.getColumnName(), + column.getJdbcMapping() + ) ); + } + targetPathColumns.add( new Assignment( assignable, (Expression) assignable ) ); }, sqmInsertStatement, entityDescriptor, @@ -179,33 +192,29 @@ protected ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executio new Assignment( columnReference, columnReference ) ); } - else if ( entityDescriptor.getGenerator() instanceof OptimizableGenerator ) { - final Optimizer optimizer = ( (OptimizableGenerator) entityDescriptor.getGenerator() ).getOptimizer(); - if ( optimizer != null && optimizer.getIncrementSize() > 1 ) { - if ( !sessionFactory.getJdbcServices().getDialect().supportsWindowFunctions() ) { - return; - } - final TemporaryTableColumn rowNumberColumn = entityTable.getColumns() - .get( entityTable.getColumns().size() - ( sessionUidColumn == null ? 1 : 2 ) ); - final ColumnReference columnReference = new ColumnReference( - (String) null, - rowNumberColumn.getColumnName(), - false, - null, - rowNumberColumn.getJdbcMapping() - ); - insertStatement.getTargetColumns().add( columnReference ); - targetPathColumns.add( new Assignment( columnReference, columnReference ) ); - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - 0, - SqmInsertStrategyHelper.createRowNumberingExpression( - querySpec, - sessionFactory - ) - ) - ); - } + else if ( !(entityDescriptor.getGenerator() instanceof OnExecutionGenerator generator + && generator.generatedOnExecution()) + && !entityTable.isRowNumberGenerated() ) { + final TemporaryTableColumn rowNumberColumn = entityTable.getColumns() + .get( entityTable.getColumns().size() - ( sessionUidColumn == null ? 1 : 2 ) ); + final ColumnReference columnReference = new ColumnReference( + (String) null, + rowNumberColumn.getColumnName(), + false, + null, + rowNumberColumn.getJdbcMapping() + ); + insertStatement.getTargetColumns().add( columnReference ); + targetPathColumns.add( new Assignment( columnReference, columnReference ) ); + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + 0, + SqmInsertStrategyHelper.createRowNumberingExpression( + querySpec, + sessionFactory + ) + ) + ); } if ( sessionUidColumn != null ) { final ColumnReference sessionUidColumnReference = new ColumnReference( @@ -228,27 +237,22 @@ else if ( entityDescriptor.getGenerator() instanceof OptimizableGenerator ) { } else { // Add the row number column if there is one - final Generator generator = entityDescriptor.getGenerator(); final BasicType rowNumberType; - if ( generator instanceof OptimizableGenerator ) { - final Optimizer optimizer = ( (OptimizableGenerator) generator ).getOptimizer(); - if ( optimizer != null && optimizer.getIncrementSize() > 1 ) { - final TemporaryTableColumn rowNumberColumn = entityTable.getColumns() - .get( entityTable.getColumns().size() - ( sessionUidColumn == null ? 1 : 2 ) ); - rowNumberType = (BasicType) rowNumberColumn.getJdbcMapping(); - final ColumnReference columnReference = new ColumnReference( - (String) null, - rowNumberColumn.getColumnName(), - false, - null, - rowNumberColumn.getJdbcMapping() - ); - insertStatement.getTargetColumns().add( columnReference ); - targetPathColumns.add( new Assignment( columnReference, columnReference ) ); - } - else { - rowNumberType = null; - } + if ( !(entityDescriptor.getGenerator() instanceof OnExecutionGenerator generator + && generator.generatedOnExecution()) + && !entityTable.isRowNumberGenerated() ) { + final TemporaryTableColumn rowNumberColumn = entityTable.getColumns() + .get( entityTable.getColumns().size() - (sessionUidColumn == null ? 1 : 2) ); + rowNumberType = (BasicType) rowNumberColumn.getJdbcMapping(); + final ColumnReference columnReference = new ColumnReference( + (String) null, + rowNumberColumn.getColumnName(), + false, + null, + rowNumberColumn.getJdbcMapping() + ); + insertStatement.getTargetColumns().add( columnReference ); + targetPathColumns.add( new Assignment( columnReference, columnReference ) ); } else { rowNumberType = null; @@ -288,6 +292,12 @@ else if ( entityDescriptor.getGenerator() instanceof OptimizableGenerator ) { final ConflictClause conflictClause = converterDelegate.visitConflictClause( sqmInsertStatement.getConflictClause() ); converterDelegate.pruneTableGroupJoins(); + boolean assignsId = false; + for ( Assignment assignment : targetPathColumns ) { + assignsId = assignsId || assignment.getAssignable() instanceof SqmPathInterpretation pathInterpretation + && isId( pathInterpretation.getExpressionType() ); + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // cross-reference the TableReference by alias. The TableGroup already // cross-references it by name, but the ColumnReference only has the alias @@ -302,12 +312,14 @@ else if ( entityDescriptor.getGenerator() instanceof OptimizableGenerator ) { sqmInsertStatement, converterDelegate, entityTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, insertingTableGroup, tableReferenceByAlias, targetPathColumns, + assignsId, insertStatement, conflictClause, sessionUidParameter, @@ -322,12 +334,14 @@ protected ExecutionDelegate buildExecutionDelegate( SqmInsertStatement sqmInsert, MultiTableSqmMutationConverter sqmConverter, TemporaryTable entityTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup insertingTableGroup, Map tableReferenceByAlias, List assignments, + boolean assignsId, InsertSelectStatement insertStatement, ConflictClause conflictClause, JdbcParameter sessionUidParameter, @@ -335,12 +349,14 @@ protected ExecutionDelegate buildExecutionDelegate( return new InsertExecutionDelegate( sqmConverter, entityTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, insertingTableGroup, tableReferenceByAlias, assignments, + assignsId, insertStatement, conflictClause, sessionUidParameter, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedUpdateHandler.java index e893e686a1da..533dd49954dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedUpdateHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedUpdateHandler.java @@ -5,6 +5,7 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.CollectionHelper; @@ -16,7 +17,6 @@ import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; import org.hibernate.query.sqm.mutation.internal.UpdateHandler; import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler; -import org.hibernate.query.sqm.mutation.spi.AfterUseAction; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableReference; @@ -45,7 +45,8 @@ public interface ExecutionDelegate { } private final TemporaryTable idTable; - private final AfterUseAction afterUseAction; + private final TemporaryTableStrategy temporaryTableStrategy; + private final boolean forceDropAfterUse; private final Function sessionUidAccess; private final DomainParameterXref domainParameterXref; @@ -53,12 +54,14 @@ public TableBasedUpdateHandler( SqmUpdateStatement sqmUpdate, DomainParameterXref domainParameterXref, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, SessionFactoryImplementor sessionFactory) { super( sqmUpdate, sessionFactory ); this.idTable = idTable; - this.afterUseAction = afterUseAction; + this.temporaryTableStrategy = temporaryTableStrategy; + this.forceDropAfterUse = forceDropAfterUse; this.sessionUidAccess = sessionUidAccess; this.domainParameterXref = domainParameterXref; } @@ -156,7 +159,8 @@ protected ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executio return buildExecutionDelegate( converterDelegate, idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, updatingTableGroup, @@ -170,7 +174,8 @@ protected ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executio protected UpdateExecutionDelegate buildExecutionDelegate( MultiTableSqmMutationConverter sqmConverter, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup updatingTableGroup, @@ -181,7 +186,8 @@ protected UpdateExecutionDelegate buildExecutionDelegate( return new UpdateExecutionDelegate( sqmConverter, idTable, - afterUseAction, + temporaryTableStrategy, + forceDropAfterUse, sessionUidAccess, domainParameterXref, updatingTableGroup, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java index db273c47fcd2..d2e5460ab903 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java @@ -5,6 +5,7 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableStrategy; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -66,7 +67,8 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.ExecutionDelegate { private final MultiTableSqmMutationConverter sqmConverter; private final TemporaryTable idTable; - private final AfterUseAction afterUseAction; + private final TemporaryTableStrategy temporaryTableStrategy; + private final boolean forceDropAfterUse; private final Function sessionUidAccess; private final TableGroup updatingTableGroup; private final Predicate suppliedPredicate; @@ -81,7 +83,8 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio public UpdateExecutionDelegate( MultiTableSqmMutationConverter sqmConverter, TemporaryTable idTable, - AfterUseAction afterUseAction, + TemporaryTableStrategy temporaryTableStrategy, + boolean forceDropAfterUse, Function sessionUidAccess, DomainParameterXref domainParameterXref, TableGroup updatingTableGroup, @@ -91,7 +94,8 @@ public UpdateExecutionDelegate( DomainQueryExecutionContext executionContext) { this.sqmConverter = sqmConverter; this.idTable = idTable; - this.afterUseAction = afterUseAction; + this.temporaryTableStrategy = temporaryTableStrategy; + this.forceDropAfterUse = forceDropAfterUse; this.sessionUidAccess = sessionUidAccess; this.updatingTableGroup = updatingTableGroup; this.sessionFactory = executionContext.getSession().getFactory(); @@ -169,6 +173,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter cteColumns; @@ -67,6 +67,7 @@ public CteTable withName(String name) { return new CteTable( name, tableGroupProducer, cteColumns ); } + @Deprecated(forRemoval = true, since = "7.1") public static CteTable createIdTable(String cteName, EntityMappingType entityDescriptor) { final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount(); final List columns = new ArrayList<>( numberOfColumns ); @@ -79,6 +80,7 @@ public static CteTable createIdTable(String cteName, EntityMappingType entityDes return new CteTable( cteName, columns ); } + @Deprecated(forRemoval = true, since = "7.1") public static CteTable createEntityTable(String cteName, EntityMappingType entityDescriptor) { final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount(); final List columns = new ArrayList<>( numberOfColumns ); @@ -108,7 +110,7 @@ public static CteTable createEntityTable(String cteName, EntityMappingType entit // We add a special row number column that we can use to identify and join rows columns.add( new CteColumn( - "rn_", + ENTITY_ROW_NUMBER_COLUMN, entityDescriptor.getEntityPersister() .getFactory() .getTypeConfiguration() @@ -118,109 +120,92 @@ public static CteTable createEntityTable(String cteName, EntityMappingType entit return new CteTable( cteName, columns ); } - public static void forEachCteColumn(String prefix, ModelPart modelPart, Consumer consumer) { - if ( modelPart instanceof BasicValuedMapping basicValuedMapping ) { - consumer.accept( new CteColumn( prefix, basicValuedMapping.getJdbcMapping() ) ); - } - else if ( modelPart instanceof EntityValuedModelPart entityPart ) { - final ModelPart targetPart; - if ( modelPart instanceof Association association ) { - if ( association.getForeignKeyDescriptor() == null ) { - // This is expected to happen when processing a - // PostInitCallbackEntry because the callbacks - // are not ordered. The exception is caught in - // MappingModelCreationProcess.executePostInitCallbacks() - // and the callback is re-queued. - throw new IllegalStateException( "ForeignKeyDescriptor not ready for [" + association.getPartName() + "] on entity: " + modelPart.findContainingEntityMapping().getEntityName() ); - } - if ( association.getSideNature() != ForeignKeyDescriptor.Nature.KEY ) { - // Inverse one-to-one receives no column - return; - } - targetPart = association.getForeignKeyDescriptor().getTargetPart(); - } - else { - targetPart = entityPart.getEntityMappingType().getIdentifierMapping(); - } - forEachCteColumn( prefix + "_" + entityPart.getPartName(), targetPart, consumer ); - } - else if ( modelPart instanceof DiscriminatedAssociationModelPart discriminatedPart ) { - final String newPrefix = prefix + "_" + discriminatedPart.getPartName() + "_"; - forEachCteColumn( - newPrefix + "discriminator", - discriminatedPart.getDiscriminatorPart(), - consumer - ); - forEachCteColumn( - newPrefix + "key", - discriminatedPart.getKeyPart(), - consumer - ); + public static CteTable createIdTable(String cteName, PersistentClass persistentClass) { + final Property identifierProperty = persistentClass.getIdentifierProperty(); + final String idName; + if ( identifierProperty != null ) { + idName = identifierProperty.getName(); } else { - final EmbeddableValuedModelPart embeddablePart = ( EmbeddableValuedModelPart ) modelPart; - final AttributeMappingsList attributeMappings = embeddablePart.getEmbeddableTypeDescriptor().getAttributeMappings(); - for ( int i = 0; i < attributeMappings.size(); i++ ) { - AttributeMapping mapping = attributeMappings.get( i ); - if ( !( mapping instanceof PluralAttributeMapping ) ) { - forEachCteColumn( prefix + "_" + mapping.getAttributeName(), mapping, consumer ); - } - } + idName = "id"; } + final List columns = new ArrayList<>( persistentClass.getIdentifier().getColumnSpan() ); + final Metadata metadata = persistentClass.getIdentifier().getBuildingContext().getMetadataCollector(); + forEachCteColumn( idName, persistentClass.getIdentifier(), columns::add ); + return new CteTable( cteName, columns ); } - public static int determineModelPartStartIndex(EntityPersister entityDescriptor, ModelPart modelPart) { - int offset = 0; - final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - if ( modelPart == identifierMapping ) { - return offset; + public static CteTable createEntityTable(String cteName, PersistentClass persistentClass) { + final List columns = new ArrayList<>( persistentClass.getTable().getColumnSpan() ); + final Property identifierProperty = persistentClass.getIdentifierProperty(); + final String idName; + if ( identifierProperty != null ) { + idName = identifierProperty.getName(); } - offset += identifierMapping.getJdbcTypeCount(); - final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping(); - if ( discriminatorMapping != null ) { - if ( modelPart == discriminatorMapping ) { - return offset; - } - offset += discriminatorMapping.getJdbcTypeCount(); + else { + idName = "id"; } - final AttributeMappingsList attributeMappings = entityDescriptor.getAttributeMappings(); - for ( int i = 0; i < attributeMappings.size(); i++ ) { - AttributeMapping attribute = attributeMappings.get( i ); - if ( !( attribute instanceof PluralAttributeMapping ) ) { - final int result = determineModelPartStartIndex( offset, attribute, modelPart ); - if ( result < 0 ) { - return -result; - } - offset = result; + final Metadata metadata = persistentClass.getIdentifier().getBuildingContext().getMetadataCollector(); + forEachCteColumn( idName, persistentClass.getIdentifier(), columns::add ); + + final Value discriminator = persistentClass.getDiscriminator(); + if ( discriminator != null && !discriminator.getSelectables().get( 0 ).isFormula() ) { + forEachCteColumn( "class", persistentClass.getIdentifier(), columns::add ); + } + + // Collect all columns for all entity subtype attributes + for ( Property property : persistentClass.getPropertyClosure() ) { + if ( !property.isSynthetic() ) { + forEachCteColumn( + property.getName(), + property.getValue(), + columns::add + ); } } - return -1; + // We add a special row number column that we can use to identify and join rows + columns.add( + new CteColumn( + ENTITY_ROW_NUMBER_COLUMN, + metadata.getDatabase().getTypeConfiguration().getBasicTypeForJavaType( Integer.class ) + ) + ); + return new CteTable( cteName, columns ); + } + + private static void forEachCteColumn(String prefix, Value value, Consumer consumer) { + SqmMutationStrategyHelper.forEachSelectableMapping( prefix, value, (columnName, selectable) -> { + consumer.accept( new CteColumn( columnName, selectable.getType() ) ); + } ); } - private static int determineModelPartStartIndex(int offset, ModelPart modelPart, ModelPart modelPartToFind) { - if ( modelPart == modelPartToFind ) { - return -offset; + private static void forEachCteColumn(String prefix, ModelPart modelPart, Consumer consumer) { + SqmMutationStrategyHelper.forEachSelectableMapping( prefix, modelPart, (s, selectableMapping) -> { + consumer.accept( new CteColumn( s, selectableMapping.getJdbcMapping() ) ); + } ); + } + + public List findCteColumns(ModelPart modelPart) { + final String prefix; + if ( modelPart instanceof AttributeMapping attributeMapping ) { + prefix = attributeMapping.getAttributeName(); } - if ( modelPart instanceof EntityValuedModelPart entityValuedModelPart ) { - final ModelPart keyPart = - modelPart instanceof Association association - ? association.getForeignKeyDescriptor() - : entityValuedModelPart.getEntityMappingType().getIdentifierMapping(); - return determineModelPartStartIndex( offset, keyPart, modelPartToFind ); + else if ( modelPart instanceof DiscriminatorMapping ) { + prefix = "class"; } - else if ( modelPart instanceof EmbeddableValuedModelPart embeddablePart ) { - final AttributeMappingsList attributeMappings = - embeddablePart.getEmbeddableTypeDescriptor().getAttributeMappings(); - for ( int i = 0; i < attributeMappings.size(); i++ ) { - final AttributeMapping mapping = attributeMappings.get( i ); - final int result = determineModelPartStartIndex( offset, mapping, modelPartToFind ); - if ( result < 0 ) { - return result; + else { + prefix = ""; + } + final int jdbcTypeCount = modelPart.getJdbcTypeCount(); + final List columns = new ArrayList<>( jdbcTypeCount ); + SqmMutationStrategyHelper.forEachSelectableMapping( prefix, modelPart, (s, selectableMapping) -> { + for ( CteColumn cteColumn : cteColumns ) { + if ( s.equals( cteColumn.getColumnExpression() ) ) { + columns.add( cteColumn ); + break; } - offset = result; } - return offset; - } - return offset + modelPart.getJdbcTypeCount(); + } ); + return columns; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java index 9fbc3c30abb2..307d7db588d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/insert/InsertSelectStatement.java @@ -64,6 +64,13 @@ public void forEachTargetColumn(BiConsumer consumer) { } } + public void addTargetColumnReference(ColumnReference reference) { + if ( targetColumnReferences == null ) { + targetColumnReferences = new ArrayList<>(); + } + targetColumnReferences.add( reference ); + } + public void addTargetColumnReferences(ColumnReference... references) { if ( targetColumnReferences == null ) { targetColumnReferences = new ArrayList<>(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/LocalTemporaryTableMutationStrategyNoDropTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/LocalTemporaryTableMutationStrategyNoDropTest.java index 6418e9bd114b..ee70c795c01d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/LocalTemporaryTableMutationStrategyNoDropTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/LocalTemporaryTableMutationStrategyNoDropTest.java @@ -4,8 +4,11 @@ */ package org.hibernate.orm.test; -import java.util.Map; - +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.internal.SqlStringGenerationContextImpl; import org.hibernate.boot.spi.BootstrapContext; @@ -13,34 +16,28 @@ import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cache.spi.CacheImplementor; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.temptable.StandardTemporaryTableExporter; import org.hibernate.dialect.temptable.TemporaryTable; +import org.hibernate.dialect.temptable.TemporaryTableExporter; import org.hibernate.dialect.temptable.TemporaryTableKind; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.generator.Generator; import org.hibernate.mapping.GeneratorSettings; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.query.sqm.function.SqmFunctionRegistry; -import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; import org.hibernate.sql.ast.spi.ParameterMarkerStrategy; -import org.hibernate.type.descriptor.jdbc.JdbcType; - +import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; 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.hibernate.type.descriptor.jdbc.JdbcType; import org.junit.jupiter.api.Test; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Inheritance; -import jakarta.persistence.InheritanceType; +import java.util.Map; import static java.util.Collections.emptyMap; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; @@ -52,14 +49,12 @@ ) @SessionFactory @ServiceRegistry( - settings = { - @Setting(name = LocalTemporaryTableMutationStrategy.DROP_ID_TABLES, value = "false") - }, services = @ServiceRegistry.Service( role = ParameterMarkerStrategy.class, impl = LocalTemporaryTableMutationStrategyNoDropTest.ParameterMarkerStrategyImpl.class ) ) +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsTemporaryTable.class) @JiraKey("HHH-16486") public class LocalTemporaryTableMutationStrategyNoDropTest { @@ -69,10 +64,10 @@ public class LocalTemporaryTableMutationStrategyNoDropTest { public void testGetSqlTruncateCommand(SessionFactoryScope scope) { scope.inTransaction( session -> { - StandardTemporaryTableExporter standardTemporaryTableExporter - = createStandardTemporaryTableExporter( session ); - TemporaryTable idTable = createTemporaryTable( scope, session ); - String sqlTruncateCommand = standardTemporaryTableExporter.getSqlTruncateCommand( + final TemporaryTableExporter temporaryTableExporter = + scope.getSessionFactory().getJdbcServices().getDialect().getTemporaryTableExporter(); + final TemporaryTable idTable = createTemporaryTable( scope ); + final String sqlTruncateCommand = temporaryTableExporter.getSqlTruncateCommand( idTable, null, session @@ -84,25 +79,15 @@ public void testGetSqlTruncateCommand(SessionFactoryScope scope) { } - private static StandardTemporaryTableExporter createStandardTemporaryTableExporter(SessionImplementor session) { - return new StandardTemporaryTableExporter( getDialect() ); - } - - private static Dialect getDialect() { - return new TestDialect(); - } - - private static TemporaryTable createTemporaryTable( - SessionFactoryScope scope, - SessionImplementor session) { - SessionFactoryImplementor sessionFactory = scope.getSessionFactory(); - JdbcServices jdbcServices = sessionFactory.getJdbcServices(); - Dialect dialect = getDialect(); + private static TemporaryTable createTemporaryTable(SessionFactoryScope scope) { + final SessionFactoryImplementor sessionFactory = scope.getSessionFactory(); + final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); return TemporaryTable.createIdTable( - session.getEntityPersister( null, new TestEntity() ), + scope.getMetadataImplementor().getEntityBinding( TestEntity.class.getName() ), basename -> TemporaryTable.ID_TABLE_PREFIX + basename, - dialect, - new ModelCreationContext( sessionFactory, scope, dialect, jdbcServices ) + TemporaryTableKind.PERSISTENT, + jdbcServices.getDialect(), + new ModelCreationContext( sessionFactory, scope, jdbcServices ) ); } @@ -113,20 +98,6 @@ public String createMarker(int position, JdbcType jdbcType) { } } - public static class TestDialect extends Dialect { - - @Override - public boolean supportsTemporaryTables() { - return true; - } - - @Override - public TemporaryTableKind getSupportedTemporaryTableKind() { - return TemporaryTableKind.PERSISTENT; - } - } - - @Entity(name = "ParentEntity") @Inheritance(strategy = InheritanceType.JOINED) public static class ParentEntity { @@ -146,13 +117,11 @@ private static class ModelCreationContext implements RuntimeModelCreationContext private final SessionFactoryImplementor sessionFactory; private final SessionFactoryScope scope; - private final Dialect dialect; private final JdbcServices jdbcServices; - public ModelCreationContext(SessionFactoryImplementor sessionFactory, SessionFactoryScope scope, Dialect dialect, JdbcServices jdbcServices) { + public ModelCreationContext(SessionFactoryImplementor sessionFactory, SessionFactoryScope scope, JdbcServices jdbcServices) { this.sessionFactory = sessionFactory; this.scope = scope; - this.dialect = dialect; this.jdbcServices = jdbcServices; } @@ -188,7 +157,7 @@ public Map getSettings() { @Override public Dialect getDialect() { - return dialect; + return jdbcServices.getDialect(); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/model/relational/QualifiedNameParserTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/model/relational/QualifiedNameParserTest.java index b8eb7ad3458d..8f8afaa6fef3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/model/relational/QualifiedNameParserTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/model/relational/QualifiedNameParserTest.java @@ -38,7 +38,7 @@ public void testStringSplittingWithSchema() { @Test public void testStringSplittingWithCatalogAndSchema() { QualifiedNameParser.NameParts nameParts = PARSER.parse( - "schema.catalog.MyEntity", + "catalog.schema.MyEntity", DEFAULT_CATALOG, DEFAULT_SCHEMA ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyCompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyCompositeIdTest.java index 0eca7fbcc2c5..95fd07ff4aa2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyCompositeIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyCompositeIdTest.java @@ -13,6 +13,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -21,6 +22,8 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * @author Vlad Mihalcea @@ -39,13 +42,19 @@ protected Class[] getAnnotatedClasses() { @Override protected void configure(Configuration configuration) { super.configure( configuration ); - Class strategyClass = getMultiTableBulkIdStrategyClass(); - if ( strategyClass != null ) { - configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, strategyClass ); + Class mutationStrategyClass = getMultiTableMutationStrategyClass(); + if ( mutationStrategyClass != null ) { + configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, mutationStrategyClass ); + } + Class insertStrategyClass = getMultiTableInsertStrategyClass(); + if ( insertStrategyClass != null ) { + configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_INSERT_STRATEGY, insertStrategyClass ); } } - protected abstract Class getMultiTableBulkIdStrategyClass(); + protected abstract Class getMultiTableMutationStrategyClass(); + + protected abstract Class getMultiTableInsertStrategyClass(); @Override protected boolean isCleanupTestDataRequired() { @@ -62,7 +71,7 @@ public void setUp() { doInHibernate( this::sessionFactory, session -> { for ( int i = 0; i < entityCount(); i++ ) { Doctor doctor = new Doctor(); - doctor.setId( i ); + doctor.setId( i + 1 ); doctor.setCompanyName( "Red Hat USA" ); doctor.setEmployed( ( i % 2 ) == 0 ); session.persist( doctor ); @@ -70,7 +79,7 @@ public void setUp() { for ( int i = 0; i < entityCount(); i++ ) { Engineer engineer = new Engineer(); - engineer.setId( i ); + engineer.setId( i + 1 + entityCount() ); engineer.setCompanyName( "Red Hat Europe" ); engineer.setEmployed( ( i % 2 ) == 0 ); engineer.setFellow( ( i % 2 ) == 1 ); @@ -118,6 +127,36 @@ public void testDeleteFromEngineer() { }); } + @Test + public void testInsert() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(id, companyName, name, employed, fellow) values (0, 'Red Hat', :name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + final Engineer engineer = session.find( Engineer.class, + new AbstractMutationStrategyCompositeIdTest_.Person_.Id( 0, "Red Hat" ) ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertSelect() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(id, companyName, name, employed, fellow) " + + "select d.id + " + (entityCount() * 2) + ", 'Red Hat', 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + final Engineer engineer = session.find( Engineer.class, + new AbstractMutationStrategyCompositeIdTest_.Person_.Id( entityCount() * 2 + 1, "Red Hat" ) ); + assertEquals( entityCount(), insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + //tag::batch-bulk-hql-temp-table-base-class-example[] @Entity(name = "Person") @Inheritance(strategy = InheritanceType.JOINED) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdTest.java new file mode 100644 index 000000000000..2c99efe211f5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdTest.java @@ -0,0 +1,184 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.SequenceGenerator; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractMutationStrategyGeneratedIdTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class, + Doctor.class, + Engineer.class + }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + Class insertStrategyClass = getMultiTableInsertStrategyClass(); + if ( insertStrategyClass != null ) { + configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_INSERT_STRATEGY, insertStrategyClass ); + } + } + + protected abstract Class getMultiTableInsertStrategyClass(); + + @Override + protected boolean isCleanupTestDataRequired() { + return true; + } + + @Override + protected boolean isCleanupTestDataUsingBulkDelete() { + return true; + } + + @Before + public void setUp() { + doInHibernate( this::sessionFactory, session -> { + Doctor doctor = new Doctor(); + doctor.setName( "Doctor John" ); + doctor.setEmployed( true ); + session.persist( doctor ); + }); + } + + @Test + public void testInsertStatic() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(id, name, employed, fellow) values (0, :name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + + final Engineer engineer = session.find( Engineer.class, 0 ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertGenerated() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(name, employed, fellow) values (:name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertSelectStatic() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(id, name, employed, fellow) " + + "select d.id + 1, 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( 1, insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertSelectGenerated() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(name, employed, fellow) " + + "select 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( 1, insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Entity(name = "Person") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Person { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @SequenceGenerator(allocationSize = 1) + private Integer id; + + private String name; + + private boolean employed; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isEmployed() { + return employed; + } + + public void setEmployed(boolean employed) { + this.employed = employed; + } + } + @Entity(name = "Doctor") + public static class Doctor extends Person { + } + + @Entity(name = "Engineer") + public static class Engineer extends Person { + + private boolean fellow; + + public boolean isFellow() { + return fellow; + } + + public void setFellow(boolean fellow) { + this.fellow = fellow; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdWithOptimizerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdWithOptimizerTest.java new file mode 100644 index 000000000000..522dfb57e628 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdWithOptimizerTest.java @@ -0,0 +1,182 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Before; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractMutationStrategyGeneratedIdWithOptimizerTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class, + Doctor.class, + Engineer.class + }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + Class insertStrategyClass = getMultiTableInsertStrategyClass(); + if ( insertStrategyClass != null ) { + configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_INSERT_STRATEGY, insertStrategyClass ); + } + } + + protected abstract Class getMultiTableInsertStrategyClass(); + + @Override + protected boolean isCleanupTestDataRequired() { + return true; + } + + @Override + protected boolean isCleanupTestDataUsingBulkDelete() { + return true; + } + + @Before + public void setUp() { + doInHibernate( this::sessionFactory, session -> { + Doctor doctor = new Doctor(); + doctor.setName( "Doctor John" ); + doctor.setEmployed( true ); + session.persist( doctor ); + }); + } + + @Test + public void testInsertStatic() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(id, name, employed, fellow) values (0, :name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + + final Engineer engineer = session.find( Engineer.class, 0 ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertGenerated() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(name, employed, fellow) values (:name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertSelectStatic() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(id, name, employed, fellow) " + + "select d.id + 1, 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( 1, insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertSelectGenerated() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(name, employed, fellow) " + + "select 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( 1, insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Entity(name = "Person") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Person { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private Integer id; + + private String name; + + private boolean employed; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isEmployed() { + return employed; + } + + public void setEmployed(boolean employed) { + this.employed = employed; + } + } + @Entity(name = "Doctor") + public static class Doctor extends Person { + } + + @Entity(name = "Engineer") + public static class Engineer extends Person { + + private boolean fellow; + + public boolean isFellow() { + return fellow; + } + + public void setFellow(boolean fellow) { + this.fellow = fellow; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdentityTest.java new file mode 100644 index 000000000000..4884e426da10 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyGeneratedIdentityTest.java @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.AbstractTransactSQLDialect; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.SkipForDialect; +import org.junit.Before; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public abstract class AbstractMutationStrategyGeneratedIdentityTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class, + Doctor.class, + Engineer.class + }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + Class insertStrategyClass = getMultiTableInsertStrategyClass(); + if ( insertStrategyClass != null ) { + configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_INSERT_STRATEGY, insertStrategyClass ); + } + } + + protected abstract Class getMultiTableInsertStrategyClass(); + + @Override + protected boolean isCleanupTestDataRequired() { + return true; + } + + @Override + protected boolean isCleanupTestDataUsingBulkDelete() { + return true; + } + + @Before + public void setUp() { + doInHibernate( this::sessionFactory, session -> { + Doctor doctor = new Doctor(); + doctor.setName( "Doctor John" ); + doctor.setEmployed( true ); + session.persist( doctor ); + }); + } + + @Test + @SkipForDialect(dialectClass = MySQLDialect.class, matchSubTypes = true, reason = "MySQL ignores a provided value for an auto_increment column if it's lower than the current sequence value") + @SkipForDialect(dialectClass = AbstractTransactSQLDialect.class, matchSubTypes = true, reason = "T-SQL complains IDENTITY_INSERT is off when a value for an identity column is provided") + public void testInsertStatic() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(id, name, employed, fellow) values (0, :name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + + final Engineer engineer = session.find( Engineer.class, 0 ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle doesn't support insert-select with a returning clause") + public void testInsertGenerated() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(name, employed, fellow) values (:name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + @SkipForDialect(dialectClass = MySQLDialect.class, matchSubTypes = true, reason = "MySQL ignores a provided value for an auto_increment column if it's lower than the current sequence value") + @SkipForDialect(dialectClass = AbstractTransactSQLDialect.class, matchSubTypes = true, reason = "T-SQL complains IDENTITY_INSERT is off when a value for an identity column is provided") + public void testInsertSelectStatic() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(id, name, employed, fellow) " + + "select d.id + 1, 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( 1, insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle doesn't support insert-select with a returning clause") + public void testInsertSelectGenerated() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(name, employed, fellow) " + + "select 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + final Engineer engineer = session.createQuery( "from Engineer e where e.name = 'John Doe'", Engineer.class ) + .getSingleResult(); + assertEquals( 1, insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Entity(name = "Person") + @Inheritance(strategy = InheritanceType.JOINED) + public static class Person { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + private String name; + + private boolean employed; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isEmployed() { + return employed; + } + + public void setEmployed(boolean employed) { + this.employed = employed; + } + } + @Entity(name = "Doctor") + public static class Doctor extends Person { + } + + @Entity(name = "Engineer") + public static class Engineer extends Person { + + private boolean fellow; + + public boolean isFellow() { + return fellow; + } + + public void setFellow(boolean fellow) { + this.fellow = fellow; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyIdTest.java index 742a7cea52c3..2cbb7136d98c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/AbstractMutationStrategyIdTest.java @@ -6,7 +6,6 @@ import jakarta.persistence.Entity; import jakarta.persistence.EntityManager; -import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; @@ -16,6 +15,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -25,6 +25,8 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * @author Vlad Mihalcea @@ -43,17 +45,20 @@ protected Class[] getAnnotatedClasses() { @Override protected void configure(Configuration configuration) { super.configure( configuration ); - final Class multiTableBulkIdStrategyClass = getMultiTableBulkIdStrategyClass(); - if ( multiTableBulkIdStrategyClass != null ) { - configuration.setProperty( - AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, - multiTableBulkIdStrategyClass - ); + final Class mutationStrategyClass = getMultiTableMutationStrategyClass(); + if ( mutationStrategyClass != null ) { + configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, mutationStrategyClass ); + } + Class insertStrategyClass = getMultiTableInsertStrategyClass(); + if ( insertStrategyClass != null ) { + configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_INSERT_STRATEGY, insertStrategyClass ); } } - protected abstract Class getMultiTableBulkIdStrategyClass(); + protected abstract Class getMultiTableMutationStrategyClass(); + + protected abstract Class getMultiTableInsertStrategyClass(); @Override protected boolean isCleanupTestDataRequired() { @@ -70,12 +75,14 @@ public void setUp() { doInHibernate( this::sessionFactory, session -> { for ( int i = 0; i < entityCount(); i++ ) { Doctor doctor = new Doctor(); + doctor.setId( i + 1 ); doctor.setEmployed( ( i % 2 ) == 0 ); session.persist( doctor ); } for ( int i = 0; i < entityCount(); i++ ) { Engineer engineer = new Engineer(); + engineer.setId( i + 1 + entityCount() ); engineer.setEmployed( ( i % 2 ) == 0 ); engineer.setFellow( ( i % 2 ) == 1 ); session.persist( engineer ); @@ -136,23 +143,52 @@ public void testDeleteFromEngineer() { }); } + @Test + public void testInsert() { + doInHibernate( this::sessionFactory, session -> { + session.createQuery( "insert into Engineer(id, name, employed, fellow) values (0, :name, :employed, false)" ) + .setParameter( "name", "John Doe" ) + .setParameter( "employed", true ) + .executeUpdate(); + + final Engineer engineer = session.find( Engineer.class, 0 ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + + @Test + public void testInsertSelect() { + doInHibernate( this::sessionFactory, session -> { + final int insertCount = session.createQuery( "insert into Engineer(id, name, employed, fellow) " + + "select d.id + " + (entityCount() * 2) + ", 'John Doe', true, false from Doctor d" ) + .executeUpdate(); + final AbstractMutationStrategyIdTest.Engineer engineer = + session.find( AbstractMutationStrategyIdTest.Engineer.class, entityCount() * 2 + 1 ); + assertEquals( entityCount(), insertCount ); + assertEquals( "John Doe", engineer.getName() ); + assertTrue( engineer.isEmployed() ); + assertFalse( engineer.isFellow() ); + }); + } + @Entity(name = "Person") @Inheritance(strategy = InheritanceType.JOINED) public static class Person { @Id - @GeneratedValue - private Long id; + private Integer id; private String name; private boolean employed; - public Long getId() { + public Integer getId() { return id; } - public void setId(Long id) { + public void setId(Integer id) { this.id = id; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyCompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyCompositeIdTest.java index 87652f1098af..0e2aa0fe2d88 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyCompositeIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyCompositeIdTest.java @@ -4,6 +4,7 @@ */ package org.hibernate.orm.test.bulkid; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; /** @@ -12,7 +13,12 @@ public class DefaultMutationStrategyCompositeIdTest extends AbstractMutationStrategyCompositeIdTest { @Override - protected Class getMultiTableBulkIdStrategyClass() { + protected Class getMultiTableMutationStrategyClass() { + return null; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { return null; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdTest.java new file mode 100644 index 000000000000..cb65394ac588 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdTest.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; + +public class DefaultMutationStrategyGeneratedIdTest extends AbstractMutationStrategyGeneratedIdTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return null; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdWithOptimizerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdWithOptimizerTest.java new file mode 100644 index 000000000000..ab9dca1c80af --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdWithOptimizerTest.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; + +public class DefaultMutationStrategyGeneratedIdWithOptimizerTest extends AbstractMutationStrategyGeneratedIdWithOptimizerTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return null; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdentityTest.java new file mode 100644 index 000000000000..c101cfa5cb35 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyGeneratedIdentityTest.java @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) +public class DefaultMutationStrategyGeneratedIdentityTest extends AbstractMutationStrategyGeneratedIdentityTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return null; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyIdTest.java index b0d79fc18595..017832ab0b6f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/DefaultMutationStrategyIdTest.java @@ -4,6 +4,7 @@ */ package org.hibernate.orm.test.bulkid; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; /** @@ -12,7 +13,12 @@ public class DefaultMutationStrategyIdTest extends AbstractMutationStrategyIdTest { @Override - protected Class getMultiTableBulkIdStrategyClass() { + protected Class getMultiTableMutationStrategyClass() { + return null; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { return null; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyCompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyCompositeIdTest.java new file mode 100644 index 000000000000..6427198a71c6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyCompositeIdTest.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGlobalTemporaryTable.class) +public class GlobalTemporaryTableMutationStrategyCompositeIdTest extends AbstractMutationStrategyCompositeIdTest { + + @Override + protected Class getMultiTableMutationStrategyClass() { + return GlobalTemporaryTableMutationStrategy.class; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { + return GlobalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdTest.java new file mode 100644 index 000000000000..9e59892d23bc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdTest.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGlobalTemporaryTable.class) +public class GlobalTemporaryTableMutationStrategyGeneratedIdTest extends AbstractMutationStrategyGeneratedIdTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return GlobalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest.java new file mode 100644 index 000000000000..1b340369d524 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGlobalTemporaryTable.class) +public class GlobalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest extends AbstractMutationStrategyGeneratedIdWithOptimizerTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return GlobalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdentityTest.java new file mode 100644 index 000000000000..f6a23fc9290b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyGeneratedIdentityTest.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGlobalTemporaryTable.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGlobalTemporaryTableIdentity.class) +public class GlobalTemporaryTableMutationStrategyGeneratedIdentityTest extends AbstractMutationStrategyGeneratedIdentityTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return GlobalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyIdTest.java new file mode 100644 index 000000000000..a4ee11741f38 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/GlobalTemporaryTableMutationStrategyIdTest.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsGlobalTemporaryTable.class) +public class GlobalTemporaryTableMutationStrategyIdTest extends AbstractMutationStrategyIdTest { + + @Override + protected Class getMultiTableMutationStrategyClass() { + return GlobalTemporaryTableMutationStrategy.class; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { + return GlobalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyCompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyCompositeIdTest.java index f921d5a46bc5..557a8dff7d48 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyCompositeIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyCompositeIdTest.java @@ -5,6 +5,7 @@ package org.hibernate.orm.test.bulkid; import org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; /** @@ -13,7 +14,13 @@ public class InlineMutationStrategyCompositeIdTest extends AbstractMutationStrategyCompositeIdTest { @Override - protected Class getMultiTableBulkIdStrategyClass() { + protected Class getMultiTableMutationStrategyClass() { return InlineMutationStrategy.class; } + + @Override + protected Class getMultiTableInsertStrategyClass() { + // No inline strategy for insert + return null; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyIdTest.java index b4b7604cb23a..6eba68db499a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/InlineMutationStrategyIdTest.java @@ -5,6 +5,7 @@ package org.hibernate.orm.test.bulkid; import org.hibernate.query.sqm.mutation.internal.inline.InlineMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; /** @@ -13,7 +14,13 @@ public class InlineMutationStrategyIdTest extends AbstractMutationStrategyIdTest { @Override - protected Class getMultiTableBulkIdStrategyClass() { + protected Class getMultiTableMutationStrategyClass() { return InlineMutationStrategy.class; } + + @Override + protected Class getMultiTableInsertStrategyClass() { + // No inline strategy for insert + return null; + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyCompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyCompositeIdTest.java new file mode 100644 index 000000000000..20d7040bda96 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyCompositeIdTest.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsLocalTemporaryTable.class) +public class LocalTemporaryTableMutationStrategyCompositeIdTest extends AbstractMutationStrategyCompositeIdTest { + + @Override + protected Class getMultiTableMutationStrategyClass() { + return LocalTemporaryTableMutationStrategy.class; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { + return LocalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdTest.java new file mode 100644 index 000000000000..cbed391044a6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdTest.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsLocalTemporaryTable.class) +public class LocalTemporaryTableMutationStrategyGeneratedIdTest extends AbstractMutationStrategyGeneratedIdTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return LocalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest.java new file mode 100644 index 000000000000..36cc58ee164e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsLocalTemporaryTable.class) +public class LocalTemporaryTableMutationStrategyGeneratedIdWithOptimizerTest extends AbstractMutationStrategyGeneratedIdWithOptimizerTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return LocalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdentityTest.java new file mode 100644 index 000000000000..0909b1ddb01e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyGeneratedIdentityTest.java @@ -0,0 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsLocalTemporaryTable.class) +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsLocalTemporaryTableIdentity.class) +public class LocalTemporaryTableMutationStrategyGeneratedIdentityTest extends AbstractMutationStrategyGeneratedIdentityTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return LocalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyIdTest.java new file mode 100644 index 000000000000..cb0c05f1bb83 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/LocalTemporaryTableMutationStrategyIdTest.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsLocalTemporaryTable.class) +public class LocalTemporaryTableMutationStrategyIdTest extends AbstractMutationStrategyIdTest { + + @Override + protected Class getMultiTableMutationStrategyClass() { + return LocalTemporaryTableMutationStrategy.class; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { + return LocalTemporaryTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyCompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyCompositeIdTest.java new file mode 100644 index 000000000000..7506ed4d7b02 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyCompositeIdTest.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; + +public class PersistentTableMutationStrategyCompositeIdTest extends AbstractMutationStrategyCompositeIdTest { + + @Override + protected Class getMultiTableMutationStrategyClass() { + return PersistentTableMutationStrategy.class; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { + return PersistentTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdTest.java new file mode 100644 index 000000000000..4ab9a01e48d1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdTest.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; + +public class PersistentTableMutationStrategyGeneratedIdTest extends AbstractMutationStrategyGeneratedIdTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return PersistentTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdWithOptimizerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdWithOptimizerTest.java new file mode 100644 index 000000000000..c1597a0f84c4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdWithOptimizerTest.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; + +public class PersistentTableMutationStrategyGeneratedIdWithOptimizerTest extends AbstractMutationStrategyGeneratedIdWithOptimizerTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return PersistentTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdentityTest.java new file mode 100644 index 000000000000..cd8b7c0f18d0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyGeneratedIdentityTest.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; + +@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsIdentityColumns.class) +public class PersistentTableMutationStrategyGeneratedIdentityTest extends AbstractMutationStrategyGeneratedIdentityTest { + + @Override + protected Class getMultiTableInsertStrategyClass() { + return PersistentTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyIdTest.java new file mode 100644 index 000000000000..a6b5c769240a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bulkid/PersistentTableMutationStrategyIdTest.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.bulkid; + +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy; +import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableMutationStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; +import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; + +public class PersistentTableMutationStrategyIdTest extends AbstractMutationStrategyIdTest { + + @Override + protected Class getMultiTableMutationStrategyClass() { + return PersistentTableMutationStrategy.class; + } + + @Override + protected Class getMultiTableInsertStrategyClass() { + return PersistentTableInsertStrategy.class; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java index 438700875a3f..b7aab70829db 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/BulkManipulationTest.java @@ -18,6 +18,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.InformixDialect; +import org.hibernate.dialect.AbstractTransactSQLDialect; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MySQLDialect; @@ -524,6 +525,7 @@ public void testInsertWithMismatchedTypes() { @Test @SkipForDialect(value = CockroachDialect.class, comment = "https://github.com/cockroachdb/cockroach/issues/75101") + @SkipForDialect(value = AbstractTransactSQLDialect.class, comment = "T-SQL complains IDENTITY_INSERT is off when a value for an identity column is provided") @RequiresDialectFeature(value = DialectChecks.SupportsTemporaryTableIdentity.class, comment = "The use of the native generator leads to using identity which also needs to be supported on temporary tables") public void testInsertIntoSuperclassPropertiesFails() { TestData data = new TestData(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/naming/QualifiedNameParserTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/naming/QualifiedNameParserTests.java index 928a45846346..7f8226cbb8ec 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/naming/QualifiedNameParserTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/naming/QualifiedNameParserTests.java @@ -75,7 +75,7 @@ void testSimpleQuoted() { @Test void testIndividualQuotes() { - final String name = "`schema2`.`catalog2`.`tbl`"; + final String name = "`catalog2`.`schema2`.`tbl`"; test( name, NO_CATALOG, diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java index 0ad2fb4b22be..140161e73ddf 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java @@ -242,7 +242,7 @@ public boolean isMatch(Dialect dialect) { public static class SupportsTemporaryTable implements DialectCheck { public boolean isMatch(Dialect dialect) { - return dialect.supportsTemporaryTables(); + return dialect.getLocalTemporaryTableStrategy() != null || dialect.getGlobalTemporaryTableStrategy() != null; } } @@ -260,7 +260,13 @@ public boolean isMatch(Dialect dialect) { public static class SupportsTemporaryTableIdentity implements DialectCheck { public boolean isMatch(Dialect dialect) { - return dialect.supportsTemporaryTablePrimaryKey(); + return dialect.getLocalTemporaryTableStrategy() != null + && dialect.getLocalTemporaryTableStrategy().supportsTemporaryTablePrimaryKey() + || dialect.getGlobalTemporaryTableStrategy() != null + && dialect.getGlobalTemporaryTableStrategy().supportsTemporaryTablePrimaryKey() + // Persistent tables definitely support identity + || dialect.getLocalTemporaryTableStrategy() == null + && dialect.getGlobalTemporaryTableStrategy() == null; } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java index eb1117388123..e74be83c2f19 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java @@ -162,7 +162,7 @@ public static Collection collect final S[] classAnnotations; if ( methodPluralAnn != null ) { try { - methodAnnotations = (S[]) pluralAnnotationClass.getDeclaredMethods()[0].invoke( methodPluralAnn ); + methodAnnotations = (S[]) pluralAnnotationClass.getDeclaredMethod("value").invoke( methodPluralAnn ); } catch (Exception e) { throw new RuntimeException( e ); @@ -173,7 +173,7 @@ public static Collection collect } if ( classPluralAnn != null ) { try { - classAnnotations = (S[]) pluralAnnotationClass.getDeclaredMethods()[0].invoke( classPluralAnn ); + classAnnotations = (S[]) pluralAnnotationClass.getDeclaredMethod("value").invoke( classPluralAnn ); } catch (Exception e) { throw new RuntimeException( e ); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index 934c270c2f2f..7bcf4e0599ee 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -451,7 +451,35 @@ public boolean apply(Dialect dialect) { public static class SupportsTemporaryTable implements DialectFeatureCheck { public boolean apply(Dialect dialect) { - return dialect.supportsTemporaryTables(); + return dialect.getLocalTemporaryTableStrategy() != null || dialect.getGlobalTemporaryTableStrategy() != null; + } + } + + public static class SupportsLocalTemporaryTable implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return dialect.getLocalTemporaryTableStrategy() != null; + } + } + + public static class SupportsGlobalTemporaryTable implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return dialect.getGlobalTemporaryTableStrategy() != null; + } + } + + public static class SupportsLocalTemporaryTableIdentity implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return dialect.getIdentityColumnSupport().supportsIdentityColumns() + && dialect.getLocalTemporaryTableStrategy() != null + && dialect.getLocalTemporaryTableStrategy().supportsTemporaryTablePrimaryKey(); + } + } + + public static class SupportsGlobalTemporaryTableIdentity implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return dialect.getIdentityColumnSupport().supportsIdentityColumns() + && dialect.getGlobalTemporaryTableStrategy() != null + && dialect.getGlobalTemporaryTableStrategy().supportsTemporaryTablePrimaryKey(); } }