From ba5ef1b14904a31f9e07b7b3ebc574d315b1f2fd Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 11 Nov 2019 16:09:11 -0600 Subject: [PATCH] HHH-13715 - working support for "multi-table" HQL/Criteria UPDATE and DELETE queries; local-temp-table support works tested on H2. I believe cte support works as well - need to set up and environment with a database supporting it. There is a problem saving entities with secondary tables atm so for now i cannot create the data to assert that the correct rows were deleted. But looking at the executed SQL it works --- .../boot/model/relational/Exportable.java | 2 +- .../dialect/AbstractTransactSQLDialect.java | 26 +- .../org/hibernate/dialect/Cache71Dialect.java | 4 +- .../org/hibernate/dialect/DB297Dialect.java | 4 +- .../org/hibernate/dialect/DB2Dialect.java | 4 +- .../org/hibernate/dialect/DerbyDialect.java | 48 +-- .../java/org/hibernate/dialect/Dialect.java | 10 +- .../java/org/hibernate/dialect/H2Dialect.java | 34 +- .../dialect/HANAColumnStoreDialect.java | 4 +- .../dialect/HANARowStoreDialect.java | 4 +- .../org/hibernate/dialect/HSQLDialect.java | 3 +- .../hibernate/dialect/InformixDialect.java | 40 +- .../org/hibernate/dialect/IngresDialect.java | 3 +- .../org/hibernate/dialect/MySQLDialect.java | 41 ++- .../hibernate/dialect/Oracle8iDialect.java | 3 +- .../org/hibernate/dialect/Oracle9Dialect.java | 3 +- .../dialect/PostgreSQL81Dialect.java | 4 +- .../dialect/PostgreSQL82Dialect.java | 4 +- .../org/hibernate/dialect/SAPDBDialect.java | 4 +- .../hibernate/dialect/TeradataDialect.java | 5 +- .../hibernate/dialect/TimesTenDialect.java | 3 +- .../engine/jdbc/spi/JdbcServices.java | 4 + .../hibernate/internal/util/StringHelper.java | 7 + .../internal/DatabaseSnapshotExecutor.java | 8 +- .../MetamodelSelectBuilderProcess.java | 3 - .../SingleIdEntityLoaderDynamicBatch.java | 2 +- .../loader/internal/SingleIdLoadPlan.java | 2 +- .../metamodel/mapping/ColumnConsumer.java | 19 + .../metamodel/mapping/EntityMappingType.java | 8 + .../mapping/ForeignKeyDescriptor.java | 32 +- .../metamodel/mapping/ModelPart.java | 19 +- .../mapping/PluralAttributeMapping.java | 2 + .../metamodel/mapping/Queryable.java | 1 + .../BasicValuedSingularAttributeMapping.java | 6 + .../internal/EmbeddedAttributeMapping.java | 9 +- .../internal/MappingModelCreationHelper.java | 20 +- .../internal/PluralAttributeMappingImpl.java | 16 + .../internal/SimpleForeignKeyDescriptor.java | 59 +++ .../domain/AbstractIdentifiableType.java | 59 ++- .../model/domain/IdentifiableDomainType.java | 4 + .../model/domain/internal/EntityTypeImpl.java | 2 +- .../internal/MappedSuperclassTypeImpl.java | 9 + .../entity/AbstractEntityPersister.java | 33 +- .../entity/JoinedSubclassEntityPersister.java | 20 + .../entity/SingleTableEntityPersister.java | 20 + .../entity/UnionSubclassEntityPersister.java | 21 ++ .../org/hibernate/query/spi/QueryEngine.java | 34 +- .../internal/ConcreteSqmSelectQueryPlan.java | 12 +- .../sqm/internal/SimpleDeleteQueryPlan.java | 10 +- .../internal/SqmIdSelectGenerator.java | 140 +++++++ .../internal/SqmMutationStrategyHelper.java | 108 ++++-- .../cte/AbstractCteMutationHandler.java | 51 +++ .../cte/CteBasedMutationStrategy.java | 68 +++- .../internal/cte/CteDeleteHandler.java | 260 +++++++++++++ .../internal/cte/CteUpdateHandler.java | 40 ++ .../idtable/AbstractTableBasedHandler.java | 342 ++++++++++++++++++ .../internal/idtable/AfterUseAction.java | 18 + .../internal/idtable/BeforeUseAction.java | 17 + .../mutation/internal/idtable/IdTable.java | 83 +++++ .../internal/idtable/IdTableColumn.java | 51 +++ .../internal/idtable/IdTableExporter.java | 25 ++ .../internal/idtable/IdTableHelper.java | 235 ++++++++++++ .../idtable/IdTableSessionUidColumn.java | 22 ++ .../idtable/LocalTemporaryTableStrategy.java | 57 ++- .../idtable/PhysicalIdTableExporter.java | 90 +++++ .../idtable/TableBasedDeleteHandler.java | 169 +++++++++ .../internal/idtable/TempIdTableExporter.java | 95 +++++ .../UnrestrictedTableBasedDeleteHandler.java | 96 +++++ .../internal/idtable/package-info.java | 21 ++ .../mutation/spi/AbstractMutationHandler.java | 3 +- ...n.java => SimpleSqmDeleteTranslation.java} | 4 +- ...er.java => SimpleSqmDeleteTranslator.java} | 4 +- .../sqm/sql/SqmInsertSelectTranslation.java | 37 ++ .../sqm/sql/SqmInsertSelectTranslator.java | 17 + .../sqm/sql/SqmQuerySpecTranslation.java | 37 ++ ...etation.java => SqmSelectTranslation.java} | 8 +- ...onverter.java => SqmSelectTranslator.java} | 6 +- ...nterpretation.java => SqmTranslation.java} | 2 +- .../query/sqm/sql/SqmTranslatorFactory.java | 11 +- ....java => StandardSqmDeleteTranslator.java} | 22 +- .../StandardSqmInsertSelectTranslator.java | 92 +++++ ....java => StandardSqmSelectTranslator.java} | 28 +- .../domain/SqmPolymorphicRootDescriptor.java | 5 + .../sql/ast/SqlAstDeleteTranslator.java | 3 + .../sql/ast/SqlAstInsertSelectTranslator.java | 18 + .../sql/ast/SqlAstSelectTranslator.java | 4 +- .../sql/ast/SqlAstTranslatorFactory.java | 20 +- ...bstractSqlAstToJdbcOperationConverter.java | 10 +- .../spi/SqlAstToJdbcOperationConverter.java | 5 +- .../spi/StandardSqlAstDeleteTranslator.java | 53 ++- .../StandardSqlAstInsertSelectTranslator.java | 64 ++++ .../spi/StandardSqlAstSelectTranslator.java | 17 +- .../spi/StandardSqlAstTranslatorFactory.java | 11 +- .../hibernate/sql/ast/tree/cte/CteColumn.java | 37 ++ .../sql/ast/tree/cte/CteStatement.java | 45 +++ .../hibernate/sql/ast/tree/cte/CteTable.java | 193 ++++++++++ .../sql/ast/tree/cte/CteTableGroup.java | 113 ++++++ .../hibernate/sql/exec/spi/JdbcInsert.java | 13 + .../mapping/SecondaryTableTests.java | 94 +++++ .../orm/test/sql/ast/SmokeTests.java | 16 +- .../sql/exec/HqlDeleteExecutionTests.java | 76 +++- 101 files changed, 3455 insertions(+), 295 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ColumnConsumer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmIdSelectGenerator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AbstractTableBasedHandler.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AfterUseAction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/BeforeUseAction.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTable.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableColumn.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableExporter.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableHelper.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableSessionUidColumn.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PhysicalIdTableExporter.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TempIdTableExporter.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UnrestrictedTableBasedDeleteHandler.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/package-info.java rename hibernate-core/src/main/java/org/hibernate/query/sqm/sql/{SimpleSqmDeleteInterpretation.java => SimpleSqmDeleteTranslation.java} (89%) rename hibernate-core/src/main/java/org/hibernate/query/sqm/sql/{SimpleSqmDeleteToSqlAstConverter.java => SimpleSqmDeleteTranslator.java} (70%) create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslation.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmQuerySpecTranslation.java rename hibernate-core/src/main/java/org/hibernate/query/sqm/sql/{SqmSelectInterpretation.java => SqmSelectTranslation.java} (78%) rename hibernate-core/src/main/java/org/hibernate/query/sqm/sql/{SqmSelectToSqlAstConverter.java => SqmSelectTranslator.java} (60%) rename hibernate-core/src/main/java/org/hibernate/query/sqm/sql/{SqmInterpretation.java => SqmTranslation.java} (94%) rename hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/{StandardSqmDeleteToSqlAstConverter.java => StandardSqmDeleteTranslator.java} (84%) create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java rename hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/{StandardSqmSelectToSqlAstConverter.java => StandardSqmSelectTranslator.java} (95%) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstInsertSelectTranslator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcInsert.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SecondaryTableTests.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Exportable.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Exportable.java index 5a04c4b5a4..82b3817c79 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Exportable.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/Exportable.java @@ -19,5 +19,5 @@ public interface Exportable { * * @return The exporting identifier. */ - public String getExportIdentifier(); + String getExportIdentifier(); } 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 80609bb641..94808ed5e0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -15,7 +15,7 @@ import java.util.Map; import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.CharIndexFunction; import org.hibernate.dialect.function.NoArgSQLFunction; @@ -24,7 +24,10 @@ import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.dialect.identity.AbstractTransactSQLIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction; +import org.hibernate.query.sqm.mutation.internal.idtable.IdTable; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.type.StandardBasicTypes; @@ -211,20 +214,13 @@ abstract class AbstractTransactSQLDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { - throw new NotYetImplementedFor6Exception( getClass() ); - -// return new LocalTemporaryTableBulkIdStrategy( -// new IdTableSupportStandardImpl() { -// @Override -// public String generateIdTableName(String baseName) { -// return "#" + baseName; -// } -// }, + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor) { + return new LocalTemporaryTableStrategy( + new IdTable( entityDescriptor, basename -> "#" + basename ), // // sql-server, at least needed this dropped after use; strange! -// AfterUseAction.DROP, -// TempTableDdlTransactionHandling.NONE -// ); + AfterUseAction.DROP, + TempTableDdlTransactionHandling.NONE + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java index a726624850..41830f595a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Cache71Dialect.java @@ -38,7 +38,7 @@ import org.hibernate.exception.internal.CacheSQLExceptionConversionDelegate; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.Lockable; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.CacheJoinFragment; @@ -444,7 +444,7 @@ public class Cache71Dialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( // new IdTableSupportStandardImpl() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB297Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB297Dialect.java index 89b599bfbc..69e8ebc7b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB297Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB297Dialect.java @@ -10,7 +10,7 @@ import java.sql.Types; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.dialect.function.DB2SubstringFunction; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.type.descriptor.sql.CharTypeDescriptor; import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; @@ -36,7 +36,7 @@ public class DB297Dialect extends DB2Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // // Starting in DB2 9.7, "real" global temporary tables that can be shared between sessions 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 bc6059b54f..a8465e953d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -33,7 +33,7 @@ import org.hibernate.engine.spi.RowSelection; import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.internal.util.JdbcExceptionHelper; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB2DatabaseImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; @@ -393,7 +393,7 @@ public class DB2Dialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // // Prior to DB2 9.7, "real" global temporary tables that can be shared between sessions diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java index b699ba9f76..0024ee0182 100755 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -13,7 +13,7 @@ import java.sql.Types; import java.util.Locale; import org.hibernate.MappingException; -import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.dialect.function.AnsiTrimFunction; import org.hibernate.dialect.function.DerbyConcatFunction; import org.hibernate.dialect.pagination.AbstractLimitHandler; @@ -24,7 +24,11 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.spi.RowSelection; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction; +import org.hibernate.query.sqm.mutation.internal.idtable.IdTable; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.CaseFragment; import org.hibernate.sql.DerbyCaseFragment; @@ -590,31 +594,27 @@ public class DerbyDialect extends DB2Dialect { * The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection. * * - * {@link DB2Dialect} returns a {@link org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy} that + * {@link DB2Dialect} returns a {@link org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy} that * will make temporary tables created at startup and hence unavailable for subsequent connections.
* see HHH-10238. - *

- * @return - * @param runtimeRootEntityDescriptor */ @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { - throw new NotYetImplementedFor6Exception( getClass() ); -// return new LocalTemporaryTableBulkIdStrategy(new IdTableSupportStandardImpl() { -// @Override -// public String generateIdTableName(String baseName) { -// return "session." + super.generateIdTableName( baseName ); -// } -// -// @Override -// public String getCreateIdTableCommand() { -// return "declare global temporary table"; -// } -// -// @Override -// public String getCreateIdTableStatementOptions() { -// return "not logged"; -// } -// }, AfterUseAction.CLEAN, null); + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { + return new LocalTemporaryTableStrategy( + new IdTable( rootEntityDescriptor, basename -> "HT_" + basename ), + () -> new TempIdTableExporter() { + @Override + protected String getCreateCommand() { + return "declare global temporary table"; + } + + @Override + protected String getCreateOptions() { + return "not logged"; + } + }, + AfterUseAction.CLEAN, + TempTableDdlTransactionHandling.NONE + ); } } 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 52b63c2626..6aac973dc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -92,7 +92,7 @@ import org.hibernate.mapping.Constraint; import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.Index; import org.hibernate.mapping.Table; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.Lockable; import org.hibernate.procedure.internal.StandardCallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; @@ -101,6 +101,7 @@ import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; +import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ANSICaseFragment; import org.hibernate.sql.ANSIJoinFragment; @@ -110,10 +111,7 @@ import org.hibernate.sql.JoinFragment; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.ANSICaseExpressionWalker; import org.hibernate.sql.ast.spi.CaseExpressionWalker; -import org.hibernate.sql.ast.spi.SqlAstWalker; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; -import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; -import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.tool.hbm2ddl.SchemaUpdate; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; @@ -1517,7 +1515,7 @@ public abstract class Dialect implements ConversionContext { return getCreateTableString(); } - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); } @@ -3114,7 +3112,7 @@ public abstract class Dialect implements ConversionContext { * Note that {@link SessionFactoryOptions#getSqmTranslatorFactory()} has higher * precedence as it comes directly from the user config * - * @see org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter + * @see StandardSqmSelectTranslator * @see QueryEngine#getSqmTranslatorFactory() */ public SqmTranslatorFactory getSqmTranslatorFactory() { 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 83a79d6e3e..8051252090 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -10,8 +10,8 @@ import java.sql.SQLException; import java.sql.Types; import org.hibernate.JDBCException; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.PessimisticLockException; +import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.function.AvgWithArgumentCastFunction; import org.hibernate.dialect.function.NoArgSQLFunction; @@ -32,7 +32,11 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.ReflectHelper; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.persister.entity.Queryable; +import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction; +import org.hibernate.query.sqm.mutation.internal.idtable.IdTable; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; @@ -368,26 +372,12 @@ public class H2Dialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { - throw new NotYetImplementedFor6Exception( getClass() ); - -// return new LocalTemporaryTableBulkIdStrategy( -// new IdTableSupportStandardImpl() { -// @Override -// public String getCreateIdTableCommand() { -// return "create cached local temporary table if not exists"; -// } -// -// @Override -// public String getCreateIdTableStatementOptions() { -// // actually 2 different options are specified here: -// // 1) [on commit drop] - says to drop the table on transaction commit -// // 2) [transactional] - says to not perform an implicit commit of any current transaction -// return "on commit drop transactional"; } -// }, -// AfterUseAction.CLEAN, -// TempTableDdlTransactionHandling.NONE -// ); + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor) { + return new LocalTemporaryTableStrategy( + new IdTable( entityDescriptor, basename -> "HT_" + basename ), + AfterUseAction.NONE, + TempTableDdlTransactionHandling.NONE + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java index eb5ddb0a9d..73eb048f3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANAColumnStoreDialect.java @@ -10,7 +10,7 @@ import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.type.StandardBasicTypes; @@ -48,7 +48,7 @@ public class HANAColumnStoreDialect extends AbstractHANADialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( new IdTableSupportStandardImpl() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java index c0be3951c7..e7540cdf3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANARowStoreDialect.java @@ -7,7 +7,7 @@ package org.hibernate.dialect; import org.hibernate.NotYetImplementedFor6Exception; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; /** @@ -36,7 +36,7 @@ public class HANARowStoreDialect extends AbstractHANADialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( new IdTableSupportStandardImpl() { 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 97386e4050..87d8ec9053 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -42,6 +42,7 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Lockable; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; @@ -503,7 +504,7 @@ public class HSQLDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // // Hibernate uses this information for temporary tables that it uses for its own operations diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java index 8ed3636771..e91008712e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/InformixDialect.java @@ -11,6 +11,7 @@ import java.sql.Types; import java.util.Locale; import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.NvlFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; @@ -25,7 +26,11 @@ import org.hibernate.dialect.unique.UniqueDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.internal.util.JdbcExceptionHelper; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction; +import org.hibernate.query.sqm.mutation.internal.idtable.IdTable; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorInformixDatabaseImpl; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; @@ -279,24 +284,23 @@ public class InformixDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { - throw new NotYetImplementedFor6Exception( getClass() ); + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { + return new LocalTemporaryTableStrategy( + new IdTable( rootEntityDescriptor, basename -> "HT_" + basename ), + () -> new TempIdTableExporter() { + @Override + protected String getCreateCommand() { + return "create temp table"; + } -// return new LocalTemporaryTableBulkIdStrategy( -// new IdTableSupportStandardImpl() { -// @Override -// public String getCreateIdTableCommand() { -// return "create temp table"; -// } -// -// @Override -// public String getCreateIdTableStatementOptions() { -// return "with no log"; -// } -// }, -// AfterUseAction.CLEAN, -// null -// ); + @Override + protected String getCreateOptions() { + return "with no log"; + } + }, + AfterUseAction.NONE, + TempTableDdlTransactionHandling.NONE + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java index a579f476fe..06a2a03bcc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/IngresDialect.java @@ -17,6 +17,7 @@ import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.dialect.pagination.FirstLimitHandler; import org.hibernate.dialect.pagination.LegacyFirstLimitHandler; import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.tool.schema.extract.internal.SequenceNameExtractorImpl; @@ -270,7 +271,7 @@ public class IngresDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( 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 7d896e3ba9..f8f8221711 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -17,6 +17,7 @@ import org.hibernate.JDBCException; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NullPrecedence; import org.hibernate.PessimisticLockException; +import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.StandardSQLFunction; @@ -33,7 +34,11 @@ import org.hibernate.exception.LockTimeoutException; import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.mapping.Column; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction; +import org.hibernate.query.sqm.mutation.internal.idtable.IdTable; +import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy; +import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.type.StandardBasicTypes; @@ -343,24 +348,24 @@ public class MySQLDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { - throw new NotYetImplementedFor6Exception( getClass() ); + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { -// return new LocalTemporaryTableBulkIdStrategy( -// new IdTableSupportStandardImpl() { -// @Override -// public String getCreateIdTableCommand() { -// return "create temporary table if not exists"; -// } -// -// @Override -// public String getDropIdTableCommand() { -// return "drop temporary table"; -// } -// }, -// AfterUseAction.DROP, -// TempTableDdlTransactionHandling.NONE -// ); + return new LocalTemporaryTableStrategy( + new IdTable( rootEntityDescriptor, basename -> "HT_" + basename ), + () -> new TempIdTableExporter() { + @Override + protected String getCreateCommand() { + return "create temporary table if not exists"; + } + + @Override + protected String getDropCommand() { + return "drop temporary table"; + } + }, + AfterUseAction.NONE, + TempTableDdlTransactionHandling.NONE + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java index ea6fa7b403..b50a0f2853 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle8iDialect.java @@ -35,6 +35,7 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.internal.util.JdbcExceptionHelper; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.procedure.internal.StandardCallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; @@ -622,7 +623,7 @@ public class Oracle8iDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java index 85e09ad0cf..f25fac12cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Oracle9Dialect.java @@ -17,6 +17,7 @@ import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.JdbcExceptionHelper; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorOracleDatabaseImpl; @@ -344,7 +345,7 @@ public class Oracle9Dialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java index 77aa95f79c..a2e9af88c7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL81Dialect.java @@ -36,7 +36,7 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.internal.util.JdbcExceptionHelper; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.procedure.internal.PostgresCallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; @@ -366,7 +366,7 @@ public class PostgreSQL81Dialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new LocalTemporaryTableBulkIdStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL82Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL82Dialect.java index 27281b505b..fcf8ccd7bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL82Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQL82Dialect.java @@ -8,7 +8,7 @@ package org.hibernate.dialect; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.boot.model.TypeContributions; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.PostgresUUIDType; @@ -33,7 +33,7 @@ public class PostgreSQL82Dialect extends PostgreSQL81Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new LocalTemporaryTableBulkIdStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SAPDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SAPDBDialect.java index b28122a793..46816a0b3e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SAPDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SAPDBDialect.java @@ -15,7 +15,7 @@ import org.hibernate.dialect.function.NoArgSQLFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.StandardSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.CaseFragment; import org.hibernate.sql.DecodeCaseFragment; @@ -226,7 +226,7 @@ public class SAPDBDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new LocalTemporaryTableBulkIdStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TeradataDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/TeradataDialect.java index b1ff43a050..c50a90df27 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TeradataDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TeradataDialect.java @@ -5,6 +5,7 @@ * See the lgpl.txt file in the root directory or . */ package org.hibernate.dialect; + import java.sql.Types; import org.hibernate.HibernateException; @@ -12,7 +13,7 @@ import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.dialect.function.VarArgsSQLFunction; -import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.type.StandardBasicTypes; @@ -119,7 +120,7 @@ public class TeradataDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( this, AfterUseAction.CLEAN ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java index cd50e9b23c..13087d93bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java @@ -24,6 +24,7 @@ import org.hibernate.dialect.lock.UpdateLockingStrategy; import org.hibernate.dialect.pagination.FirstLimitHandler; import org.hibernate.dialect.pagination.LegacyFirstLimitHandler; import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Lockable; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; @@ -218,7 +219,7 @@ public class TimesTenDialect extends Dialect { } @Override - public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) { + public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) { throw new NotYetImplementedFor6Exception( getClass() ); // return new GlobalTemporaryTableBulkIdStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java index e03d7d725b..2c27c3f527 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java @@ -94,4 +94,8 @@ public interface JdbcServices extends Service { default JdbcMutationExecutor getJdbcUpdateExecutor() { return StandardJdbcMutationExecutor.INSTANCE; } + + default JdbcMutationExecutor getJdbcInsertExecutor() { + return StandardJdbcMutationExecutor.INSTANCE; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index ef0a99b091..b53aa747ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -119,6 +119,13 @@ public final class StringHelper { return new String( buffer ); } + public static void repeat(String string, int times, String separator, StringBuilder buffer) { + buffer.append( string ); + for ( int i = 1; i < times; i++ ) { + buffer.append( separator ).append( string ); + } + } + public static String replace(String template, String placeholder, String replacement) { return replace( template, placeholder, replacement, false ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/DatabaseSnapshotExecutor.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/DatabaseSnapshotExecutor.java index 191d1d88c4..6441822296 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/DatabaseSnapshotExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/DatabaseSnapshotExecutor.java @@ -100,9 +100,6 @@ class DatabaseSnapshotExecutor { final NavigablePath idPath = rootPath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ); entityDescriptor.getIdentifierMapping().visitColumns( - idPath, - rootTableGroup, - state, (col, tab, jdbcMapping) -> { final TableReference tableReference = rootTableGroup.resolveTableReference( tab ); @@ -151,9 +148,6 @@ class DatabaseSnapshotExecutor { contributorMapping -> { final NavigablePath attrPath = rootPath.append( contributorMapping.getAttributeName() ); contributorMapping.visitColumns( - attrPath, - rootTableGroup, - state, (columnExpression, containingTableExpression, jdbcMapping) -> { final TableReference tableReference = rootTableGroup.resolveTableReference( containingTableExpression ); @@ -203,7 +197,7 @@ class DatabaseSnapshotExecutor { final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); - jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory ).interpret( selectStatement ); + jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( selectStatement ); } Object[] loadDatabaseSnapshot(Object id, SharedSessionContractImplementor session) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java index aef5ba6a44..da66478518 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/MetamodelSelectBuilderProcess.java @@ -252,9 +252,6 @@ public class MetamodelSelectBuilderProcess { final List columnReferences = new ArrayList<>( numberOfKeyColumns ); keyPart.visitColumns( - keyPath, - rootTableGroup, - sqlAstCreationState, (columnExpression, containingTableExpression, jdbcMapping) -> { final TableReference tableReference = rootTableGroup.resolveTableReference( containingTableExpression ); columnReferences.add( diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdEntityLoaderDynamicBatch.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdEntityLoaderDynamicBatch.java index c0c438cfe7..6443f34382 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdEntityLoaderDynamicBatch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdEntityLoaderDynamicBatch.java @@ -99,7 +99,7 @@ public class SingleIdEntityLoaderDynamicBatch extends SingleIdEntityLoaderSup final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); - final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory ).interpret( sqlAstDescriptor.getSqlAst() ); + final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAstDescriptor.getSqlAst() ); final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( getLoadable().getIdentifierMapping().getJdbcTypeCount( sessionFactory.getTypeConfiguration() ) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdLoadPlan.java b/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdLoadPlan.java index cb0c0f2b6c..d87eb0b2fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdLoadPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/internal/SingleIdLoadPlan.java @@ -50,7 +50,7 @@ class SingleIdLoadPlan { final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); - final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory ).interpret( sqlAstDescriptor.getSqlAst() ); + final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAstDescriptor.getSqlAst() ); final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( restrictivePart.getJdbcTypeCount( sessionFactory.getTypeConfiguration() ) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ColumnConsumer.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ColumnConsumer.java new file mode 100644 index 0000000000..a4cd790db2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ColumnConsumer.java @@ -0,0 +1,19 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.metamodel.mapping; + +/** + * @author Steve Ebersole + */ +@FunctionalInterface +public interface ColumnConsumer { + // todo (6.0) : pass values `updateable`, `checkable`, etc + void accept( + String columnExpression, + String containingTableExpression, + JdbcMapping jdbcMapping); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java index 9643eb54e2..26ae68f998 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java @@ -152,6 +152,14 @@ public interface EntityMappingType extends ManagedMappingType, Loadable { // by default do nothing } + default void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + interface ConstraintOrderedTableConsumer { + void consume(String tableExpression, Supplier> tableKeyColumnVisitationSupplier); + } + @Override default void visitAttributeMappings(Consumer action) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java index effe568748..3e3e7cefde 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java @@ -18,7 +18,9 @@ import org.hibernate.sql.results.spi.DomainResultCreationState; /** * @author Steve Ebersole */ -public interface ForeignKeyDescriptor { +public interface ForeignKeyDescriptor extends VirtualModelPart { + String PART_NAME = "{fk}"; + DomainResult createDomainResult(NavigablePath collectionPath, TableGroup tableGroup, DomainResultCreationState creationState); Predicate generateJoinPredicate( @@ -27,4 +29,32 @@ public interface ForeignKeyDescriptor { JoinType joinType, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext); + + @Override + default String getPartName() { + return PART_NAME; + } + + /** + * Visits the FK "referring" columns + */ + @Override + default void visitColumns(ColumnConsumer consumer) { + visitReferringColumns( consumer ); + } + + void visitReferringColumns(ColumnConsumer consumer); + + void visitTargetColumns(ColumnConsumer consumer); + + void visitColumnMappings(FkColumnMappingConsumer consumer); + + interface FkColumnMappingConsumer { + void consume( + String referringTable, + String referringColumn, + String targetTable, + String targetColumn, + JdbcMapping jdbcMapping); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java index 0bee03eb54..d4d8478afc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ModelPart.java @@ -7,16 +7,11 @@ package org.hibernate.metamodel.mapping; import java.util.function.BiConsumer; -import java.util.function.Consumer; import org.hibernate.NotYetImplementedFor6Exception; -import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.NavigablePath; -import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; -import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.spi.DomainResult; import org.hibernate.sql.results.spi.DomainResultCreationState; import org.hibernate.query.sqm.sql.internal.DomainResultProducer; @@ -70,20 +65,8 @@ public interface ModelPart extends MappingModelExpressable { throw new NotYetImplementedFor6Exception( getClass() ); } - default void visitColumns( - NavigablePath navigablePath, - TableGroup tableGroup, - DomainResultCreationState creationState, - ColumnConsumer consumer) { + default void visitColumns(ColumnConsumer consumer) { } - @FunctionalInterface - interface ColumnConsumer { - // todo (6.0) : pass values `updateable`, `checkable`, etc - void accept( - String columnExpression, - String containingTableExpression, - JdbcMapping jdbcMapping); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java index 36325884a9..b30a84418e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java @@ -41,4 +41,6 @@ public interface PluralAttributeMapping default void visitFetchables(Consumer fetchableConsumer, EntityMappingType treatTargetType) { fetchableConsumer.accept( getElementDescriptor() ); } + + String getSeparateCollectionTable(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Queryable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Queryable.java index a0555c6f78..3bfe992406 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Queryable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Queryable.java @@ -7,6 +7,7 @@ package org.hibernate.metamodel.mapping; import java.util.function.Consumer; +import java.util.function.Supplier; import org.hibernate.boot.spi.SessionFactoryOptions; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java index d4f509facd..c35ef34fe0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedSingularAttributeMapping.java @@ -13,6 +13,7 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.metamodel.mapping.ColumnConsumer; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.SingularAttributeMapping; @@ -215,4 +216,9 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu TypeConfiguration typeConfiguration) { action.accept( getJdbcMapping() ); } + + @Override + public void visitColumns(ColumnConsumer consumer) { + consumer.accept( mappedColumnExpression, tableExpression, jdbcMapping ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index 570530fd4f..5c1de3b8a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -16,6 +16,7 @@ import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.metamodel.mapping.ColumnConsumer; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -129,12 +130,8 @@ public class EmbeddedAttributeMapping } @Override - public void visitColumns( - NavigablePath navigablePath, - TableGroup tableGroup, - DomainResultCreationState creationState, - ColumnConsumer consumer) { - getEmbeddableTypeDescriptor().visitColumns( navigablePath, tableGroup, creationState, consumer ); + public void visitColumns(ColumnConsumer consumer) { + getEmbeddableTypeDescriptor().visitColumns( consumer ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java index b246125be9..cf35df2831 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java @@ -25,6 +25,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -46,6 +47,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionMappingType; import org.hibernate.metamodel.mapping.CollectionPart; +import org.hibernate.metamodel.mapping.ColumnConsumer; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; @@ -136,6 +138,11 @@ public class MappingModelCreationHelper { return 1; } + @Override + public void visitColumns(ColumnConsumer consumer) { + consumer.accept( getMappedColumnExpression(), getContainingTableExpression(), getJdbcMapping() ); + } + @Override public void visitJdbcTypes( Consumer action, @@ -635,7 +642,7 @@ public class MappingModelCreationHelper { final CollectionPersister collectionDescriptor = domainModel.findCollectionDescriptor( bootValueMapping.getRole() ); assert collectionDescriptor != null; - String tableExpression = ( (Joinable) collectionDescriptor ).getTableName(); + final String tableExpression = ( (Joinable) collectionDescriptor ).getTableName(); final String sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( bootProperty.getName() ); @@ -823,6 +830,7 @@ public class MappingModelCreationHelper { return new PluralAttributeMappingImpl( attrName, + bootValueMapping, propertyAccess, entityMappingType -> contributorMetadata, collectionMappingType, @@ -869,6 +877,7 @@ public class MappingModelCreationHelper { final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget; return new SimpleForeignKeyDescriptor( + bootValueMapping.getKey().getTable().getName(), bootValueMapping.getKey().getColumnIterator().next().getText( dialect ), simpleFkTarget.getContainingTableExpression(), simpleFkTarget.getMappedColumnExpression(), @@ -896,10 +905,19 @@ public class MappingModelCreationHelper { fkTarget = referencedEntityDescriptor.findSubPart( bootValueMapping.getReferencedPropertyName() ); } + final JdbcServices jdbcServices = creationProcess.getCreationContext().getSessionFactory().getJdbcServices(); + if ( fkTarget instanceof BasicValuedModelPart ) { final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget; return new SimpleForeignKeyDescriptor( + creationProcess.getCreationContext() + .getBootstrapContext() + .getMetadataBuildingOptions() + .getPhysicalNamingStrategy().toPhysicalTableName( + bootValueMapping.getTable().getNameIdentifier(), + jdbcServices.getJdbcEnvironment() + ).getText(), bootValueMapping.getColumnIterator().next().getText( dialect ), simpleFkTarget.getContainingTableExpression(), simpleFkTarget.getMappedColumnExpression(), diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index bb0727c674..54aa094e4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -12,6 +12,7 @@ import org.hibernate.LockMode; import org.hibernate.engine.FetchStrategy; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.mapping.Collection; import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionMappingType; import org.hibernate.metamodel.mapping.CollectionPart; @@ -22,6 +23,7 @@ import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.Joinable; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAstCreationState; @@ -59,12 +61,14 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme private final CascadeStyle cascadeStyle; private final CollectionPersister collectionDescriptor; + private final String separateCollectionTable; private final String sqlAliasStem; @SuppressWarnings("WeakerAccess") public PluralAttributeMappingImpl( String attributeName, + Collection bootDescriptor, PropertyAccess propertyAccess, StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess, CollectionMappingType collectionMappingType, @@ -90,6 +94,13 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme this.collectionDescriptor = collectionDescriptor; this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName ); + + if ( bootDescriptor.isOneToMany() ) { + separateCollectionTable = null; + } + else { + separateCollectionTable = ( (Joinable) collectionDescriptor ).getTableName(); + } } @Override @@ -122,6 +133,11 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme return identifierDescriptor; } + @Override + public String getSeparateCollectionTable() { + return separateCollectionTable; + } + @Override public int getStateArrayPosition() { return stateArrayPosition; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java index 3bfc90389d..b12f0c2c84 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java @@ -6,10 +6,17 @@ */ package org.hibernate.metamodel.mapping.internal; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.mapping.ColumnConsumer; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.query.ComparisonOperator; import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.JoinType; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationState; @@ -23,21 +30,26 @@ import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.internal.domain.basic.BasicResult; import org.hibernate.sql.results.spi.DomainResult; import org.hibernate.sql.results.spi.DomainResultCreationState; +import org.hibernate.type.descriptor.java.JavaTypeDescriptor; +import org.hibernate.type.spi.TypeConfiguration; /** * @author Steve Ebersole */ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor { + private final String keyColumnContainingTable; private final String keyColumnExpression; private final String targetColumnContainingTable; private final String targetColumnExpression; private final JdbcMapping jdbcMapping; public SimpleForeignKeyDescriptor( + String keyColumnContainingTable, String keyColumnExpression, String targetColumnContainingTable, String targetColumnExpression, JdbcMapping jdbcMapping) { + this.keyColumnContainingTable = keyColumnContainingTable; this.keyColumnExpression = keyColumnExpression; this.targetColumnContainingTable = targetColumnContainingTable; this.targetColumnExpression = targetColumnExpression; @@ -114,4 +126,51 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor { keyReference ); } + + @Override + public JavaTypeDescriptor getJavaTypeDescriptor() { + return jdbcMapping.getJavaTypeDescriptor(); + } + + @Override + public void visitReferringColumns(ColumnConsumer consumer) { + consumer.accept( keyColumnContainingTable, keyColumnExpression, jdbcMapping ); + } + + @Override + public void visitTargetColumns(ColumnConsumer consumer) { + consumer.accept( targetColumnContainingTable, targetColumnExpression, jdbcMapping ); + } + + @Override + public void visitColumnMappings(FkColumnMappingConsumer consumer) { + consumer.consume( keyColumnContainingTable, keyColumnExpression, targetColumnContainingTable, targetColumnExpression, jdbcMapping ); + } + + @Override + public int getJdbcTypeCount(TypeConfiguration typeConfiguration) { + return 1; + } + + @Override + public List getJdbcMappings(TypeConfiguration typeConfiguration) { + return Collections.singletonList( jdbcMapping ); + } + + @Override + public void visitJdbcTypes( + Consumer action, + Clause clause, + TypeConfiguration typeConfiguration) { + action.accept( jdbcMapping ); + } + + @Override + public void visitJdbcValues( + Object value, + Clause clause, + JdbcValuesConsumer valuesConsumer, + SharedSessionContractImplementor session) { + valuesConsumer.consume( value, jdbcMapping ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java index ce86916368..89c8f09fb6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java @@ -10,12 +10,20 @@ import java.io.Serializable; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; +import javax.persistence.metamodel.Bindable; import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.SingularAttribute; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.model.domain.internal.AttributeContainer; +import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource; +import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; +import org.hibernate.query.sqm.SqmPathSource; +import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; -import org.hibernate.type.spi.TypeConfiguration; + +import org.jboss.logging.Logger; /** * Defines commonality for the JPA {@link IdentifiableType} types. JPA defines @@ -36,6 +44,8 @@ public abstract class AbstractIdentifiableType private final boolean hasIdClass; private SingularPersistentAttribute id; private Set> idClassAttributes; + private SqmPathSource identifierDescriptor; + private final boolean isVersioned; private SingularPersistentAttribute versionAttribute; @@ -66,6 +76,11 @@ public abstract class AbstractIdentifiableType return (InFlightAccessImpl) super.getInFlightAccess(); } + @Override + public SqmPathSource getIdentifierDescriptor() { + return identifierDescriptor; + } + public boolean hasIdClass() { return hasIdClass; } @@ -173,7 +188,6 @@ public abstract class AbstractIdentifiableType * * @return IdClass attributes or {@code null} */ - @SuppressWarnings("unchecked") public Set> getIdClassAttributesSafely() { if ( !hasIdClass() ) { return null; @@ -335,6 +349,47 @@ public abstract class AbstractIdentifiableType @Override public void finishUp() { managedTypeAccess.finishUp(); + + identifierDescriptor = interpretIdDescriptor(); + } + } + + private static final Logger log = Logger.getLogger( AbstractIdentifiableType.class ); + + private SqmPathSource interpretIdDescriptor() { + log.tracef( "Interpreting domain-model identifier descriptor" ); + + if ( getSuperType() != null ) { + return getSuperType().getIdentifierDescriptor(); + } + else if ( id != null ) { + // simple id or aggregate composite id + final SimpleDomainType type = id.getType(); + if ( type instanceof BasicDomainType ) { + //noinspection unchecked + return new BasicSqmPathSource( + EntityIdentifierMapping.ROLE_LOCAL_NAME, + (BasicDomainType) type, + Bindable.BindableType.SINGULAR_ATTRIBUTE + ); + } + else { + assert type instanceof EmbeddableDomainType; + final EmbeddableDomainType compositeType = (EmbeddableDomainType) type; + //noinspection unchecked + return new EmbeddedSqmPathSource( + EntityIdentifierMapping.ROLE_LOCAL_NAME, + compositeType, + Bindable.BindableType.SINGULAR_ATTRIBUTE + ); + } + } + else if ( idClassAttributes != null && ! idClassAttributes.isEmpty() ) { + // non-aggregate composite id + throw new NotYetImplementedFor6Exception( getClass() ); + } + else { + throw new UnsupportedOperationException( "Could not build SqmPathSource for entity identifier : " + getTypeName() ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/IdentifiableDomainType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/IdentifiableDomainType.java index f2d00d8c98..5d2a0c3f34 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/IdentifiableDomainType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/IdentifiableDomainType.java @@ -11,12 +11,16 @@ import java.util.function.Consumer; import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.SingularAttribute; +import org.hibernate.query.sqm.SqmPathSource; + /** * Extension to the JPA {@link IdentifiableType} contract * * @author Steve Ebersole */ public interface IdentifiableDomainType extends ManagedDomainType, IdentifiableType { + SqmPathSource getIdentifierDescriptor(); + @Override SingularPersistentAttribute getId(Class type); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java index 84ec2b3781..9a92caa6d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/EntityTypeImpl.java @@ -18,8 +18,8 @@ import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; -import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.hql.spi.SqmCreationState; +import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java index 9df376c85f..7e30bbca77 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/MappedSuperclassTypeImpl.java @@ -6,13 +6,22 @@ */ package org.hibernate.metamodel.model.domain.internal; +import javax.persistence.metamodel.Bindable; + +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.graph.spi.SubGraphImplementor; +import org.hibernate.mapping.Component; import org.hibernate.mapping.MappedSuperclass; +import org.hibernate.mapping.Property; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.model.domain.AbstractIdentifiableType; import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType; +import org.hibernate.query.sqm.SqmPathSource; +import org.hibernate.type.BasicType; +import org.hibernate.type.Type; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** 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 3ea3b79fd5..99d715bdb8 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 @@ -130,6 +130,7 @@ import org.hibernate.mapping.Component; import org.hibernate.mapping.Formula; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Table; @@ -166,6 +167,8 @@ import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.Setter; import org.hibernate.query.ComparisonOperator; import org.hibernate.query.NavigablePath; +import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; +import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.Alias; @@ -6189,14 +6192,40 @@ public abstract class AbstractEntityPersister final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory(); if ( isMultiTable() ) { - sqmMultiTableMutationStrategy = null; - //sessionFactory.getJdbcServices().getJdbcEnvironment().getDialect().getFallbackSqmMutationStrategy( this ) + sqmMultiTableMutationStrategy = interpretSqmMultiTableStrategy( + this, + creationProcess + ); } else { sqmMultiTableMutationStrategy = null; } } + protected static SqmMultiTableMutationStrategy interpretSqmMultiTableStrategy( + AbstractEntityPersister entityMappingDescriptor, + MappingModelCreationProcess creationProcess) { + assert entityMappingDescriptor.isMultiTable(); + + if ( entityMappingDescriptor.getSuperMappingType() != null ) { + return entityMappingDescriptor.getSuperMappingType().getSqmMultiTableMutationStrategy(); + } + + // we need the boot model so we can have access to the Table + final RootClass entityBootDescriptor = (RootClass) creationProcess.getCreationContext() + .getBootModel() + .getEntityBinding( entityMappingDescriptor.getRootEntityName() ); + final Table rootTable = entityBootDescriptor.getRootTable(); + + return SqmMutationStrategyHelper.resolveStrategy( + entityBootDescriptor, + entityMappingDescriptor, + creationProcess.getCreationContext().getSessionFactory().getSessionFactoryOptions() , + creationProcess.getCreationContext().getSessionFactory().getServiceRegistry() + ); + + } + @Override public SqmMultiTableMutationStrategy getSqmMultiTableMutationStrategy() { return sqmMultiTableMutationStrategy; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index cabd9ec268..4dea660c11 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -15,6 +15,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Supplier; @@ -1251,6 +1252,25 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { } } + @Override + public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { + final AtomicInteger tablePositionWrapper = new AtomicInteger( ); + + for ( String tableName : constraintOrderedTableNames ) { + final int tablePosition = tablePositionWrapper.getAndIncrement(); + + consumer.consume( + tableName, + () -> columnConsumer -> { + final String[] keyColumnNames = constraintOrderedKeyColumnNames[tablePosition]; + for ( String column : keyColumnNames ) { + columnConsumer.accept( column, tableName, null ); + } + } + ); + } + } + private class CaseSearchedExpressionInfo{ CaseSearchedExpression caseSearchedExpression; List columnReferences = new ArrayList<>( ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index 3631a9e79c..a8385e35a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -14,6 +14,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Supplier; @@ -919,4 +920,23 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { ) ); } + + @Override + public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { + final AtomicInteger tablePositionWrapper = new AtomicInteger( ); + + for ( String tableName : constraintOrderedTableNames ) { + final int tablePosition = tablePositionWrapper.getAndIncrement(); + + consumer.consume( + tableName, + () -> columnConsumer -> { + final String[] keyColumnNames = constraintOrderedKeyColumnNames[tablePosition]; + for ( String column : keyColumnNames ) { + columnConsumer.accept( column, tableName, null ); + } + } + ); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index f3acc29dd5..ce3d573251 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -14,6 +14,7 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Supplier; @@ -417,6 +418,26 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { return isAbstract() || hasSubclasses(); } + + @Override + public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { + final AtomicInteger tablePositionWrapper = new AtomicInteger( ); + + for ( String tableName : constraintOrderedTableNames ) { + final int tablePosition = tablePositionWrapper.getAndIncrement(); + + consumer.consume( + tableName, + () -> columnConsumer -> { + final String[] keyColumnNames = constraintOrderedKeyColumnNames[tablePosition]; + for ( String column : keyColumnNames ) { + columnConsumer.accept( column, tableName, null ); + } + } + ); + } + } + @Override protected void buildDiscriminatorMapping() { if ( hasSubclasses() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java index 0167da915a..73db7f4b65 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java @@ -33,11 +33,13 @@ import org.hibernate.query.sqm.internal.SqmCreationOptionsStandard; import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry; import org.hibernate.query.sqm.spi.SqmCreationContext; -import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter; -import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator; +import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator; +import org.hibernate.query.sqm.sql.SqmSelectTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; -import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteToSqlAstConverter; -import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteTranslator; +import org.hibernate.query.sqm.sql.internal.StandardSqmInsertSelectTranslator; +import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.spi.SqlAstCreationContext; @@ -145,16 +147,15 @@ public class QueryEngine { return dialect.getSqmTranslatorFactory(); } - //noinspection Convert2Lambda return new SqmTranslatorFactory() { @Override - public SqmSelectToSqlAstConverter createSelectConverter( + public SqmSelectTranslator createSelectTranslator( QueryOptions queryOptions, DomainParameterXref domainParameterXref, QueryParameterBindings domainParameterBindings, LoadQueryInfluencers influencers, SqlAstCreationContext creationContext) { - return new StandardSqmSelectToSqlAstConverter( + return new StandardSqmSelectTranslator( queryOptions, domainParameterXref, domainParameterBindings, @@ -164,13 +165,28 @@ public class QueryEngine { } @Override - public SimpleSqmDeleteToSqlAstConverter createSimpleDeleteConverter( + public SimpleSqmDeleteTranslator createSimpleDeleteTranslator( QueryOptions queryOptions, DomainParameterXref domainParameterXref, QueryParameterBindings domainParameterBindings, LoadQueryInfluencers influencers, SqlAstCreationContext creationContext) { - return new StandardSqmDeleteToSqlAstConverter( + return new StandardSqmDeleteTranslator( + creationContext, + queryOptions, + domainParameterXref, + domainParameterBindings + ); + } + + @Override + public SqmInsertSelectTranslator createInsertSelectTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings, + LoadQueryInfluencers influencers, + SqlAstCreationContext creationContext) { + return new StandardSqmInsertSelectTranslator( creationContext, queryOptions, domainParameterXref, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java index ce4f499483..e87fab86d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java @@ -25,9 +25,9 @@ import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.SelectQueryPlan; -import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.SqmSelectTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; -import org.hibernate.query.sqm.sql.SqmSelectInterpretation; +import org.hibernate.query.sqm.sql.SqmSelectTranslation; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; @@ -164,7 +164,7 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory(); - final SqmSelectToSqlAstConverter sqmConverter = sqmTranslatorFactory.createSelectConverter( + final SqmSelectTranslator sqmConverter = sqmTranslatorFactory.createSelectTranslator( executionContext.getQueryOptions(), domainParameterXref, executionContext.getQueryParameterBindings(), @@ -172,14 +172,14 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { sessionFactory ); - final SqmSelectInterpretation interpretation = sqmConverter.interpret( sqm ); + final SqmSelectTranslation interpretation = sqmConverter.translate( sqm ); final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); - jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory ) - .interpret( interpretation.getSqlAst() ); + jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ) + .translate( interpretation.getSqlAst() ); this.jdbcParamsXref = SqmUtil.generateJdbcParamsXref( domainParameterXref, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java index b79c690ca5..b96034394d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java @@ -16,8 +16,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryParameterImplementor; -import org.hibernate.query.sqm.sql.SimpleSqmDeleteInterpretation; -import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter; +import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslation; +import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; @@ -55,7 +55,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { final QueryEngine queryEngine = factory.getQueryEngine(); final SqmTranslatorFactory converterFactory = queryEngine.getSqmTranslatorFactory(); - final SimpleSqmDeleteToSqlAstConverter converter = converterFactory.createSimpleDeleteConverter( + final SimpleSqmDeleteTranslator converter = converterFactory.createSimpleDeleteTranslator( executionContext.getQueryOptions(), domainParameterXref, executionContext.getQueryParameterBindings(), @@ -63,12 +63,12 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { factory ); - final SimpleSqmDeleteInterpretation sqmInterpretation = converter.interpret( sqmDelete ); + final SimpleSqmDeleteTranslation sqmInterpretation = converter.translate( sqmDelete ); final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); - final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteConverter( factory ); + final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory ); jdbcDelete = sqlAstTranslator.translate( sqmInterpretation.getSqlAst() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmIdSelectGenerator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmIdSelectGenerator.java new file mode 100644 index 0000000000..bf678bb903 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/SqmIdSelectGenerator.java @@ -0,0 +1,140 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.collections.Stack; +import org.hibernate.internal.util.collections.StandardStack; +import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.hql.spi.SqmCreationOptions; +import org.hibernate.query.hql.spi.SqmCreationProcessingState; +import org.hibernate.query.hql.spi.SqmCreationState; +import org.hibernate.query.sqm.SqmQuerySource; +import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl; +import org.hibernate.query.sqm.mutation.internal.idtable.IdTableSessionUidColumn; +import org.hibernate.query.sqm.spi.SqmCreationContext; +import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; +import org.hibernate.query.sqm.tree.domain.SqmPath; +import org.hibernate.query.sqm.tree.expression.SqmLiteral; +import org.hibernate.query.sqm.tree.from.SqmFromClause; +import org.hibernate.query.sqm.tree.from.SqmRoot; +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; +import org.hibernate.query.sqm.tree.select.SqmSelectClause; +import org.hibernate.query.sqm.tree.select.SqmSelectStatement; +import org.hibernate.query.sqm.tree.select.SqmSelection; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.type.StringType; + +import org.jboss.logging.Logger; + +/** + * Helper used to generate the SELECT for selection of an entity's + * identifier, here specifically intended to be used as the SELECT + * portion of a multi-table SQM mutation + * + * @author Steve Ebersole + */ +public class SqmIdSelectGenerator { + private static final Logger log = Logger.getLogger( SqmIdSelectGenerator.class ); + + public static SqmQuerySpec generateSqmEntityIdSelect( + SqmDeleteOrUpdateStatement sqmStatement, + ExecutionContext executionContext, + SessionFactoryImplementor sessionFactory) { + final SqmIdSelectGenerator generator = new SqmIdSelectGenerator( sqmStatement, executionContext, sessionFactory ); + return generator.process(); + } + + private final SqmDeleteOrUpdateStatement sourceSqmStatement; + private final ExecutionContext executionContext; + private final SqmCreationContext creationContext; + private final EntityDomainType entityType; + + public SqmIdSelectGenerator( + SqmDeleteOrUpdateStatement sourceSqmStatement, + ExecutionContext executionContext, + SqmCreationContext creationContext) { + this.sourceSqmStatement = sourceSqmStatement; + this.executionContext = executionContext; + this.creationContext = creationContext; + + final String targetEntityName = sourceSqmStatement.getTarget().getEntityName(); + this.entityType = creationContext.getJpaMetamodel().entity( targetEntityName ); + } + + private SqmQuerySpec process() { + final SqmQuerySpec sqmQuerySpec = new SqmQuerySpec( creationContext.getNodeBuilder() ); + + + final Stack processingStateStack = new StandardStack<>(); + + final SqmCreationState creationState = new SqmCreationState() { + @Override + public SqmCreationContext getCreationContext() { + return creationContext; + } + + @Override + public SqmCreationOptions getCreationOptions() { + return () -> false; + } + + @Override + public Stack getProcessingStateStack() { + return processingStateStack; + } + }; + + // temporary - used just for creating processingState + final SqmSelectStatement sqmSelectStatement = new SqmSelectStatement( creationContext.getNodeBuilder() ); + //noinspection unchecked + sqmSelectStatement.setQuerySpec( sqmQuerySpec ); + + final SqmCreationProcessingState processingState = new SqmQuerySpecCreationProcessingStateStandardImpl( + null, + sqmSelectStatement, + creationState + ); + + processingStateStack.push( processingState ); + + final SqmFromClause sqmFromClause = new SqmFromClause(); + sqmQuerySpec.setFromClause( sqmFromClause ); + + //noinspection unchecked + final SqmRoot sqmRoot = new SqmRoot( entityType, null, sourceSqmStatement.nodeBuilder() ); + sqmFromClause.addRoot( sqmRoot ); + + final SqmSelectClause sqmSelectClause = new SqmSelectClause( true, creationContext.getNodeBuilder() ); + sqmQuerySpec.setSelectClause( sqmSelectClause ); + applySelections( sqmQuerySpec, sqmRoot, processingState ); + + if ( sourceSqmStatement.getWhereClause() != null ) { + sqmQuerySpec.applyPredicate( sourceSqmStatement.getWhereClause().getPredicate() ); + } + + return sqmQuerySpec; + } + + private void applySelections( + SqmQuerySpec sqmQuerySpec, + SqmRoot sqmRoot, + SqmCreationProcessingState processingState) { + //noinspection unchecked + final SqmPath idPath = entityType.getIdentifierDescriptor().createSqmPath( sqmRoot, processingState.getCreationState() ); + + //noinspection unchecked + sqmQuerySpec.getSelectClause().add( + new SqmSelection( + idPath, + null, + processingState.getCreationState().getCreationContext().getNodeBuilder() + ) + ); + } + +} 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 d6abf8e453..93fe9bf55c 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 @@ -7,30 +7,46 @@ package org.hibernate.query.sqm.mutation.internal; import java.util.List; +import java.util.Map; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.boot.spi.SessionFactoryOptions; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.mapping.RootClass; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmUtil; +import org.hibernate.query.sqm.mutation.internal.idtable.IdTable; import org.hibernate.query.sqm.mutation.spi.DeleteHandler; import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.mutation.spi.UpdateHandler; +import org.hibernate.query.sqm.sql.SqmSelectTranslation; +import org.hibernate.query.sqm.sql.SqmSelectTranslator; +import org.hibernate.query.sqm.sql.SqmTranslatorFactory; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.domain.SqmSimplePath; import org.hibernate.query.sqm.tree.expression.SqmExpression; +import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate; +import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate; import org.hibernate.query.sqm.tree.predicate.SqmGroupedPredicate; import org.hibernate.query.sqm.tree.predicate.SqmInListPredicate; -import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate; import org.hibernate.query.sqm.tree.predicate.SqmJunctivePredicate; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; +import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.service.ServiceRegistry; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcParameter; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.exec.spi.JdbcSelect; /** * @author Steve Ebersole @@ -49,7 +65,7 @@ public class SqmMutationStrategyHelper { * entity hierarchy. */ public static SqmMultiTableMutationStrategy resolveStrategy( - RootClass bootRootEntityDescriptor, + RootClass bootEntityDescriptor, EntityPersister runtimeRootEntityDescriptor, SessionFactoryOptions options, ServiceRegistry serviceRegistry) { @@ -213,43 +229,59 @@ public class SqmMutationStrategyHelper { * or UPDATE SQM query */ public static List selectMatchingIds( - DomainParameterXref domainParameterXref, SqmDeleteOrUpdateStatement sqmDeleteStatement, + DomainParameterXref domainParameterXref, ExecutionContext executionContext) { - throw new NotYetImplementedFor6Exception( SqmMutationStrategyHelper.class ); + final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); + final QueryEngine queryEngine = factory.getQueryEngine(); + final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory(); -// final SqmQuerySpec sqmIdSelectQuerySpec = SqmIdSelectGenerator.generateSqmEntityIdSelect( -// sqmDeleteStatement, -// executionContext.getSession().getSessionFactory() -// ); -// -// final SqmSelectToSqlAstConverter sqmConverter = new SqmSelectToSqlAstConverter( -// executionContext.getQueryOptions(), -// domainParameterXref, -// executionContext.getDomainParameterBindingContext().getQueryParameterBindings(), -// executionContext.getLoadQueryInfluencers(), -// executionContext.getCallback(), -// executionContext.getSession().getSessionFactory() -// ); -// -// final SqmSelectStatement sqmIdSelect = new SqmSelectStatement( sqmIdSelectQuerySpec ); -// -// final SqmSelectInterpretation sqmSelectInterpretation = sqmConverter.interpret( sqmIdSelect ); -// -// final JdbcSelect jdbcSelect = SqlAstSelectToJdbcSelectConverter.interpret( -// sqmSelectInterpretation, -// executionContext.getSession().getSessionFactory() -// ); -// -// return JdbcSelectExecutorStandardImpl.INSTANCE.list( -// jdbcSelect, -// QueryHelper.buildJdbcParameterBindings( -// domainParameterXref, -// SqmConsumeHelper.generateJdbcParamsXref( domainParameterXref, sqmConverter ), -// executionContext -// ), -// executionContext, -// row -> row[0] -// ); + final SqmSelectTranslator selectConverter = sqmTranslatorFactory.createSelectTranslator( + executionContext.getQueryOptions(), + domainParameterXref, + executionContext.getQueryParameterBindings(), + executionContext.getLoadQueryInfluencers(), + factory + ); + + final SqmQuerySpec sqmIdSelectQuerySpec = SqmIdSelectGenerator.generateSqmEntityIdSelect( + sqmDeleteStatement, + executionContext, + factory + ); + + final SqmSelectStatement sqmIdSelect = new SqmSelectStatement( factory.getNodeBuilder() ); + //noinspection unchecked + sqmIdSelect.setQuerySpec( sqmIdSelectQuerySpec ); + + final SqmSelectTranslation sqmInterpretation = selectConverter.translate( sqmIdSelect ); + + final JdbcServices jdbcServices = factory.getJdbcServices(); + final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); + final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); + + final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( factory ).translate( sqmInterpretation.getSqlAst() ); + + final Map, Map>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref( + domainParameterXref, + sqmInterpretation::getJdbcParamsBySqmParam + ); + + final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( + executionContext.getQueryParameterBindings(), + domainParameterXref, + jdbcParamsXref, + // todo (6.0) : ugh. this one is important + null, + executionContext.getSession() + ); + + + return factory.getJdbcServices().getJdbcSelectExecutor().list( + jdbcSelect, + jdbcParameterBindings, + executionContext, + row -> row + ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java new file mode 100644 index 0000000000..e5a8ef71da --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java @@ -0,0 +1,51 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.cte; + +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler; +import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; +import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; +import org.hibernate.sql.ast.tree.cte.CteTable; + +/** + * Defines how identifier values are selected from the updatable/deletable tables. + * + * @author Evandro Pires da Silva + * @author Vlad Mihalcea + * @author Steve Ebersole + */ +public abstract class AbstractCteMutationHandler extends AbstractMutationHandler { + private final CteTable cteTable; + private final DomainParameterXref domainParameterXref; + private final CteBasedMutationStrategy strategy; + + public AbstractCteMutationHandler( + CteTable cteTable, + SqmDeleteOrUpdateStatement sqmStatement, + DomainParameterXref domainParameterXref, + CteBasedMutationStrategy strategy, + HandlerCreationContext creationContext) { + super( sqmStatement, creationContext ); + this.cteTable = cteTable; + this.domainParameterXref = domainParameterXref; + + this.strategy = strategy; + } + + public CteTable getCteTable() { + return cteTable; + } + + public DomainParameterXref getDomainParameterXref() { + return domainParameterXref; + } + + public CteBasedMutationStrategy getStrategy() { + return strategy; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java index 123b5f8088..330264fc7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteBasedMutationStrategy.java @@ -6,16 +6,21 @@ */ package org.hibernate.query.sqm.mutation.internal.cte; -import org.hibernate.NotYetImplementedFor6Exception; +import java.util.Locale; + import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.DeleteHandler; import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.mutation.spi.UpdateHandler; +import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; +import org.hibernate.sql.ast.tree.cte.CteTable; /** * @asciidoc @@ -79,9 +84,43 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy { public static final String SHORT_NAME = "cte"; public static final String TABLE_NAME = "id_cte"; + private final EntityPersister rootDescriptor; + private final CteTable cteTable; + public CteBasedMutationStrategy( EntityPersister rootDescriptor, BootstrapContext bootstrapContext) { + this.rootDescriptor = rootDescriptor; + + final Dialect dialect = bootstrapContext.getTypeConfiguration() + .getSessionFactory() + .getServiceRegistry() + .getService( JdbcServices.class ) + .getJdbcEnvironment() + .getDialect(); + + if ( !dialect.supportsNonQueryWithCTE() ) { + throw new UnsupportedOperationException( + getClass().getSimpleName() + + " can only be used with Dialects that support CTE that can take UPDATE or DELETE statements as well" + ); + } + + if ( !dialect.supportsValuesList() ) { + throw new UnsupportedOperationException( + getClass().getSimpleName() + + " can only be used with Dialects that support VALUES lists" + ); + } + + if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) { + throw new UnsupportedOperationException( + getClass().getSimpleName() + + " can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)" + ); + } + + this.cteTable = new CteTable( rootDescriptor, bootstrapContext ); } @Override @@ -89,7 +128,28 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy { SqmUpdateStatement sqmUpdateStatement, DomainParameterXref domainParameterXref, HandlerCreationContext creationContext) { - throw new NotYetImplementedFor6Exception( getClass() ); + checkMatch( sqmUpdateStatement, creationContext ); + + return new CteUpdateHandler( cteTable, sqmUpdateStatement, domainParameterXref, this, creationContext ); + } + + private void checkMatch(SqmDeleteOrUpdateStatement sqmStatement, HandlerCreationContext creationContext) { + final String targetEntityName = sqmStatement.getTarget().getEntityName(); + final EntityPersister targetEntityDescriptor = creationContext.getSessionFactory() + .getDomainModel() + .getEntityDescriptor( targetEntityName ); + + if ( targetEntityDescriptor != rootDescriptor && ! rootDescriptor.isSubclassEntityName( targetEntityDescriptor.getEntityName() ) ) { + throw new IllegalArgumentException( + String.format( + Locale.ROOT, + "Target of query [%s] did not match configured entity [%s]", + targetEntityName, + rootDescriptor.getEntityName() + ) + ); + } + } @Override @@ -97,6 +157,8 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy { SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, HandlerCreationContext creationContext) { - throw new NotYetImplementedFor6Exception( getClass() ); + checkMatch( sqmDeleteStatement, creationContext ); + + return new CteDeleteHandler( cteTable, sqmDeleteStatement, domainParameterXref, this, creationContext ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java new file mode 100644 index 0000000000..ef0d74c5ae --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java @@ -0,0 +1,260 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.cte; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.ColumnConsumer; +import org.hibernate.metamodel.mapping.MappingModelExpressable; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper; +import org.hibernate.query.sqm.mutation.spi.DeleteHandler; +import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; +import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; +import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.cte.CteTable; +import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SqlTuple; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcDelete; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +/** + * Bulk-id delete handler that uses CTE and VALUES lists. + * + * @author Evandro Pires da Silva + * @author Vlad Mihalcea + * @author Steve Ebersole + */ +@SuppressWarnings("WeakerAccess") +public class CteDeleteHandler extends AbstractCteMutationHandler implements DeleteHandler { + private final SqlAstTranslatorFactory sqlAstTranslatorFactory; + + protected CteDeleteHandler( + CteTable cteTable, + SqmDeleteStatement sqmDeleteStatement, + DomainParameterXref domainParameterXref, + CteBasedMutationStrategy strategy, + HandlerCreationContext creationContext) { + super( cteTable, sqmDeleteStatement, domainParameterXref, strategy, creationContext ); + + final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory(); + final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); + final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); + sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); + } + + @Override + public SqmDeleteStatement getSqmDeleteOrUpdateStatement() { + return (SqmDeleteStatement) super.getSqmDeleteOrUpdateStatement(); + } + + @Override + public int execute(ExecutionContext executionContext) { + final List ids = selectMatchingIds( executionContext ); + if ( ids == null || ids.isEmpty() ) { + return 0; + } + + final QuerySpec cteQuerySpec = getCteTable().createCteSubQuery( executionContext ); + + final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( getDomainParameterXref().getQueryParameterCount() ); + final QuerySpec cteDefinitionQuerySpec = getCteTable().createCteDefinition( + ids, + jdbcParameterBindings, + executionContext + ); + + // for every table to be deleted, create the CteStatement and execute it + + getEntityDescriptor().visitAttributeMappings( + attribute -> { + if ( attribute instanceof PluralAttributeMapping ) { + final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute; + + if ( pluralAttribute.getSeparateCollectionTable() != null ) { + // this collection has a separate collection table, meaning it is one of: + // 1) element-collection + // 2) many-to-many + // 3) one-to many using a dedicated join-table + // + // in all of these cases, we should clean up the matching rows in the + // collection table + + executeDelete( + cteDefinitionQuerySpec, + pluralAttribute.getSeparateCollectionTable(), + () -> columnConsumer -> pluralAttribute.getKeyDescriptor().visitReferringColumns( columnConsumer ), + pluralAttribute.getKeyDescriptor(), + cteQuerySpec, + jdbcParameterBindings, + executionContext + ); + } + } + } + ); + + getEntityDescriptor().visitConstraintOrderedTables( + (tableExpression, tableColumnsVisitationSupplier) -> { + executeDelete( + cteDefinitionQuerySpec, + tableExpression, + tableColumnsVisitationSupplier, + getEntityDescriptor().getIdentifierMapping(), + cteQuerySpec, + jdbcParameterBindings, + executionContext + ); + } + ); + + return ids.size(); + } + + private List selectMatchingIds(ExecutionContext executionContext) { + return SqmMutationStrategyHelper.selectMatchingIds( + getSqmDeleteOrUpdateStatement(), + getDomainParameterXref(), + executionContext + ); + } + + protected void executeDelete( + QuerySpec cteDefinition, + String targetTable, + Supplier> columnsToMatchVisitationSupplier, + MappingModelExpressable cteType, + QuerySpec cteSubQuery, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext) { + final CteStatement cteStatement = generateCteStatement( + cteDefinition, + targetTable, + columnsToMatchVisitationSupplier, + cteType, + cteSubQuery, + executionContext + ); + + final SessionFactoryImplementor sessionFactory = getCreationContext().getSessionFactory(); + + final JdbcDelete jdbcDelete = sqlAstTranslatorFactory.buildDeleteTranslator( sessionFactory ) + .translate( cteStatement ); + + + final LogicalConnectionImplementor logicalConnection = executionContext.getSession() + .getJdbcCoordinator() + .getLogicalConnection(); + + sessionFactory.getJdbcServices().getJdbcDeleteExecutor().execute( + jdbcDelete, + jdbcParameterBindings, + sql -> { + try { + return logicalConnection.getPhysicalConnection().prepareStatement( sql ); + } + catch (SQLException e) { + throw sessionFactory.getJdbcServices().getSqlExceptionHelper().convert( + e, + "Error performing DELETE", + sql + ); + } + }, + (integer, preparedStatement) -> {}, + executionContext + ); + } + + protected CteStatement generateCteStatement( + QuerySpec cteDefinition, + String targetTable, + Supplier> columnsToMatchVisitationSupplier, + MappingModelExpressable cteType, + QuerySpec cteSubQuery, + ExecutionContext executionContext) { + final DeleteStatement deleteStatement = generateCteConsumer( + targetTable, + columnsToMatchVisitationSupplier, + cteType, + cteSubQuery, + executionContext + ); + return new CteStatement( + cteDefinition, + CteBasedMutationStrategy.TABLE_NAME, + getCteTable(), + deleteStatement + ); + } + + + private DeleteStatement generateCteConsumer( + String targetTable, + Supplier> columnsToMatchVisitationSupplier, + MappingModelExpressable cteType, + QuerySpec cteSubQuery, + ExecutionContext executionContext) { + final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory(); + final TableReference targetTableReference = new TableReference( + targetTable, + null, + false, + sessionFactory + ); + + final List columnsToMatchReferences = new ArrayList<>(); + + columnsToMatchVisitationSupplier.get().accept( + (columnExpression, containingTableExpression, jdbcMapping) -> { + columnsToMatchReferences.add( + new ColumnReference( + targetTableReference, + columnExpression, + jdbcMapping, + sessionFactory + ) + ); + } + ); + + final Expression columnsToMatchExpression; + + if ( columnsToMatchReferences.size() == 1 ) { + columnsToMatchExpression = columnsToMatchReferences.get( 0 ); + } + else { + columnsToMatchExpression = new SqlTuple( columnsToMatchReferences, cteType ); + } + + return new DeleteStatement( + targetTableReference, + new InSubQueryPredicate( + columnsToMatchExpression, + cteSubQuery, + false + ) + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java new file mode 100644 index 0000000000..f6d8df79f0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.cte; + +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; +import org.hibernate.query.sqm.mutation.spi.UpdateHandler; +import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; +import org.hibernate.sql.ast.tree.cte.CteTable; +import org.hibernate.sql.exec.spi.ExecutionContext; + +/** + * + * @author Evandro Pires da Silva + * @author Vlad Mihalcea + * @author Steve Ebersole + */ +public class CteUpdateHandler + extends AbstractCteMutationHandler + implements UpdateHandler { + + public CteUpdateHandler( + CteTable cteTable, + SqmUpdateStatement sqmStatement, + DomainParameterXref domainParameterXref, + CteBasedMutationStrategy strategy, + HandlerCreationContext creationContext) { + super( cteTable, sqmStatement, domainParameterXref, strategy, creationContext ); + } + + @Override + public int execute(ExecutionContext executionContext) { + throw new NotYetImplementedFor6Exception(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AbstractTableBasedHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AbstractTableBasedHandler.java new file mode 100644 index 0000000000..abfc822d68 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AbstractTableBasedHandler.java @@ -0,0 +1,342 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.util.Collections; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.hibernate.LockMode; +import org.hibernate.boot.TempTableDdlTransactionHandling; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.ComparisonOperator; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.internal.SqmUtil; +import org.hibernate.query.sqm.mutation.internal.SqmIdSelectGenerator; +import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler; +import org.hibernate.query.sqm.mutation.spi.Handler; +import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; +import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation; +import org.hibernate.query.sqm.sql.SqmSelectTranslator; +import org.hibernate.query.sqm.sql.SqmTranslatorFactory; +import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; +import org.hibernate.query.sqm.tree.expression.SqmLiteral; +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; +import org.hibernate.query.sqm.tree.select.SqmSelection; +import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.SqlAstInsertSelectTranslator; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.expression.QueryLiteral; +import org.hibernate.sql.ast.tree.from.StandardTableGroup; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; +import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcInsert; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.results.internal.SqlSelectionImpl; +import org.hibernate.type.UUIDCharType; + +/** + * Support for {@link Handler} implementations + * + * @author Steve Ebersole + */ +public abstract class AbstractTableBasedHandler extends AbstractMutationHandler { + private final IdTable idTable; + private final TempTableDdlTransactionHandling ddlTransactionHandling; + private final BeforeUseAction beforeUseAction; + private final AfterUseAction afterUseAction; + + private final DomainParameterXref domainParameterXref; + + private final Function sessionUidAccess; + + private final Supplier exporterSupplier; + + + public AbstractTableBasedHandler( + SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement, + IdTable idTable, + TempTableDdlTransactionHandling ddlTransactionHandling, + DomainParameterXref domainParameterXref, + BeforeUseAction beforeUseAction, + AfterUseAction afterUseAction, + Function sessionUidAccess, + Supplier exporterSupplier, + HandlerCreationContext creationContext) { + super( sqmDeleteOrUpdateStatement, creationContext ); + this.idTable = idTable; + this.ddlTransactionHandling = ddlTransactionHandling; + this.beforeUseAction = beforeUseAction; + this.afterUseAction = afterUseAction; + + this.domainParameterXref = domainParameterXref; + + this.sessionUidAccess = sessionUidAccess; + this.exporterSupplier = exporterSupplier; + } + + public IdTable getIdTable() { + return idTable; + } + + public DomainParameterXref getDomainParameterXref() { + return domainParameterXref; + } + + public BeforeUseAction getBeforeUseAction() { + return beforeUseAction; + } + + public AfterUseAction getAfterUseAction() { + return afterUseAction; + } + + public Function getSessionUidAccess() { + return sessionUidAccess; + } + + public Supplier getExporterSupplier() { + return exporterSupplier; + } + + @Override + public int execute(ExecutionContext executionContext) { + + // In general: + // 1) prepare for use - this is completely a subclass hook + // 2) perform execution + // 3) release after use - again, completely a subclass hook + + beforeExecution( executionContext ); + + try { + return performExecution( executionContext ); + } + finally { + afterExecution( executionContext ); + } + } + + /** + * Allow subclasses a chance to perform any preliminary work they need + * to perform prior to execution + */ + protected void beforeExecution(ExecutionContext executionContext) { + } + + /** + * Allow subclasses a chance to perform any clean-up work they need + * to perform after execution + */ + protected void afterExecution(ExecutionContext executionContext) { + } + + protected int performExecution(ExecutionContext executionContext) { + performBeforeUseActions( executionContext ); + + try { + // 1) save the matching ids into the id table + final int affectedRowCount = saveMatchingIdsIntoIdTable( executionContext ); + + // 2) perform the actual individual update or deletes, using + // inclusion in the id-table as restriction + performMutations( executionContext ); + + return affectedRowCount; + } + finally { + performAfterUseActions( executionContext ); + } + } + + private void performBeforeUseActions(ExecutionContext executionContext) { + if ( getBeforeUseAction() == BeforeUseAction.CREATE ) { + IdTableHelper.createIdTable( idTable, getExporterSupplier().get(), ddlTransactionHandling, executionContext.getSession() ); + } + } + + private void performAfterUseActions(ExecutionContext executionContext) { + if ( getAfterUseAction() == AfterUseAction.CLEAN ) { + IdTableHelper.cleanIdTableRows( + idTable, + getExporterSupplier().get(), + sessionUidAccess, + executionContext.getSession() + ); + } + else if ( getAfterUseAction() == AfterUseAction.DROP ) { + IdTableHelper.dropIdTable( + idTable, + getExporterSupplier().get(), + ddlTransactionHandling, + executionContext.getSession() + ); + } + } + + protected int saveMatchingIdsIntoIdTable(ExecutionContext executionContext) { + final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); + + final SqmQuerySpec sqmIdSelect = SqmIdSelectGenerator.generateSqmEntityIdSelect( + getSqmDeleteOrUpdateStatement(), + executionContext, + factory + ); + + if ( getIdTable().getSessionUidColumn() != null ) { + //noinspection unchecked + sqmIdSelect.getSelectClause().add( + new SqmSelection( + new SqmLiteral( + executionContext.getSession().getSessionIdentifier().toString(), + UUIDCharType.INSTANCE, + executionContext.getSession().getFactory().getNodeBuilder() + ), + null, + executionContext.getSession().getFactory().getNodeBuilder() + ) + ); + } + + final SqmTranslatorFactory sqmTranslatorFactory = factory.getQueryEngine().getSqmTranslatorFactory(); + final SqmSelectTranslator sqmTranslator = sqmTranslatorFactory.createSelectTranslator( + QueryOptions.NONE, + domainParameterXref, + executionContext.getQueryParameterBindings(), + executionContext.getSession().getLoadQueryInfluencers(), + factory + ); + + final SqmQuerySpecTranslation sqmIdSelectTranslation = sqmTranslator.translate( sqmIdSelect ); + + final InsertSelectStatement insertSelectStatement = new InsertSelectStatement(); + + final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory ); + + insertSelectStatement.setTargetTable( idTableReference ); + insertSelectStatement.setSourceSelectStatement( sqmIdSelectTranslation.getSqlAst() ); + + + final JdbcServices jdbcServices = factory.getJdbcServices(); + final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); + final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); + final SqlAstInsertSelectTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildInsertTranslator( factory ); + final JdbcInsert jdbcInsert = sqlAstTranslator.translate( insertSelectStatement ); + + + final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( + executionContext.getQueryParameterBindings(), + domainParameterXref, + SqmUtil.generateJdbcParamsXref( + domainParameterXref, + sqmIdSelectTranslation::getJdbcParamsBySqmParam + ), + sqmTranslator, + executionContext.getSession() + ); + + return jdbcServices.getJdbcInsertExecutor().execute( + jdbcInsert, + jdbcParameterBindings, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> { + }, + executionContext + ); + } + + + public QuerySpec createIdTableSubQuery(ExecutionContext executionContext) { + final QuerySpec querySpec = new QuerySpec( false ); + + final TableReference idTableReference = new TableReference( + idTable.getTableExpression(), + null, + true, + executionContext.getSession().getFactory() + ); + final TableGroup idTableGroup = new StandardTableGroup( + new NavigablePath( idTableReference.getTableExpression() ), + getEntityDescriptor(), + LockMode.NONE, + idTableReference, + Collections.emptyList(), + null, + executionContext.getSession().getFactory() + ); + + querySpec.getFromClause().addRoot( idTableGroup ); + + applySelections( querySpec, idTableReference, executionContext ); + applyRestrictions( querySpec, idTableReference, executionContext ); + + return querySpec; + } + + private void applySelections( + QuerySpec querySpec, + TableReference tableReference, + ExecutionContext executionContext) { + for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) { + final IdTableColumn idTableColumn = idTable.getIdTableColumns().get( i ); + if ( idTableColumn != idTable.getSessionUidColumn() ) { + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + i+1, + i, + new ColumnReference( + tableReference, + idTableColumn.getColumnName(), + idTableColumn.getJdbcMapping(), + executionContext.getSession().getFactory() + ), + idTableColumn.getJdbcMapping() + ) + ); + } + } + } + + private void applyRestrictions( + QuerySpec querySpec, + TableReference idTableReference, + ExecutionContext executionContext) { + if ( idTable.getSessionUidColumn() != null ) { + querySpec.applyPredicate( + new ComparisonPredicate( + new ColumnReference( + idTableReference, + idTable.getSessionUidColumn().getColumnName(), + idTable.getSessionUidColumn().getJdbcMapping(), + executionContext.getSession().getFactory() + ), + ComparisonOperator.EQUAL, + new QueryLiteral( + sessionUidAccess.apply( executionContext.getSession() ), + UUIDCharType.INSTANCE, + Clause.WHERE + ) + ) + ); + } + } + + protected abstract void performMutations(ExecutionContext executionContext); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AfterUseAction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AfterUseAction.java new file mode 100644 index 0000000000..6492821c57 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/AfterUseAction.java @@ -0,0 +1,18 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +/** + * Actions to perform in regards to an id-table after each use. + * + * @author Steve Ebersole + */ +public enum AfterUseAction { + CLEAN, + DROP, + NONE +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/BeforeUseAction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/BeforeUseAction.java new file mode 100644 index 0000000000..8beb98d4c2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/BeforeUseAction.java @@ -0,0 +1,17 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +/** + * Actions to perform in regards to an id-table prior to each use. + * + * @author Steve Ebersole + */ +public enum BeforeUseAction { + CREATE, + NONE +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTable.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTable.java new file mode 100644 index 0000000000..340a26f324 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTable.java @@ -0,0 +1,83 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import org.hibernate.boot.model.relational.Exportable; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.Queryable; +import org.hibernate.persister.entity.Joinable; + +/** + * @author Steve Ebersole + */ +public class IdTable implements Exportable { + private final EntityMappingType entityDescriptor; + private final String qualifiedTableName; + + private IdTableSessionUidColumn sessionUidColumn; + private final List columns = new ArrayList<>(); + + public IdTable( + EntityMappingType entityDescriptor, + Function idTableNameAdjuster) { + this.entityDescriptor = entityDescriptor; + + this.qualifiedTableName = idTableNameAdjuster.apply( + ( (Joinable) entityDescriptor.getEntityPersister() ).getTableName() + ); + + entityDescriptor.getIdentifierMapping().visitColumns( + (columnExpression, containingTableExpression, jdbcMapping) -> columns.add( + new IdTableColumn( + this, + columnExpression, + jdbcMapping + ) + ) + ); + } + + public EntityMappingType getEntityDescriptor() { + return entityDescriptor; + } + + public String getQualifiedTableName() { + return qualifiedTableName; + } + + public List getIdTableColumns() { + return columns; + } + + public IdTableSessionUidColumn getSessionUidColumn() { + return sessionUidColumn; + } + + public String getTableExpression() { + return qualifiedTableName; + } + + public void addColumn(IdTableColumn column) { + columns.add( column ); + if ( column instanceof IdTableSessionUidColumn ) { + this.sessionUidColumn = (IdTableSessionUidColumn) column; + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Exportable + + @Override + public String getExportIdentifier() { + return getQualifiedTableName(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableColumn.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableColumn.java new file mode 100644 index 0000000000..06ad0d7d20 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableColumn.java @@ -0,0 +1,51 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import org.hibernate.metamodel.mapping.JdbcMapping; + +/** + * A column in a IdTable. As these columns mirror the entity id columns, we know a few things about it inherently, + * such as being non-nullable + * + * @author Steve Ebersole + */ +public class IdTableColumn { + private final IdTable containingTable; + private final String columnName; + private final JdbcMapping jdbcMapping; + + public IdTableColumn( + IdTable containingTable, + String columnName, + JdbcMapping jdbcMapping) { + this.containingTable = containingTable; + this.columnName = columnName; + this.jdbcMapping = jdbcMapping; + } + + public IdTable getContainingTable() { + return containingTable; + } + + public String getColumnName() { + return columnName; + } + + public JdbcMapping getJdbcMapping() { + return jdbcMapping; + } + + public String getDefaultValue() { + return null; + } + + public String getSqlTypeDefinition() { + // todo (6.0) : this really ought to get the type def from the persister + return null; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableExporter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableExporter.java new file mode 100644 index 0000000000..d5c41fe655 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableExporter.java @@ -0,0 +1,25 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.util.function.Function; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; + +/** + * @author Steve Ebersole + */ +public interface IdTableExporter { + String getSqlCreateCommand(IdTable idTable); + + String getSqlDropCommand(IdTable idTable); + + String getSqlTruncateCommand( + IdTable idTable, + Function sessionUidAccess, + SharedSessionContractImplementor session); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableHelper.java new file mode 100644 index 0000000000..cb2d969442 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableHelper.java @@ -0,0 +1,235 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.function.Function; + +import org.hibernate.boot.TempTableDdlTransactionHandling; +import org.hibernate.engine.jdbc.internal.FormatStyle; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.hibernate.engine.jdbc.spi.SqlStatementLogger; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.jdbc.AbstractWork; + +/** + * @author Steve Ebersole + */ +public class IdTableHelper { + private final static CoreMessageLogger log = CoreLogging.messageLogger( IdTableHelper.class ); + + public static final String SESSION_ID_COLUMN_NAME = "hib_sess_id"; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Creation + + public static void createIdTable( + IdTable idTable, + IdTableExporter exporter, + TempTableDdlTransactionHandling ddlTransactionHandling, + SharedSessionContractImplementor session) { + executeWork( + new IdTableCreationWork( idTable, exporter, session ), + ddlTransactionHandling, + session + ); + } + + private static class IdTableCreationWork extends AbstractWork { + private final IdTable idTable; + private final IdTableExporter exporter; + private final SharedSessionContractImplementor session; + + IdTableCreationWork( + IdTable idTable, + IdTableExporter exporter, + SharedSessionContractImplementor session) { + this.idTable = idTable; + this.exporter = exporter; + this.session = session; + } + + @Override + public void execute(Connection connection) { + final JdbcServices jdbcServices = session.getFactory().getJdbcServices(); + + try { + final String creationCommand = exporter.getSqlCreateCommand( idTable ); + logStatement( creationCommand, jdbcServices ); + + try (Statement statement = connection.createStatement()) { + statement.executeUpdate( creationCommand ); + jdbcServices.getSqlExceptionHelper().handleAndClearWarnings( statement, WARNING_HANDLER ); + } + catch (SQLException e) { + log.debugf( + "unable to create id table [%s]; `%s` failed : %s", + idTable.getQualifiedTableName(), + creationCommand, + e.getMessage() + ); + } + } + catch( Exception e ) { + log.debugf( "Error creating id table(s) : %s", e.getMessage() ); + } + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Drop + + public static void dropIdTable( + IdTable idTable, + IdTableExporter exporter, + TempTableDdlTransactionHandling ddlTransactionHandling, + SharedSessionContractImplementor session) { + executeWork( + new IdTableDropWork( idTable, exporter, session ), + ddlTransactionHandling, + session + ); + } + + private static class IdTableDropWork extends AbstractWork { + private final IdTable idTable; + private final IdTableExporter exporter; + private final SharedSessionContractImplementor session; + + IdTableDropWork( + IdTable idTable, + IdTableExporter exporter, + SharedSessionContractImplementor session) { + this.idTable = idTable; + this.exporter = exporter; + this.session = session; + } + + @Override + public void execute(Connection connection) { + final JdbcServices jdbcServices = session.getFactory().getJdbcServices(); + + try { + final String dropCommand = exporter.getSqlCreateCommand( idTable ); + logStatement( dropCommand, jdbcServices ); + + try (Statement statement = connection.createStatement()) { + statement.executeUpdate( dropCommand ); + jdbcServices.getSqlExceptionHelper().handleAndClearWarnings( statement, WARNING_HANDLER ); + } + catch (SQLException e) { + log.debugf( + "unable to drop id table [%s]; `%s` failed : %s", + idTable.getQualifiedTableName(), + dropCommand, + e.getMessage() + ); + } + } + catch( Exception e ) { + log.debugf( "Error dropping id table(s) : %s", e.getMessage() ); + } + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Clean + + public static void cleanIdTableRows( + IdTable idTable, + IdTableExporter exporter, + Function sessionUidAccess, + SharedSessionContractImplementor session) { + PreparedStatement ps = null; + try { + final String sql = exporter.getSqlTruncateCommand( idTable, sessionUidAccess, session ); + + ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false ); + + if ( idTable.getSessionUidColumn() != null ) { + final String sessionUid = sessionUidAccess.apply( session ); + ps.setString( 1, sessionUid ); + } + + session.getJdbcCoordinator().getResultSetReturn().executeUpdate( ps ); + } + catch( Throwable t ) { + log.unableToCleanupTemporaryIdTable(t); + } + finally { + if ( ps != null ) { + try { + session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( ps ); + } + catch( Throwable ignore ) { + // ignore + } + } + } + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Misc + + private static void executeWork( + AbstractWork work, + TempTableDdlTransactionHandling ddlTransactionHandling, + SharedSessionContractImplementor session) { + if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) { + // simply execute the work using a Connection obtained from JdbcConnectionAccess + // + // NOTE : we do not (potentially) release the Connection here + // via LogicalConnectionImplementor#afterStatement because + // for sure we will be immediately using it again to + // populate the id table and use it... + + try { + work.execute( session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection() ); + } + catch (SQLException e) { + log.error( "Unable to use JDBC Connection to create perform id table management", e ); + } + } + else { + session.getTransactionCoordinator() + .createIsolationDelegate() + .delegateWork( work, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT ); + } + } + + private static SqlExceptionHelper.WarningHandler WARNING_HANDLER = new SqlExceptionHelper.WarningHandlerLoggingSupport() { + public boolean doProcess() { + return log.isDebugEnabled(); + } + + public void prepare(SQLWarning warning) { + log.warningsCreatingTempTable( warning ); + } + + @Override + protected void logWarning(String description, String message) { + log.debug( description ); + log.debug( message ); + } + }; + + + private static void logStatement(String sql, JdbcServices jdbcServices) { + final SqlStatementLogger statementLogger = jdbcServices.getSqlStatementLogger(); + statementLogger.logStatement( sql, FormatStyle.BASIC.getFormatter() ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableSessionUidColumn.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableSessionUidColumn.java new file mode 100644 index 0000000000..21bb9a9c34 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/IdTableSessionUidColumn.java @@ -0,0 +1,22 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import org.hibernate.type.UUIDCharType; + +/** + * @author Steve Ebersole + */ +public class IdTableSessionUidColumn extends IdTableColumn { + public IdTableSessionUidColumn(IdTable containingTable) { + super( + containingTable, + IdTableHelper.SESSION_ID_COLUMN_NAME, + UUIDCharType.INSTANCE + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java index d9b2658086..16aca8a5e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/LocalTemporaryTableStrategy.java @@ -6,7 +6,10 @@ */ package org.hibernate.query.sqm.mutation.internal.idtable; +import java.util.function.Supplier; + import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.mutation.spi.DeleteHandler; import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; @@ -23,6 +26,34 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrategy { public static final String SHORT_NAME = "local_temporary"; + private final IdTable idTable; + private final Supplier idTableExporterAccess; + private final AfterUseAction afterUseAction; + private final TempTableDdlTransactionHandling ddlTransactionHandling; + + public LocalTemporaryTableStrategy( + IdTable idTable, + Supplier idTableExporterAccess, + AfterUseAction afterUseAction, + TempTableDdlTransactionHandling ddlTransactionHandling) { + this.idTable = idTable; + this.idTableExporterAccess = idTableExporterAccess; + this.afterUseAction = afterUseAction; + this.ddlTransactionHandling = ddlTransactionHandling; + } + + public LocalTemporaryTableStrategy( + IdTable idTable, + AfterUseAction afterUseAction, + TempTableDdlTransactionHandling ddlTransactionHandling) { + this( + idTable, + () -> new TempIdTableExporter( true ), + afterUseAction, + ddlTransactionHandling + ); + } + @Override public UpdateHandler buildUpdateHandler( SqmUpdateStatement sqmUpdateStatement, @@ -36,6 +67,30 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg SqmDeleteStatement sqmDeleteStatement, DomainParameterXref domainParameterXref, HandlerCreationContext creationContext) { - throw new NotYetImplementedFor6Exception( getClass() ); + if ( sqmDeleteStatement.getWhereClause() == null + || sqmDeleteStatement.getWhereClause().getPredicate() == null ) { + // optimization - special handler not needing the temp table + return new UnrestrictedTableBasedDeleteHandler( + sqmDeleteStatement, + idTable, + ddlTransactionHandling, + domainParameterXref, + BeforeUseAction.NONE, + afterUseAction, + sessionContractImplementor -> null, + creationContext + ); + } + + return new TableBasedDeleteHandler( + sqmDeleteStatement, + idTable, + idTableExporterAccess, + BeforeUseAction.CREATE, + AfterUseAction.NONE, + ddlTransactionHandling, + domainParameterXref, + creationContext + ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PhysicalIdTableExporter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PhysicalIdTableExporter.java new file mode 100644 index 0000000000..66878c5e21 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/PhysicalIdTableExporter.java @@ -0,0 +1,90 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.util.function.Function; + +import org.hibernate.boot.Metadata; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SharedSessionContractImplementor; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("WeakerAccess") +public class PhysicalIdTableExporter implements IdTableExporter { + + protected String getCreateCommand() { + return "create table"; + } + + protected String getCreateOptions() { + return null; + } + + protected String getDropCommand() { + return "drop table"; + } + + protected String getTruncateIdTableCommand(){ + return "delete from"; + } + + @Override + public String getSqlCreateCommand(IdTable idTable) { + final StringBuilder buffer = new StringBuilder( getCreateCommand() ).append( ' ' ); + buffer.append( idTable.getQualifiedTableName() ); + buffer.append( '(' ); + + boolean firstPass = true; + for ( IdTableColumn column : idTable.getIdTableColumns() ) { + if ( firstPass ) { + firstPass = false; + } + else { + buffer.append( ", " ); + } + + buffer.append( column.getColumnName() ).append( ' ' ); + buffer.append( column.getSqlTypeDefinition() ); + // id values cannot be null + buffer.append( " not null" ); + } + + buffer.append( ") " ); + + final String createOptions = getCreateOptions(); + if ( createOptions != null ) { + buffer.append( createOptions ); + } + + return buffer.toString(); + } + + @Override + public String getSqlDropCommand(IdTable idTable) { + return getDropCommand() + ' ' + idTable.getQualifiedTableName(); + } + + @Override + public String getSqlTruncateCommand( + IdTable idTable, + Function sessionUidAccess, + SharedSessionContractImplementor session) { + if ( idTable.getSessionUidColumn() != null ) { + assert sessionUidAccess != null; + final String uid = sessionUidAccess.apply( session ); + return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName() + + " where " + idTable.getSessionUidColumn().getColumnName() + " = " + uid; + } + else { + return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName(); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java new file mode 100644 index 0000000000..03b933f448 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TableBasedDeleteHandler.java @@ -0,0 +1,169 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.hibernate.boot.TempTableDdlTransactionHandling; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.ColumnConsumer; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.DeleteHandler; +import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; +import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; +import org.hibernate.sql.ast.SqlAstDeleteTranslator; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SqlTuple; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcDelete; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +/** +* @author Steve Ebersole +*/ +public class TableBasedDeleteHandler + extends AbstractTableBasedHandler + implements DeleteHandler { + + public TableBasedDeleteHandler( + SqmDeleteStatement sqmDeleteStatement, + IdTable idTable, + Supplier exporterSupplier, + BeforeUseAction beforeUseAction, + AfterUseAction afterUseAction, + TempTableDdlTransactionHandling transactionality, + DomainParameterXref domainParameterXref, + HandlerCreationContext creationContext) { + super( + sqmDeleteStatement, + idTable, + transactionality, + domainParameterXref, + beforeUseAction, + afterUseAction, + session -> session.getSessionIdentifier().toString(), + exporterSupplier, + creationContext + ); + } + + @Override + public SqmDeleteStatement getSqmDeleteOrUpdateStatement() { + return (SqmDeleteStatement) super.getSqmDeleteOrUpdateStatement(); + } + + @Override + protected void performMutations(ExecutionContext executionContext) { + // create the selection of "matching ids" from the id-table. this is used as the subquery in + // used to restrict the deletions from each table + final QuerySpec idTableSelectSubQuerySpec = createIdTableSubQuery( executionContext ); + + getEntityDescriptor().visitConstraintOrderedTables( + (tableExpression, tableKeyColumnsVisitationSupplier) -> { + deleteFrom( tableExpression, tableKeyColumnsVisitationSupplier, idTableSelectSubQuerySpec, executionContext ); + } + ); + } + + private static class TableKeyExpressionCollector { + private final EntityMappingType entityMappingType; + + public TableKeyExpressionCollector(EntityMappingType entityMappingType) { + this.entityMappingType = entityMappingType; + } + + Expression firstColumnExpression; + List collectedColumnExpressions; + + void apply(ColumnReference columnReference) { + if ( firstColumnExpression == null ) { + firstColumnExpression = columnReference; + } + else if ( collectedColumnExpressions == null ) { + collectedColumnExpressions = new ArrayList<>(); + collectedColumnExpressions.add( firstColumnExpression ); + collectedColumnExpressions.add( columnReference ); + } + else { + collectedColumnExpressions.add( columnReference ); + } + } + + Expression buildKeyExpression() { + if ( collectedColumnExpressions == null ) { + return firstColumnExpression; + } + + return new SqlTuple( collectedColumnExpressions, entityMappingType.getIdentifierMapping() ); + } + } + + private void deleteFrom( + String tableExpression, + Supplier> tableKeyColumnVisitationSupplier, + QuerySpec idTableSelectSubQuery, + ExecutionContext executionContext) { + final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); + + final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( getEntityDescriptor() ); + + tableKeyColumnVisitationSupplier.get().accept( + (columnExpression, containingTableExpression, jdbcMapping) -> { + assert containingTableExpression.equals( tableExpression ); + keyColumnCollector.apply( + new ColumnReference( + (String) null, + columnExpression, + jdbcMapping, + factory + ) + ); + } + ); + + final InSubQueryPredicate predicate = new InSubQueryPredicate( + keyColumnCollector.buildKeyExpression(), + idTableSelectSubQuery, + false + ); + + final DeleteStatement deleteStatement = new DeleteStatement( + new TableReference( tableExpression, null, true, factory ), + predicate + ); + + final JdbcServices jdbcServices = factory.getJdbcServices(); + final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); + + final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); + final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory ); + final JdbcDelete jdbcDelete = sqlAstTranslator.translate( deleteStatement ); + + jdbcServices.getJdbcDeleteExecutor().execute( + jdbcDelete, + JdbcParameterBindings.NO_BINDINGS, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> {}, + executionContext + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TempIdTableExporter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TempIdTableExporter.java new file mode 100644 index 0000000000..1f20c1ca26 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/TempIdTableExporter.java @@ -0,0 +1,95 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.util.function.Function; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("WeakerAccess") +public class TempIdTableExporter implements IdTableExporter { + private final boolean isLocal; + + public TempIdTableExporter() { + this( true ); + } + + public TempIdTableExporter(boolean isLocal) { + this.isLocal = isLocal; + } + + protected String getCreateCommand() { + return "create " + (isLocal ? "local" : "global") + " temporary table"; + } + + protected String getCreateOptions() { + return null; + } + + protected String getDropCommand() { + return "drop table"; + } + + protected String getTruncateIdTableCommand(){ + return "delete from"; + } + + @Override + public String getSqlCreateCommand(IdTable idTable) { + final StringBuilder buffer = new StringBuilder( getCreateCommand() ).append( ' ' ); + buffer.append( idTable.getQualifiedTableName() ); + buffer.append( '(' ); + + boolean firstPass = true; + for ( IdTableColumn column : idTable.getIdTableColumns() ) { + if ( firstPass ) { + firstPass = false; + } + else { + buffer.append( ", " ); + } + + buffer.append( column.getColumnName() ).append( ' ' ); + buffer.append( column.getSqlTypeDefinition() ); + // id values cannot be null + buffer.append( " not null" ); + } + + buffer.append( ") " ); + + final String createOptions = getCreateOptions(); + if ( createOptions != null ) { + buffer.append( createOptions ); + } + + return buffer.toString(); + } + + @Override + public String getSqlDropCommand(IdTable idTable) { + return getDropCommand() + " " + idTable.getQualifiedTableName(); + } + + @Override + public String getSqlTruncateCommand( + IdTable idTable, + Function sessionUidAccess, + SharedSessionContractImplementor session) { + if ( idTable.getSessionUidColumn() != null ) { + assert sessionUidAccess != null; + final String uid = sessionUidAccess.apply( session ); + return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName() + + " where " + idTable.getSessionUidColumn().getColumnName() + " = " + uid; + } + else { + return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName(); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UnrestrictedTableBasedDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UnrestrictedTableBasedDeleteHandler.java new file mode 100644 index 0000000000..6aeb5bb932 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/UnrestrictedTableBasedDeleteHandler.java @@ -0,0 +1,96 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.mutation.internal.idtable; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import org.hibernate.boot.TempTableDdlTransactionHandling; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.mutation.spi.DeleteHandler; +import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext; +import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; +import org.hibernate.sql.ast.SqlAstDeleteTranslator; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; +import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcDelete; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; + +/** + * @author Steve Ebersole + */ +public class UnrestrictedTableBasedDeleteHandler extends TableBasedDeleteHandler implements DeleteHandler { + public UnrestrictedTableBasedDeleteHandler( + SqmDeleteStatement sqmDeleteStatement, + IdTable idTable, + TempTableDdlTransactionHandling ddlTransactionHandling, + DomainParameterXref domainParameterXref, + BeforeUseAction beforeUseAction, + AfterUseAction afterUseAction, + Function sessionUidAccess, + HandlerCreationContext creationContext) { + super( + sqmDeleteStatement, + idTable, + () -> null, + beforeUseAction, + afterUseAction, + ddlTransactionHandling, + domainParameterXref, + creationContext + ); + } + + @Override + public int execute(ExecutionContext executionContext) { + final AtomicInteger rows = new AtomicInteger(); + + getEntityDescriptor().visitConstraintOrderedTables( + (tableExpression, tableKeyColumnsVisitationSupplier) -> { + rows.set( deleteFrom( tableExpression, executionContext ) ); + } + ); + + return rows.get(); + } + + private int deleteFrom( + String tableExpression, + ExecutionContext executionContext) { + final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); + + final DeleteStatement deleteStatement = new DeleteStatement( + new TableReference( tableExpression, null, true, factory ), + null + ); + + final JdbcServices jdbcServices = factory.getJdbcServices(); + final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); + + final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); + final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory ); + final JdbcDelete jdbcDelete = sqlAstTranslator.translate( deleteStatement ); + + return jdbcServices.getJdbcDeleteExecutor().execute( + jdbcDelete, + JdbcParameterBindings.NO_BINDINGS, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ), + (integer, preparedStatement) -> {}, + executionContext + ); + + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/package-info.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/package-info.java new file mode 100644 index 0000000000..0ab8676ff5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/package-info.java @@ -0,0 +1,21 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ + +/** + * Support for multi-table SQM mutation (insert, update, delete) operations using + * a table to temporarily hold the matching ids. There are 3 forms: + * + * * {@link org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy} uses + * local temp tables as defined by the SQL spec + * * {@link org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy} uses + * global temp tables as defined by the SQL spec + * * {@link org.hibernate.query.sqm.mutation.internal.idtable.PersistentTableStrategy} uses normal table + * managed by Hibernate. + * + * @asciidoc + */ +package org.hibernate.query.sqm.mutation.internal.idtable; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/AbstractMutationHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/AbstractMutationHandler.java index f02ed5af4f..1035905fce 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/AbstractMutationHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/spi/AbstractMutationHandler.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.mutation.spi; +import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; @@ -27,7 +28,7 @@ public abstract class AbstractMutationHandler implements Handler { return sqmDeleteOrUpdateStatement; } - public EntityPersister getEntityDescriptor() { + public EntityMappingType getEntityDescriptor() { final String entityName = sqmDeleteOrUpdateStatement.getTarget() .getReferencedPathSource() .getHibernateEntityName(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslation.java similarity index 89% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteInterpretation.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslation.java index d88fa65213..15e2f79c39 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslation.java @@ -16,11 +16,11 @@ import org.hibernate.sql.exec.spi.JdbcParameter; /** * @author Steve Ebersole */ -public class SimpleSqmDeleteInterpretation implements SqmInterpretation { +public class SimpleSqmDeleteTranslation implements SqmTranslation { private final DeleteStatement sqlAst; private final Map> jdbcParamMap; - public SimpleSqmDeleteInterpretation( + public SimpleSqmDeleteTranslation( DeleteStatement sqlAst, Map> jdbcParamMap) { this.sqlAst = sqlAst; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslator.java similarity index 70% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteToSqlAstConverter.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslator.java index 303093f168..1de20ce96a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SimpleSqmDeleteTranslator.java @@ -12,6 +12,6 @@ import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; /** * @author Steve Ebersole */ -public interface SimpleSqmDeleteToSqlAstConverter extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { - SimpleSqmDeleteInterpretation interpret(SqmDeleteStatement statement); +public interface SimpleSqmDeleteTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { + SimpleSqmDeleteTranslation translate(SqmDeleteStatement statement); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslation.java new file mode 100644 index 0000000000..5985cf36b8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslation.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import java.util.List; +import java.util.Map; + +import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; +import org.hibernate.sql.exec.spi.JdbcParameter; + +/** + * @author Steve Ebersole + */ +public class SqmInsertSelectTranslation { + private final InsertSelectStatement sqlAst; + private final Map> jdbcParamMap; + + public SqmInsertSelectTranslation( + InsertSelectStatement sqlAst, + Map> jdbcParamMap) { + this.sqlAst = sqlAst; + this.jdbcParamMap = jdbcParamMap; + } + + public InsertSelectStatement getSqlAst() { + return sqlAst; + } + + public Map> getJdbcParamMap() { + return jdbcParamMap; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java new file mode 100644 index 0000000000..40ae0324e6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInsertSelectTranslator.java @@ -0,0 +1,17 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess; +import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; + +/** + * @author Steve Ebersole + */ +public interface SqmInsertSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { + SqmInsertSelectTranslation translate(SqmInsertSelectStatement statement); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmQuerySpecTranslation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmQuerySpecTranslation.java new file mode 100644 index 0000000000..55f180386a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmQuerySpecTranslation.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import java.util.List; +import java.util.Map; + +import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.exec.spi.JdbcParameter; + +/** + * @author Steve Ebersole + */ +public class SqmQuerySpecTranslation { + private final QuerySpec sqlAst; + private final Map> jdbcParamsBySqmParam; + + public SqmQuerySpecTranslation( + QuerySpec sqlAst, + Map> jdbcParamsBySqmParam) { + this.sqlAst = sqlAst; + this.jdbcParamsBySqmParam = jdbcParamsBySqmParam; + } + + public QuerySpec getSqlAst() { + return sqlAst; + } + + public Map> getJdbcParamsBySqmParam() { + return jdbcParamsBySqmParam; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslation.java similarity index 78% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectInterpretation.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslation.java index a5ca1fc346..7013ca5c4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslation.java @@ -9,7 +9,7 @@ package org.hibernate.query.sqm.sql; import java.util.List; import java.util.Map; -import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcParameter; @@ -17,15 +17,15 @@ import org.hibernate.sql.exec.spi.JdbcParameter; /** * Details of the result of interpreting an SQM SELECT AST into a SQL SELECT AST * - * @see StandardSqmSelectToSqlAstConverter#interpret(org.hibernate.query.sqm.tree.select.SqmSelectStatement) + * @see StandardSqmSelectTranslator#translate(org.hibernate.query.sqm.tree.select.SqmSelectStatement) * * @author Steve Ebersole */ -public class SqmSelectInterpretation implements SqmInterpretation { +public class SqmSelectTranslation implements SqmTranslation { private final SelectStatement sqlAst; private final Map> jdbcParamsBySqmParam; - public SqmSelectInterpretation( + public SqmSelectTranslation( SelectStatement sqlAst, Map> jdbcParamsBySqmParam) { this.sqlAst = sqlAst; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslator.java similarity index 60% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectToSqlAstConverter.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslator.java index 9fb756dc06..5d82382b3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectTranslator.java @@ -7,11 +7,13 @@ package org.hibernate.query.sqm.sql; import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess; +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; /** * @author Steve Ebersole */ -public interface SqmSelectToSqlAstConverter extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { - SqmSelectInterpretation interpret(SqmSelectStatement statement); +public interface SqmSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { + SqmSelectTranslation translate(SqmSelectStatement sqmStatement); + SqmQuerySpecTranslation translate(SqmQuerySpec sqmQuerySpec); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslation.java similarity index 94% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInterpretation.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslation.java index 3186b909f2..822dc7adaa 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslation.java @@ -18,7 +18,7 @@ import org.hibernate.sql.exec.spi.JdbcParameter; * * @author Steve Ebersole */ -public interface SqmInterpretation { +public interface SqmTranslation { Statement getSqlAst(); Map> getJdbcParamsBySqmParam(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java index 1ecf4a2904..6778746cbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmTranslatorFactory.java @@ -17,14 +17,21 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext; * @author Steve Ebersole */ public interface SqmTranslatorFactory { - SqmSelectToSqlAstConverter createSelectConverter( + SqmSelectTranslator createSelectTranslator( QueryOptions queryOptions, DomainParameterXref domainParameterXref, QueryParameterBindings domainParameterBindings, LoadQueryInfluencers influencers, SqlAstCreationContext creationContext); - SimpleSqmDeleteToSqlAstConverter createSimpleDeleteConverter( + SimpleSqmDeleteTranslator createSimpleDeleteTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings, + LoadQueryInfluencers influencers, + SqlAstCreationContext creationContext); + + SqmInsertSelectTranslator createInsertSelectTranslator( QueryOptions queryOptions, DomainParameterXref domainParameterXref, QueryParameterBindings domainParameterBindings, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteTranslator.java similarity index 84% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteToSqlAstConverter.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteTranslator.java index e39d373b9a..68547b7841 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmDeleteTranslator.java @@ -8,14 +8,15 @@ package org.hibernate.query.sqm.sql.internal; import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.NavigablePath; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; -import org.hibernate.query.sqm.sql.SimpleSqmDeleteInterpretation; -import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter; +import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslation; +import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.sql.ast.Clause; @@ -25,15 +26,22 @@ import org.hibernate.sql.ast.spi.SqlAstTreeHelper; import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.exec.spi.JdbcDelete; /** * @author Steve Ebersole */ -public class StandardSqmDeleteToSqlAstConverter +public class StandardSqmDeleteTranslator extends BaseSqmToSqlAstConverter - implements SimpleSqmDeleteToSqlAstConverter { + implements SimpleSqmDeleteTranslator { - public StandardSqmDeleteToSqlAstConverter( + public static JdbcDelete translate( + SqmDeleteStatement statement, + SessionFactoryImplementor factory) { + return null; + } + + public StandardSqmDeleteTranslator( SqlAstCreationContext creationContext, QueryOptions queryOptions, DomainParameterXref domainParameterXref, @@ -42,9 +50,9 @@ public class StandardSqmDeleteToSqlAstConverter } @Override - public SimpleSqmDeleteInterpretation interpret(SqmDeleteStatement statement) { + public SimpleSqmDeleteTranslation translate(SqmDeleteStatement statement) { final DeleteStatement deleteStatement = visitDeleteStatement( statement ); - return new SimpleSqmDeleteInterpretation( + return new SimpleSqmDeleteTranslation( deleteStatement, getJdbcParamsBySqmParam() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java new file mode 100644 index 0000000000..75b819cad5 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmInsertSelectTranslator.java @@ -0,0 +1,92 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql.internal; + +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; +import org.hibernate.query.sqm.sql.SqmInsertSelectTranslation; +import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator; +import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; + +/** + * @author Steve Ebersole + */ +public class StandardSqmInsertSelectTranslator + extends BaseSqmToSqlAstConverter + implements SqmInsertSelectTranslator { + public StandardSqmInsertSelectTranslator( + SqlAstCreationContext creationContext, + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings) { + super( creationContext, queryOptions, domainParameterXref, domainParameterBindings ); + } + + @Override + public SqmInsertSelectTranslation translate(SqmInsertSelectStatement sqmStatement) { + return new SqmInsertSelectTranslation( visitInsertSelectStatement( sqmStatement ), getJdbcParamsBySqmParam() ); + } + + @Override + public InsertSelectStatement visitInsertSelectStatement(SqmInsertSelectStatement sqmStatement) { + final InsertSelectStatement insertSelectStatement = new InsertSelectStatement(); + + final String entityName = sqmStatement.getTarget().getEntityName(); + final EntityPersister entityDescriptor = getCreationContext().getDomainModel().getEntityDescriptor( entityName ); + assert entityDescriptor != null; + + getProcessingStateStack().push( + new SqlAstProcessingStateImpl( + null, + this, + getCurrentClauseStack()::getCurrent + ) + ); + + try { + final NavigablePath rootPath = new NavigablePath( entityName ); + final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( + rootPath, + null, + JoinType.LEFT, + LockMode.WRITE, + stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ), + getSqlExpressionResolver(), + () -> predicate -> additionalRestrictions = predicate, + getCreationContext() + ); + + if ( ! rootTableGroup.getTableReferenceJoins().isEmpty() + || rootTableGroup.getTableGroupJoins().isEmpty() ) { + throw new HibernateException( "Not expecting multiple table references for an SQM INSERT-SELECT" ); + } + + getFromClauseIndex().registerTableGroup( rootPath, rootTableGroup ); + + insertSelectStatement.setTargetTable( rootTableGroup.getPrimaryTableReference() ); + + insertSelectStatement.setSourceSelectStatement( + visitQuerySpec( sqmStatement.getSelectQuerySpec() ) + ); + + return insertSelectStatement; + } + finally { + getProcessingStateStack().pop(); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java similarity index 95% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java index ed8ef17d95..f7dff2ab7f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java @@ -30,13 +30,15 @@ import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter; -import org.hibernate.query.sqm.sql.SqmSelectInterpretation; -import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation; +import org.hibernate.query.sqm.sql.SqmSelectTranslation; +import org.hibernate.query.sqm.sql.SqmSelectTranslator; import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationTarget; +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.sql.ast.JoinType; @@ -67,9 +69,9 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; * @author John O'Hara */ @SuppressWarnings("unchecked") -public class StandardSqmSelectToSqlAstConverter +public class StandardSqmSelectTranslator extends BaseSqmToSqlAstConverter - implements DomainResultCreationState, SqmSelectToSqlAstConverter { + implements DomainResultCreationState, SqmSelectTranslator { private final LoadQueryInfluencers fetchInfluencers; private final CircularFetchDetector circularFetchDetector = new CircularFetchDetector(); @@ -78,7 +80,7 @@ public class StandardSqmSelectToSqlAstConverter private GraphImplementor currentJpaGraphNode; - public StandardSqmSelectToSqlAstConverter( + public StandardSqmSelectTranslator( QueryOptions queryOptions, DomainParameterXref domainParameterXref, QueryParameterBindings domainParameterBindings, @@ -95,9 +97,17 @@ public class StandardSqmSelectToSqlAstConverter } @Override - public SqmSelectInterpretation interpret(SqmSelectStatement statement) { - return new SqmSelectInterpretation( - visitSelectStatement( statement ), + public SqmSelectTranslation translate(SqmSelectStatement sqmStatement) { + return new SqmSelectTranslation( + visitSelectStatement( sqmStatement ), + getJdbcParamsBySqmParam() + ); + } + + @Override + public SqmQuerySpecTranslation translate(SqmQuerySpec querySpec) { + return new SqmQuerySpecTranslation( + visitQuerySpec( querySpec ), getJdbcParamsBySqmParam() ); } @@ -304,7 +314,7 @@ public class StandardSqmSelectToSqlAstConverter joined, lockMode, alias, - StandardSqmSelectToSqlAstConverter.this + StandardSqmSelectTranslator.this ); } catch (RuntimeException e) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPolymorphicRootDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPolymorphicRootDescriptor.java index 45900dd7ba..645958cd51 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPolymorphicRootDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPolymorphicRootDescriptor.java @@ -390,6 +390,11 @@ public class SqmPolymorphicRootDescriptor implements EntityDomainType { throw new UnsupportedOperationException( ); } + @Override + public SqmPathSource getIdentifierDescriptor() { + return null; + } + @Override public SingularPersistentAttribute getId(Class type) { throw new UnsupportedOperationException( ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstDeleteTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstDeleteTranslator.java index 70e405d36c..3af28a1a8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstDeleteTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstDeleteTranslator.java @@ -8,6 +8,7 @@ package org.hibernate.sql.ast; import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter; import org.hibernate.sql.ast.spi.SqlSelectAstWalker; +import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.exec.spi.JdbcDelete; @@ -16,4 +17,6 @@ import org.hibernate.sql.exec.spi.JdbcDelete; */ public interface SqlAstDeleteTranslator extends SqlSelectAstWalker, SqlAstToJdbcOperationConverter { JdbcDelete translate(DeleteStatement sqlAst); + + JdbcDelete translate(CteStatement cteStatement); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstInsertSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstInsertSelectTranslator.java new file mode 100644 index 0000000000..bbde3184ea --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstInsertSelectTranslator.java @@ -0,0 +1,18 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast; + +import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter; +import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; +import org.hibernate.sql.exec.spi.JdbcInsert; + +/** + * @author Steve Ebersole + */ +public interface SqlAstInsertSelectTranslator extends SqlAstToJdbcOperationConverter { + JdbcInsert translate(InsertSelectStatement sqlAst); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstSelectTranslator.java index cda872f423..8a968e4252 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstSelectTranslator.java @@ -20,10 +20,10 @@ public interface SqlAstSelectTranslator extends SqlSelectAstWalker, SqlAstToJdbc /** * Translate the SelectStatement into the executable JdbcSelect */ - JdbcSelect interpret(SelectStatement selectStatement); + JdbcSelect translate(SelectStatement selectStatement); /** * Translate the QuerySpec into the executable JdbcSelect */ - JdbcSelect interpret(QuerySpec querySpec); + JdbcSelect translate(QuerySpec querySpec); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java index c77089d9cf..524b09a3d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslatorFactory.java @@ -9,11 +9,25 @@ package org.hibernate.sql.ast; import org.hibernate.engine.spi.SessionFactoryImplementor; /** + * Factory for obtaining single-use SQL AST translators + * * @author Steve Ebersole */ public interface SqlAstTranslatorFactory { - SqlAstSelectTranslator buildSelectConverter(SessionFactoryImplementor sessionFactory); - SqlAstDeleteTranslator buildDeleteConverter(SessionFactoryImplementor sessionFactory); + /** + * Builds a single-use select translator + */ + SqlAstSelectTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory); - // todo (6.0) : update, delete, etc + /** + * Builds a single-use delete translator + */ + SqlAstDeleteTranslator buildDeleteTranslator(SessionFactoryImplementor sessionFactory); + + /** + * Builds a single-use delete translator + */ + SqlAstInsertSelectTranslator buildInsertTranslator(SessionFactoryImplementor sessionFactory); + + // todo (6.0) : update, insert, etc } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstToJdbcOperationConverter.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstToJdbcOperationConverter.java index 4e6996e3aa..25261ba29a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstToJdbcOperationConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstToJdbcOperationConverter.java @@ -17,11 +17,11 @@ import org.hibernate.sql.ast.tree.update.Assignment; /** * @author Steve Ebersole */ -public class AbstractSqlAstToJdbcOperationConverter +public abstract class AbstractSqlAstToJdbcOperationConverter extends AbstractSqlAstWalker implements SqlAstToJdbcOperationConverter { - private final Set affectedTableExpressions = new HashSet<>(); + private final Set affectedTableNames = new HashSet<>(); protected AbstractSqlAstToJdbcOperationConverter(SessionFactoryImplementor sessionFactory) { super( sessionFactory ); @@ -33,8 +33,8 @@ public class AbstractSqlAstToJdbcOperationConverter } @Override - public Set getAffectedTableExpressions() { - return affectedTableExpressions; + public Set getAffectedTableNames() { + return affectedTableNames; } @Override @@ -48,6 +48,6 @@ public class AbstractSqlAstToJdbcOperationConverter } protected void registerAffectedTable(String tableExpression) { - affectedTableExpressions.add( tableExpression ); + affectedTableNames.add( tableExpression ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstToJdbcOperationConverter.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstToJdbcOperationConverter.java index 6a02c9ef6d..1fcf269796 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstToJdbcOperationConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstToJdbcOperationConverter.java @@ -8,6 +8,7 @@ package org.hibernate.sql.ast.spi; import java.util.Set; +import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators; @@ -18,5 +19,7 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators; * @author Steve Ebersole */ public interface SqlAstToJdbcOperationConverter extends SqlAstWalker, SqlTypeDescriptorIndicators { - Set getAffectedTableExpressions(); + Set getAffectedTableNames(); + + JdbcOperation translate(CteStatement cteStatement); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstDeleteTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstDeleteTranslator.java index 2f5fcf03f0..ca37aaf9c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstDeleteTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstDeleteTranslator.java @@ -6,12 +6,13 @@ */ package org.hibernate.sql.ast.spi; -import java.util.Collections; import java.util.List; import java.util.Set; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.sql.ast.tree.cte.CteColumn; import org.hibernate.sql.ast.SqlAstDeleteTranslator; +import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.delete.DeleteStatement; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.exec.spi.JdbcDelete; @@ -20,9 +21,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinder; /** * @author Steve Ebersole */ -public class StandardSqlAstDeleteTranslator - extends AbstractSqlAstToJdbcOperationConverter - implements SqlAstDeleteTranslator { +public class StandardSqlAstDeleteTranslator extends AbstractSqlAstToJdbcOperationConverter implements SqlAstDeleteTranslator { public StandardSqlAstDeleteTranslator(SessionFactoryImplementor sessionFactory) { super( sessionFactory ); } @@ -50,7 +49,7 @@ public class StandardSqlAstDeleteTranslator @Override public Set getAffectedTableNames() { - return getAffectedTableExpressions(); + return getAffectedTableNames(); } }; } @@ -60,4 +59,48 @@ public class StandardSqlAstDeleteTranslator // generally we do not want to render the qualifier appendSql( columnReference.getColumnExpression() ); } + + @Override + public JdbcDelete translate(CteStatement sqlAst) { + assert sqlAst.getCteConsumer() instanceof DeleteStatement; + + appendSql( "with " ); + appendSql( sqlAst.getCteLabel() ); + + appendSql( " (" ); + + String separator = ""; + + for ( int i = 0; i < sqlAst.getCteTable().getCteColumns().size(); i++ ) { + final CteColumn cteColumn = sqlAst.getCteTable().getCteColumns().get( i ); + appendSql( separator ); + appendSql( cteColumn.getColumnExpression() ); + separator = ", "; + } + + appendSql( ") as (" ); + + visitQuerySpec( sqlAst.getCteDefinition() ); + + appendSql( ") " ); + + translate( (DeleteStatement) sqlAst.getCteConsumer() ); + + return new JdbcDelete() { + @Override + public String getSql() { + return StandardSqlAstDeleteTranslator.this.getSql(); + } + + @Override + public List getParameterBinders() { + return StandardSqlAstDeleteTranslator.this.getParameterBinders(); + } + + @Override + public Set getAffectedTableNames() { + return StandardSqlAstDeleteTranslator.this.getAffectedTableNames(); + } + }; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java new file mode 100644 index 0000000000..b35e117549 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstInsertSelectTranslator.java @@ -0,0 +1,64 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.spi; + +import java.util.List; +import java.util.Set; + +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.sql.ast.SqlAstInsertSelectTranslator; +import org.hibernate.sql.ast.tree.cte.CteStatement; +import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; +import org.hibernate.sql.exec.spi.JdbcInsert; +import org.hibernate.sql.exec.spi.JdbcOperation; +import org.hibernate.sql.exec.spi.JdbcParameterBinder; + +/** + * @author Steve Ebersole + */ +public class StandardSqlAstInsertSelectTranslator + extends AbstractSqlAstToJdbcOperationConverter + implements SqlAstInsertSelectTranslator { + public StandardSqlAstInsertSelectTranslator(SessionFactoryImplementor sessionFactory) { + super( sessionFactory ); + } + + @Override + public JdbcInsert translate(InsertSelectStatement sqlAst) { + appendSql( "insert into " ); + appendSql( sqlAst.getTargetTable().getTableExpression() ); + appendSql( " " ); + + // todo (6.0) : for now we do not provide an explicit target columns (VALUES) list - we should... + // it works for our limited usage of insert-select + + visitQuerySpec( sqlAst.getSourceSelectStatement() ); + + return new JdbcInsert() { + @Override + public String getSql() { + return StandardSqlAstInsertSelectTranslator.this.getSql(); + } + + @Override + public List getParameterBinders() { + return StandardSqlAstInsertSelectTranslator.this.getParameterBinders(); + } + + @Override + public Set getAffectedTableNames() { + return StandardSqlAstInsertSelectTranslator.this.getAffectedTableNames(); + } + }; + } + + @Override + public JdbcOperation translate(CteStatement cteStatement) { + throw new NotYetImplementedFor6Exception( getClass() ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java index 7ba8f38162..65f7fc533b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstSelectTranslator.java @@ -8,12 +8,15 @@ package org.hibernate.sql.ast.spi; import java.util.Collections; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.ast.SqlAstSelectTranslator; import org.hibernate.sql.ast.SqlTreePrinter; import org.hibernate.sql.ast.tree.SqlAstTreeLogger; +import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectStatement; +import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.internal.JdbcValuesMappingProducerStandard; @@ -28,13 +31,17 @@ public class StandardSqlAstSelectTranslator extends AbstractSqlAstToJdbcOperationConverter implements SqlAstSelectTranslator { - @SuppressWarnings("WeakerAccess") public StandardSqlAstSelectTranslator(SessionFactoryImplementor sessionFactory) { super( sessionFactory ); } @Override - public JdbcSelect interpret(QuerySpec querySpec) { + public JdbcSelect translate(CteStatement cteStatement) { + throw new NotYetImplementedFor6Exception( getClass() ); + } + + @Override + public JdbcSelect translate(QuerySpec querySpec) { visitQuerySpec( querySpec ); return new JdbcSelect( @@ -44,12 +51,12 @@ public class StandardSqlAstSelectTranslator querySpec.getSelectClause().getSqlSelections(), Collections.emptyList() ), - getAffectedTableExpressions() + getAffectedTableNames() ); } @Override - public JdbcSelect interpret(SelectStatement sqlAstSelect) { + public JdbcSelect translate(SelectStatement sqlAstSelect) { if ( SqlAstTreeLogger.DEBUG_ENABLED ) { SqlTreePrinter.print( sqlAstSelect ); } @@ -63,7 +70,7 @@ public class StandardSqlAstSelectTranslator sqlAstSelect.getQuerySpec().getSelectClause().getSqlSelections(), sqlAstSelect.getDomainResultDescriptors() ), - getAffectedTableExpressions() + getAffectedTableNames() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java index f2dbec6af2..8e19b9c8bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/StandardSqlAstTranslatorFactory.java @@ -6,8 +6,10 @@ */ package org.hibernate.sql.ast.spi; +import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.ast.SqlAstDeleteTranslator; +import org.hibernate.sql.ast.SqlAstInsertSelectTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.SqlAstSelectTranslator; @@ -16,12 +18,17 @@ import org.hibernate.sql.ast.SqlAstSelectTranslator; */ public class StandardSqlAstTranslatorFactory implements SqlAstTranslatorFactory { @Override - public SqlAstSelectTranslator buildSelectConverter(SessionFactoryImplementor sessionFactory) { + public SqlAstSelectTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory) { return new StandardSqlAstSelectTranslator( sessionFactory); } @Override - public SqlAstDeleteTranslator buildDeleteConverter(SessionFactoryImplementor sessionFactory) { + public SqlAstDeleteTranslator buildDeleteTranslator(SessionFactoryImplementor sessionFactory) { return new StandardSqlAstDeleteTranslator( sessionFactory ); } + + @Override + public SqlAstInsertSelectTranslator buildInsertTranslator(SessionFactoryImplementor sessionFactory) { + return new StandardSqlAstInsertSelectTranslator( sessionFactory ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java new file mode 100644 index 0000000000..7c0a5407fb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteColumn.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.cte; + +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.sql.ast.tree.cte.CteTable; + +/** + * @author Steve Ebersole + */ +public class CteColumn { + private final CteTable cteTable; + private final String columnExpression; + private final JdbcMapping jdbcMapping; + + public CteColumn(CteTable cteTable, String columnExpression, JdbcMapping jdbcMapping) { + this.cteTable = cteTable; + this.columnExpression = columnExpression; + this.jdbcMapping = jdbcMapping; + } + + public CteTable getCteTable() { + return cteTable; + } + + public String getColumnExpression() { + return columnExpression; + } + + public JdbcMapping getJdbcMapping() { + return jdbcMapping; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java new file mode 100644 index 0000000000..80991c7845 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteStatement.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.cte; + +import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.ast.tree.select.QuerySpec; + +/** + * A statement using a CTE + * + * @author Steve Ebersole + */ +public class CteStatement implements Statement { + private final String cteLabel; + private final CteTable cteTable; + private final QuerySpec cteDefinition; + private final Statement cteConsumer; + + public CteStatement(QuerySpec cteDefinition, String cteLabel, CteTable cteTable, Statement cteConsumer) { + this.cteDefinition = cteDefinition; + this.cteLabel = cteLabel; + this.cteTable = cteTable; + this.cteConsumer = cteConsumer; + } + + public String getCteLabel() { + return cteLabel; + } + + public CteTable getCteTable() { + return cteTable; + } + + public QuerySpec getCteDefinition() { + return cteDefinition; + } + + public Statement getCteConsumer() { + return cteConsumer; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java new file mode 100644 index 0000000000..1d5bb77c0f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTable.java @@ -0,0 +1,193 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.cte; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.hibernate.LockMode; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy; +import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.from.StandardTableGroup; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.exec.internal.JdbcParameterImpl; +import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcParameter; +import org.hibernate.sql.exec.spi.JdbcParameterBinding; +import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import org.hibernate.sql.results.internal.SqlSelectionImpl; + +/** + * Describes the CTE and exposes ways to consume it + * + * @author Steve Ebersole + */ +public class CteTable { + private final EntityMappingType entityDescriptor; + private final SessionFactoryImplementor sessionFactory; + + private final List cteColumns; + + public CteTable(EntityMappingType entityDescriptor, BootstrapContext bootstrapContext) { + this.entityDescriptor = entityDescriptor; + this.sessionFactory = entityDescriptor.getEntityPersister().getFactory(); + + final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount( bootstrapContext.getTypeConfiguration() ); + cteColumns = new ArrayList<>( numberOfColumns ); + entityDescriptor.getIdentifierMapping().visitColumns( + (columnExpression, containingTableExpression, jdbcMapping) -> cteColumns.add( + new CteColumn( + this, + "cte_" + columnExpression, + jdbcMapping + ) + ) + ); + } + + public String getTableExpression() { + return CteBasedMutationStrategy.TABLE_NAME; + } + + public List getCteColumns() { + return cteColumns; + } + + public QuerySpec createCteDefinition( + List matchingIds, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext) { + final QuerySpec querySpec = new QuerySpec( false ); + + final TableReference tableValueConstructorReference = createCteDefinitionTableValueCtor( + matchingIds, + jdbcParameterBindings, + executionContext + ); + + final StandardTableGroup tableValueCtorGroup = new StandardTableGroup( + new NavigablePath( "cte" ), + null, + LockMode.NONE, + tableValueConstructorReference, + Collections.emptyList(), + null, + sessionFactory + ); + + querySpec.getFromClause().addRoot( tableValueCtorGroup ); + + applySelections( querySpec, tableValueConstructorReference ); + + return querySpec; + } + + private TableReference createCteDefinitionTableValueCtor( + List matchingIds, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext) { + // use `DerivedTable` as the TableValueConstructor + // so its `#expression` would be something like `values ( (a1, b1), (a2, b2), ... )` + + final int numberOfColumns = getCteColumns().size(); + + final StringBuilder tableValueCtorExpressionBuffer = new StringBuilder( "values(" ); + String rowSeparator = ""; + int idProcessedCount = 0; + for ( Object matchingId : matchingIds ) { + tableValueCtorExpressionBuffer.append( rowSeparator ); + + tableValueCtorExpressionBuffer.append( '(' ); + StringHelper.repeat( "?", numberOfColumns, ",", tableValueCtorExpressionBuffer ); + tableValueCtorExpressionBuffer.append( ')' ); + + final int currentIdPosition = idProcessedCount; + + entityDescriptor.getIdentifierMapping().visitJdbcValues( + matchingId, + Clause.IRRELEVANT, + (value, type) -> { + final JdbcParameter jdbcParameter = new JdbcParameterImpl( type ); + JdbcParameterBinding jdbcParameterBinding = new JdbcParameterBinding() { + @Override + public JdbcMapping getBindType() { + return type; + } + + @Override + public Object getBindValue() { + return value; + } + }; + jdbcParameterBindings.addBinding( + jdbcParameter, + jdbcParameterBinding + ); + }, + executionContext.getSession() + ); + + rowSeparator = ", "; + idProcessedCount++; + } + + tableValueCtorExpressionBuffer.append( ')' ); + + return new TableReference( + tableValueCtorExpressionBuffer.toString(), + CteBasedMutationStrategy.TABLE_NAME, + false, + sessionFactory + ); + } + + public QuerySpec createCteSubQuery(ExecutionContext executionContext) { + final QuerySpec querySpec = new QuerySpec( false ); + + final TableReference cteTableReference = new TableReference( + getTableExpression(), + null, + false, + sessionFactory + ); + + final CteTableGroup cteTableGroup = new CteTableGroup( cteTableReference ); + querySpec.getFromClause().addRoot( cteTableGroup ); + + applySelections( querySpec, cteTableReference ); + + return querySpec; + } + + private void applySelections(QuerySpec querySpec, TableReference tableReference) { + for ( int i = 0; i < cteColumns.size(); i++ ) { + final CteColumn cteColumn = cteColumns.get( i ); + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + i + 1, + i, + new ColumnReference( + tableReference, + cteColumn.getColumnExpression(), + cteColumn.getJdbcMapping(), + sessionFactory + ), + cteColumn.getJdbcMapping() + ) + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java new file mode 100644 index 0000000000..45cdd8c1b4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java @@ -0,0 +1,113 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.cte; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.hibernate.LockMode; +import org.hibernate.metamodel.mapping.ModelPartContainer; +import org.hibernate.query.NavigablePath; +import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy; +import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.from.TableReferenceJoin; + +/** + * Wraps a {@link TableReference} representing the CTE and adapts it to + * {@link org.hibernate.sql.ast.tree.from.TableGroup} for use in SQL AST + * + * @author Steve Ebersole + */ +public class CteTableGroup implements TableGroup { + private final NavigablePath navigablePath; + private final TableReference cteTableReference; + + public CteTableGroup(TableReference cteTableReference) { + this.navigablePath = new NavigablePath( CteBasedMutationStrategy.SHORT_NAME ); + this.cteTableReference = cteTableReference; + } + + @Override + public NavigablePath getNavigablePath() { + return navigablePath; + } + + @Override + public LockMode getLockMode() { + return LockMode.NONE; + } + + @Override + public ModelPartContainer getModelPart() { + return null; + } + + @Override + public Set getTableGroupJoins() { + return Collections.emptySet(); + } + + @Override + public TableReference resolveTableReference(String tableExpression) { + return cteTableReference; + } + + @Override + public TableReference resolveTableReference( + String tableExpression, + Supplier creator) { + return cteTableReference; + } + + @Override + public void setTableGroupJoins(Set joins) { + } + + @Override + public void visitTableGroupJoins(Consumer consumer) { + } + + @Override + public String getGroupAlias() { + return null; + } + + @Override + public boolean hasTableGroupJoins() { + return false; + } + + @Override + public boolean isInnerJoinPossible() { + return false; + } + + @Override + public void addTableGroupJoin(TableGroupJoin join) { + throw new UnsupportedOperationException( ); + } + + @Override + public void applyAffectedTableNames(Consumer nameCollector) { + nameCollector.accept( cteTableReference.getTableExpression() ); + } + + @Override + public TableReference getPrimaryTableReference() { + return cteTableReference; + } + + @Override + public List getTableReferenceJoins() { + return Collections.emptyList(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcInsert.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcInsert.java new file mode 100644 index 0000000000..ea01d9081c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcInsert.java @@ -0,0 +1,13 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.exec.spi; + +/** + * @author Steve Ebersole + */ +public interface JdbcInsert extends JdbcMutation { +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SecondaryTableTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SecondaryTableTests.java new file mode 100644 index 0000000000..13607fa39f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/metamodel/mapping/SecondaryTableTests.java @@ -0,0 +1,94 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.metamodel.mapping; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.SecondaryTable; +import javax.persistence.Table; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +/** + * @author Steve Ebersole + */ +@DomainModel( annotatedClasses = SecondaryTableTests.SimpleEntityWithSecondaryTables.class ) +@ServiceRegistry +@SessionFactory +public class SecondaryTableTests { + + @Test + public void simpleTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createQuery( "select e from SimpleEntityWithSecondaryTables e" ).list(); + } + ); + } + + @Entity( name = "SimpleEntityWithSecondaryTables" ) + @Table( name = "simple_w_secondary_tables0" ) + @SecondaryTable( name = "simple_w_secondary_tables1" ) + @SecondaryTable( name = "simple_w_secondary_tables2" ) + public static class SimpleEntityWithSecondaryTables { + private Integer id; + private String name; + private Date dob; + private String data; + + public SimpleEntityWithSecondaryTables() { + } + + public SimpleEntityWithSecondaryTables(Integer id, String name, Date dob, String data) { + this.id = id; + this.name = name; + this.dob = dob; + this.data = data; + } + + @Id + 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; + } + + @Column( table = "simple_w_secondary_tables1" ) + public Date getDob() { + return dob; + } + + public void setDob(Date dob) { + this.dob = dob; + } + + @Column( table = "simple_w_secondary_tables2" ) + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java index 3d0bcbdefb..b89e4dc5cf 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java @@ -16,8 +16,8 @@ import org.hibernate.query.NavigablePath; import org.hibernate.query.hql.spi.HqlQueryImplementor; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sqm.internal.QuerySqmImpl; -import org.hibernate.query.sqm.sql.SqmSelectInterpretation; -import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.SqmSelectTranslation; +import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.StandardSqlAstSelectTranslator; @@ -77,7 +77,7 @@ public class SmokeTests { //noinspection unchecked final SqmSelectStatement sqmStatement = (SqmSelectStatement) hqlQuery.getSqmStatement(); - final StandardSqmSelectToSqlAstConverter sqmConverter = new StandardSqmSelectToSqlAstConverter( + final StandardSqmSelectTranslator sqmConverter = new StandardSqmSelectTranslator( hqlQuery.getQueryOptions(), ( (QuerySqmImpl) hqlQuery ).getDomainParameterXref(), query.getParameterBindings(), @@ -85,7 +85,7 @@ public class SmokeTests { scope.getSessionFactory() ); - final SqmSelectInterpretation sqmInterpretation = sqmConverter.interpret( sqmStatement ); + final SqmSelectTranslation sqmInterpretation = sqmConverter.translate( sqmStatement ); final SelectStatement sqlAst = sqmInterpretation.getSqlAst(); final FromClause fromClause = sqlAst.getQuerySpec().getFromClause(); @@ -112,7 +112,7 @@ public class SmokeTests { assertThat( sqlSelection.getJdbcValueExtractor(), notNullValue() ); final JdbcSelect jdbcSelectOperation = new StandardSqlAstSelectTranslator( session.getSessionFactory() ) - .interpret( sqlAst ); + .translate( sqlAst ); assertThat( jdbcSelectOperation.getSql(), @@ -131,7 +131,7 @@ public class SmokeTests { //noinspection unchecked final SqmSelectStatement sqmStatement = (SqmSelectStatement) hqlQuery.getSqmStatement(); - final StandardSqmSelectToSqlAstConverter sqmConverter = new StandardSqmSelectToSqlAstConverter( + final StandardSqmSelectTranslator sqmConverter = new StandardSqmSelectTranslator( hqlQuery.getQueryOptions(), ( (QuerySqmImpl) hqlQuery ).getDomainParameterXref(), query.getParameterBindings(), @@ -139,7 +139,7 @@ public class SmokeTests { scope.getSessionFactory() ); - final SqmSelectInterpretation sqmInterpretation = sqmConverter.interpret( sqmStatement ); + final SqmSelectTranslation sqmInterpretation = sqmConverter.translate( sqmStatement ); final SelectStatement sqlAst = sqmInterpretation.getSqlAst(); final FromClause fromClause = sqlAst.getQuerySpec().getFromClause(); @@ -214,7 +214,7 @@ public class SmokeTests { assertThat( ( (BasicResultAssembler) resultAssembler ).getValueConverter(), nullValue() ); final JdbcSelect jdbcSelectOperation = new StandardSqlAstSelectTranslator( session.getSessionFactory() ) - .interpret( sqlAst ); + .translate( sqlAst ); assertThat( jdbcSelectOperation.getSql(), diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/HqlDeleteExecutionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/HqlDeleteExecutionTests.java index 4408b31294..a28232d86f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/HqlDeleteExecutionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/exec/HqlDeleteExecutionTests.java @@ -6,20 +6,30 @@ */ package org.hibernate.orm.test.sql.exec; -import org.hibernate.orm.test.metamodel.mapping.PluralAttributeTests; +import java.time.Instant; +import java.util.Date; + +import org.hibernate.orm.test.metamodel.mapping.SecondaryTableTests; import org.hibernate.testing.orm.domain.StandardDomainModel; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.FailureExpected; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + /** * @author Steve Ebersole */ @SuppressWarnings("WeakerAccess") -@DomainModel( standardModels = StandardDomainModel.GAMBIT ) +@DomainModel( + standardModels = StandardDomainModel.GAMBIT, + annotatedClasses = SecondaryTableTests.SimpleEntityWithSecondaryTables.class +) @ServiceRegistry @SessionFactory( exportSchema = true ) public class HqlDeleteExecutionTests { @@ -38,4 +48,66 @@ public class HqlDeleteExecutionTests { .executeUpdate() ); } + + @Test + public void testSimpleMultiTableDelete(SessionFactoryScope scope) { + scope.inTransaction( + session -> session.createQuery( "delete SimpleEntityWithSecondaryTables" ) + .executeUpdate() + ); + } + + @Test + public void testSimpleMultiTableRestrictedDelete(SessionFactoryScope scope) { + scope.inTransaction( + session -> session.createQuery( "delete SimpleEntityWithSecondaryTables where data = :filter" ) + .setParameter( "filter", "abc" ) + .executeUpdate() + ); + } + + @Test + @FailureExpected( reason = "Saving of entities with secondary tables is broken atm" ) + public void testSimpleMultiTableRestrictedDeleteResults(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.save( + new SecondaryTableTests.SimpleEntityWithSecondaryTables( + 1, + "first", + Date.from( Instant.now() ), + "1 - cfdjdjvokfobkofbvovoijjbvoijofjdbiof" + ) + ); + session.save( + new SecondaryTableTests.SimpleEntityWithSecondaryTables( + 2, + "second", + Date.from( Instant.now() ), + "2 - s3o2rj9 fcojv9j gj9jfv943jv29j9j4" + ) + ); + session.save( + new SecondaryTableTests.SimpleEntityWithSecondaryTables( + 3, + "third", + Date.from( Instant.now() ), + "abc" + ) + ); + } + ); + scope.inTransaction( + session -> { + final int rows = session.createQuery( "delete SimpleEntityWithSecondaryTables where data = :filter" ) + .setParameter( "filter", "abc" ) + .executeUpdate(); + assertThat( rows, is ( 1 ) ); + } + ); + scope.inTransaction( + session -> session.createQuery( "delete SimpleEntityWithSecondaryTables" ).executeUpdate() + ); + } + }