From 5e0864729b050cbad17d13e96e9750411be3eb6f Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 25 May 2021 15:29:55 +0200 Subject: [PATCH] Update H2 to 1.4.200. Replace LockMode in Fetch and DomainResult with the source alias to resolve lock mode during initializer creation. Introduce notion of aggregate function with an optional filter clause. Implement support for rendering locks into SQL. Move locking tests to orm package --- gradle/libraries.gradle | 2 +- .../main/java/org/hibernate/LockOptions.java | 30 +- .../internal/AbstractBagSemantics.java | 7 - .../internal/AbstractMapSemantics.java | 7 - .../internal/AbstractSetSemantics.java | 5 - .../internal/StandardArraySemantics.java | 7 - .../internal/StandardListSemantics.java | 7 - .../collection/spi/CollectionSemantics.java | 2 - .../dialect/AbstractHANADialect.java | 5 + .../dialect/AbstractTransactSQLDialect.java | 5 + .../dialect/CacheSqlAstTranslator.java | 13 + .../hibernate/dialect/CockroachDialect.java | 155 +++- .../dialect/CockroachSqlAstTranslator.java | 26 + .../org/hibernate/dialect/DB2Dialect.java | 55 +- .../dialect/DB2SqlAstTranslator.java | 21 +- .../org/hibernate/dialect/DB2iDialect.java | 11 +- .../org/hibernate/dialect/DB2zDialect.java | 16 +- .../org/hibernate/dialect/DerbyDialect.java | 15 +- .../dialect/DerbySqlAstTranslator.java | 15 + .../java/org/hibernate/dialect/Dialect.java | 45 ++ .../dialect/FirebirdSqlAstTranslator.java | 7 +- .../java/org/hibernate/dialect/H2Dialect.java | 14 +- .../dialect/HSQLSqlAstTranslator.java | 28 +- .../dialect/IngresSqlAstTranslator.java | 13 + .../dialect/MariaDBSqlAstTranslator.java | 5 + .../org/hibernate/dialect/MySQLDialect.java | 6 + .../dialect/MySQLSqlAstTranslator.java | 5 + .../org/hibernate/dialect/OracleDialect.java | 7 +- .../dialect/OracleSqlAstTranslator.java | 139 +++- .../hibernate/dialect/PostgreSQLDialect.java | 10 + .../dialect/PostgreSQLSqlAstTranslator.java | 21 + .../hibernate/dialect/RDMSOS2200Dialect.java | 2 +- ...r.java => RDMSOS2200SqlAstTranslator.java} | 19 +- .../hibernate/dialect/RowLockStrategy.java | 27 + .../hibernate/dialect/SQLServerDialect.java | 5 + .../dialect/SQLServerSqlAstTranslator.java | 92 +++ .../org/hibernate/dialect/SpannerDialect.java | 12 +- .../dialect/SpannerSqlAstTranslator.java | 14 + .../hibernate/dialect/SybaseASEDialect.java | 11 +- .../dialect/SybaseASESqlAstTranslator.java | 22 + .../dialect/SybaseAnywhereDialect.java | 40 +- .../SybaseAnywhereSqlAstTranslator.java | 22 + .../org/hibernate/dialect/SybaseDialect.java | 2 +- .../dialect/SybaseSqlAstTranslator.java | 17 + .../hibernate/dialect/TeradataDialect.java | 11 +- .../dialect/TeradataSqlAstTranslator.java | 40 + .../hibernate/dialect/TimesTenDialect.java | 49 ++ .../dialect/TimesTenSqlAstTranslator.java | 20 + .../function/CommonFunctionFactory.java | 68 +- .../AbstractSharedSessionContract.java | 3 + .../ast/internal/AbstractNaturalIdLoader.java | 9 +- .../internal/DatabaseSnapshotExecutor.java | 2 - .../ast/internal/LoaderSelectBuilder.java | 4 - .../internal/LoaderSqlAstCreationState.java | 4 +- .../ast/internal/SimpleNaturalIdLoader.java | 3 +- .../SingleIdEntityLoaderStandardImpl.java | 11 +- .../loader/ast/internal/SingleIdLoadPlan.java | 21 +- .../SingleUniqueKeyEntityLoaderStandard.java | 6 +- .../hibernate/loader/ast/spi/Loadable.java | 2 - .../AbstractCompositeIdentifierMapping.java | 3 - .../metamodel/mapping/EntityMappingType.java | 3 - .../AbstractEntityDiscriminatorMapping.java | 3 - .../internal/AnyDiscriminatorPart.java | 2 - .../mapping/internal/AnyKeyPart.java | 2 - .../internal/BasicAttributeMapping.java | 2 - .../BasicEntityIdentifierMappingImpl.java | 2 - .../internal/BasicValuedCollectionPart.java | 2 - .../CollectionIdentifierDescriptorImpl.java | 2 - ...criminatedAssociationAttributeMapping.java | 3 - .../DiscriminatedAssociationMapping.java | 1 - .../internal/DiscriminatedCollectionPart.java | 3 - .../internal/EmbeddedAttributeMapping.java | 3 - .../internal/EmbeddedCollectionPart.java | 3 - .../EmbeddedForeignKeyDescriptor.java | 2 - .../internal/EntityCollectionPart.java | 6 +- .../internal/EntityVersionMappingImpl.java | 2 - .../internal/PluralAttributeMappingImpl.java | 25 +- .../internal/SimpleForeignKeyDescriptor.java | 2 - .../internal/ToOneAttributeMapping.java | 23 +- .../entity/AbstractEntityPersister.java | 5 +- .../entity/SingleTableEntityPersister.java | 6 +- .../entity/UnionSubclassEntityPersister.java | 5 +- .../QueryInterpretationCacheStandardImpl.java | 2 +- .../query/internal/SimpleQueryOptions.java | 34 + .../org/hibernate/query/results/Builders.java | 3 - .../DomainResultCreationStateImpl.java | 12 +- .../ImplicitAttributeFetchBuilder.java | 2 - .../query/results/JdbcValuesMappingImpl.java | 49 +- .../query/results/ResultSetMappingImpl.java | 5 +- .../query/results/TableGroupImpl.java | 12 +- .../CompleteFetchBuilderBasicPart.java | 2 - .../CompleteResultBuilderEntityJpa.java | 1 - .../CompleteResultBuilderEntityStandard.java | 1 - .../dynamic/DynamicFetchBuilderLegacy.java | 1 - .../dynamic/DynamicFetchBuilderStandard.java | 3 - .../DynamicResultBuilderEntityCalculated.java | 5 +- .../DynamicResultBuilderEntityStandard.java | 7 +- .../ImplicitFetchBuilderEmbeddable.java | 3 - .../implicit/ImplicitFetchBuilderEntity.java | 5 - .../implicit/ImplicitFetchBuilderPlural.java | 2 - ...licitModelPartResultBuilderEmbeddable.java | 2 - .../ImplicitModelPartResultBuilderEntity.java | 2 - .../hibernate/query/spi/AbstractQuery.java | 15 +- .../query/spi/QueryInterpretationCache.java | 6 + .../query/spi/SqlOmittingQueryOptions.java | 2 +- .../query/sql/internal/NativeQueryImpl.java | 26 +- .../query/sqm/SemanticQueryWalker.java | 1 - .../AbstractSqmFunctionDescriptor.java | 23 + ...actSqmSelfRenderingFunctionDescriptor.java | 43 ++ .../function/FunctionRenderingSupport.java | 10 + .../function/NamedSqmFunctionDescriptor.java | 40 +- .../PatternBasedSqmFunctionDescriptor.java | 12 +- ...ringAggregateFunctionSqlAstExpression.java | 54 ++ ...SelfRenderingFunctionSqlAstExpression.java | 4 + .../SelfRenderingSqmAggregateFunction.java | 54 ++ .../sqm/function/SqmFunctionDescriptor.java | 14 + .../sqm/function/SqmFunctionRegistry.java | 41 +- .../internal/ConcreteSqmSelectQueryPlan.java | 6 +- .../query/sqm/internal/QuerySqmImpl.java | 28 +- .../sqm/internal/SqmInterpretationsKey.java | 56 +- .../internal/MatchingIdSelectionHelper.java | 26 +- .../MultiTableSqmMutationConverter.java | 26 +- .../cte/AbstractCteMutationHandler.java | 22 +- .../idtable/ExecuteWithIdTableHelper.java | 21 +- .../idtable/ExecuteWithoutIdTableHelper.java | 2 +- .../DisjunctionRestrictionProducer.java | 128 --- .../internal/inline/InlineStrategy.java | 2 +- ...leValueConstructorRestrictionProducer.java | 57 -- .../NamedFunctionDescriptorBuilder.java | 9 +- .../PatternFunctionDescriptorBuilder.java | 5 +- .../function/internal/PatternRenderer.java | 44 +- .../sqm/spi/BaseSemanticQueryWalker.java | 1 - .../sqm/sql/BaseSqmToSqlAstConverter.java | 39 +- .../EntityValuedPathInterpretation.java | 2 - .../query/sqm/tree/cte/SqmCteStatement.java | 10 + .../query/sqm/tree/expression/Conversion.java | 2 +- .../sqm/tree/expression/SqmFunction.java | 23 +- .../org/hibernate/sql/ForUpdateFragment.java | 61 +- .../java/org/hibernate/sql/ast/Clause.java | 1 + .../hibernate/sql/ast/SqlAstTranslator.java | 5 + .../sql/ast/spi/AbstractSqlAstTranslator.java | 726 +++++++++++++++--- .../sql/ast/spi/AggregateFunctionChecker.java | 363 +++++++++ .../sql/ast/spi/SqlAstCreationState.java | 2 +- .../sql/ast/tree/cte/CteMaterialization.java | 28 + .../sql/ast/tree/cte/CteStatement.java | 12 + .../hibernate/sql/ast/tree/cte/CteTable.java | 2 +- .../sql/ast/tree/cte/CteTableGroup.java | 4 +- .../AggregateFunctionExpression.java | 19 + .../sql/ast/tree/expression/Distinct.java | 2 +- .../sql/ast/tree/expression/Duration.java | 2 +- .../sql/ast/tree/expression/DurationUnit.java | 4 +- .../sql/ast/tree/from/AbstractTableGroup.java | 14 +- .../ast/tree/from/CompositeTableGroup.java | 4 +- .../ast/tree/from/CorrelatedTableGroup.java | 2 +- .../sql/ast/tree/from/FromClause.java | 71 ++ .../sql/ast/tree/from/LazyTableGroup.java | 10 +- .../MutatingTableReferenceGroupWrapper.java | 4 +- .../ast/tree/from/RootTableGroupProducer.java | 1 - .../sql/ast/tree/from/StandardTableGroup.java | 12 +- .../sql/ast/tree/from/TableGroup.java | 2 +- .../sql/ast/tree/from/TableGroupJoin.java | 9 +- .../ast/tree/from/TableGroupJoinProducer.java | 3 - .../sql/ast/tree/from/TableJoin.java | 22 + .../sql/ast/tree/from/TableReferenceJoin.java | 11 +- .../sql/ast/tree/from/UnionTableGroup.java | 9 +- .../sql/ast/tree/select/QueryPart.java | 2 +- .../sql/exec/internal/CallbackImpl.java | 40 + .../JdbcSelectExecutorStandardImpl.java | 34 +- .../sql/exec/spi/AbstractJdbcOperation.java | 100 +++ .../org/hibernate/sql/exec/spi/Callback.java | 4 + .../sql/exec/spi/ExecutionContext.java | 5 + .../hibernate/sql/exec/spi/JdbcDelete.java | 19 +- .../hibernate/sql/exec/spi/JdbcInsert.java | 19 +- .../sql/exec/spi/JdbcLockStrategy.java | 28 + .../hibernate/sql/exec/spi/JdbcOperation.java | 10 +- .../hibernate/sql/exec/spi/JdbcSelect.java | 81 +- .../hibernate/sql/exec/spi/JdbcUpdate.java | 19 +- .../sql/exec/spi/NativeJdbcMutation.java | 10 + .../results/graph/AssemblerCreationState.java | 3 + .../sql/results/graph/FetchParent.java | 2 - .../sql/results/graph/Fetchable.java | 1 - .../internal/CollectionDomainResult.java | 1 - .../internal/EagerCollectionFetch.java | 5 - .../EntityCollectionPartTableGroup.java | 4 +- .../internal/EmbeddableFetchImpl.java | 2 - .../EmbeddableForeignKeyResultImpl.java | 3 - .../internal/EmbeddableResultImpl.java | 2 - .../entity/AbstractEntityInitializer.java | 53 +- .../entity/AbstractEntityResultGraphNode.java | 10 +- .../graph/entity/EntityValuedFetchable.java | 2 - .../internal/EntityFetchJoinedImpl.java | 13 +- .../entity/internal/EntityResultImpl.java | 8 +- .../EntityResultJoinedSubclassImpl.java | 2 +- .../sql/results/internal/ResultsHelper.java | 17 +- .../RowProcessingStateStandardImpl.java | 2 +- .../results/internal/StandardRowReader.java | 5 +- .../CircularBiDirectionalFetchImpl.java | 1 - .../internal/domain/CircularFetchImpl.java | 2 - .../internal/DeferredResultSetAccess.java | 157 ++-- ...luesSourceProcessingStateStandardImpl.java | 6 + ...oaderInitializationWithNoLockModeTest.java | 2 +- .../functional/OracleFollowOnLockingTest.java | 52 +- .../unit/lockhint/AbstractLockHintTest.java | 2 +- .../unit/lockhint/MySQLStorageEngineTest.java | 2 +- .../lockhint/SQLServer2005LockHintsTest.java | 2 +- .../unit/lockhint/SQLServerLockHintsTest.java | 2 +- .../lockhint/SybaseASE15LockHintsTest.java | 2 +- .../unit/lockhint/SybaseLockHintsTest.java | 2 +- .../unit/locktimeout/DB2LockTimeoutTest.java | 4 +- .../unit/locktimeout/HANALockTimeoutTest.java | 2 +- .../locktimeout/OracleLockTimeoutTest.java | 2 +- .../PostgreSQLLockTimeoutTest.java | 2 +- ...tractSequenceInformationExtractorTest.java | 2 +- ...B2390SequenceInformationExtractorTest.java | 2 +- ...B2400SequenceInformationExtractorTest.java | 2 +- ...alectSequenceInformationExtractorTest.java | 6 +- ...alectSequenceInformationExtractorTest.java | 6 +- ...alectSequenceInformationExtractorTest.java | 6 +- .../test}/engine/spi/EntityEntryTest.java | 9 +- .../test/jpa}/EntityManagerClosedTest.java | 4 +- .../{ => orm}/test/jpa/lock/JPALockTest.java | 2 +- .../{jpa/test => orm/test/jpa}/lock/Lock.java | 2 +- .../test/jpa/lock/LockExceptionTests.java | 2 +- .../test => orm/test/jpa}/lock/LockTest.java | 19 +- .../jpa}/lock/LockTimeoutPropertyTest.java | 2 +- .../test => orm/test/jpa}/lock/Lockable.java | 2 +- .../jpa}/lock/NativeSQLQueryTimeoutTest.java | 2 +- .../test => orm/test/jpa}/lock/Person.java | 2 +- .../test/jpa}/lock/QueryLockingTest.java | 2 +- .../test/jpa/lock/RepeatableReadTest.java | 2 +- ...tementIsClosedAfterALockExceptionTest.java | 2 +- .../test/jpa}/lock/UnversionedLock.java | 2 +- .../orm/test/jpa/lock/UpgradeLockTest.java | 2 - .../jpa/schemagen/JpaSchemaGeneratorTest.java | 2 +- .../{ => orm}/test/lob/BlobLocatorTest.java | 7 +- .../{ => orm}/test/lob/ClobLocatorTest.java | 7 +- .../{ => orm}/test/lob/LobHolder.java | 2 +- .../{ => orm}/test/lob/LobMappings.hbm.xml | 2 +- .../{ => orm}/test/lob/LobMergeTest.java | 7 +- .../hibernate/{ => orm}/test/locking/A.java | 2 +- .../test/locking/AbstractSkipLockedTest.java | 26 +- .../locking/HANAOptimisticLockingTest.java | 2 +- ...heritanceOptimisticForceIncrementTest.java | 2 +- .../{ => orm}/test/locking/LockModeTest.java | 17 +- .../test/locking/LockRefreshTest.java | 2 +- .../PessimisticReadSkipLockedTest.java | 2 +- .../PessimisticWriteLockTimeoutTest.java | 15 +- .../PessimisticWriteSkipLockedTest.java | 2 +- .../test/locking/UpgradeSkipLockedTest.java | 2 +- .../{ => orm}/test/locking/paging/Door.java | 2 +- .../locking/paging/PagingAndLockingTest.java | 6 +- .../genericApi/BasicGetLoadAccessTest.java | 2 +- .../genericApi/ProxiedGetLoadAccessTest.java | 2 +- .../QueryParametersValidationArrayTest.java | 2 +- .../orm/test/query/sqm/BaseSqmUnitTest.java | 6 + .../mutation/multitable/IdSelectionTests.java | 3 +- .../org/hibernate/testing/DialectChecks.java | 12 + .../junit4/BaseCoreFunctionalTestCase.java | 3 +- .../orm/junit/DialectFeatureChecks.java | 6 + 259 files changed, 3597 insertions(+), 1138 deletions(-) rename hibernate-core/src/main/java/org/hibernate/dialect/{RDBMSOS2200SqlAstTranslator.java => RDMSOS2200SqlAstTranslator.java} (86%) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/RowLockStrategy.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/internal/SimpleQueryOptions.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingAggregateFunctionSqlAstExpression.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/DisjunctionRestrictionProducer.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/TableValueConstructorRestrictionProducer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AggregateFunctionChecker.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteMaterialization.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/AggregateFunctionExpression.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableJoin.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/exec/internal/CallbackImpl.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/exec/spi/AbstractJdbcOperation.java create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcLockStrategy.java rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/batchfetch/BatchingEntityLoaderInitializationWithNoLockModeTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/functional/OracleFollowOnLockingTest.java (91%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/lockhint/AbstractLockHintTest.java (97%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/lockhint/MySQLStorageEngineTest.java (96%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/lockhint/SQLServer2005LockHintsTest.java (94%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/lockhint/SQLServerLockHintsTest.java (92%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/lockhint/SybaseASE15LockHintsTest.java (92%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/lockhint/SybaseLockHintsTest.java (91%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/locktimeout/DB2LockTimeoutTest.java (92%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/locktimeout/HANALockTimeoutTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/locktimeout/PostgreSQLLockTimeoutTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/sequence/AbstractSequenceInformationExtractorTest.java (96%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/sequence/DB2390SequenceInformationExtractorTest.java (94%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/sequence/DB2400SequenceInformationExtractorTest.java (94%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/sequence/DerbyTenFiveDialectSequenceInformationExtractorTest.java (87%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/sequence/DerbyTenSevenDialectSequenceInformationExtractorTest.java (89%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/dialect/unit/sequence/DerbyTenSixDialectSequenceInformationExtractorTest.java (89%) rename hibernate-core/src/test/java/org/hibernate/{ => orm/test}/engine/spi/EntityEntryTest.java (92%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/EntityManagerClosedTest.java (99%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/jpa/lock/JPALockTest.java (99%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/Lock.java (96%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/jpa/lock/LockExceptionTests.java (99%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/LockTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/LockTimeoutPropertyTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/Lockable.java (95%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/NativeSQLQueryTimeoutTest.java (97%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/Person.java (95%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/QueryLockingTest.java (99%) mode change 100755 => 100644 rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/jpa/lock/RepeatableReadTest.java (99%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/StatementIsClosedAfterALockExceptionTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{jpa/test => orm/test/jpa}/lock/UnversionedLock.java (95%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/lob/BlobLocatorTest.java (97%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/lob/ClobLocatorTest.java (97%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/lob/LobHolder.java (97%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/lob/LobMappings.hbm.xml (92%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/lob/LobMergeTest.java (96%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/A.java (96%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/AbstractSkipLockedTest.java (88%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/HANAOptimisticLockingTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/JoinedInheritanceOptimisticForceIncrementTest.java (98%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/LockModeTest.java (94%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/LockRefreshTest.java (99%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/PessimisticReadSkipLockedTest.java (85%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/PessimisticWriteLockTimeoutTest.java (85%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/PessimisticWriteSkipLockedTest.java (85%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/UpgradeSkipLockedTest.java (91%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/paging/Door.java (94%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/locking/paging/PagingAndLockingTest.java (94%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/ops/genericApi/BasicGetLoadAccessTest.java (99%) rename hibernate-core/src/test/java/org/hibernate/{ => orm}/test/ops/genericApi/ProxiedGetLoadAccessTest.java (98%) diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 6506f31604..a1ee800174 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -13,7 +13,7 @@ ext { junitVintageVersion = '5.7.1' junit5Version = '5.7.1' - h2Version = '1.4.199' + h2Version = '1.4.200' bytemanVersion = '4.0.13' //Compatible with JDK16 jnpVersion = '5.0.6.CR1' diff --git a/hibernate-core/src/main/java/org/hibernate/LockOptions.java b/hibernate-core/src/main/java/org/hibernate/LockOptions.java index b77774d368..0448f0e3e2 100644 --- a/hibernate-core/src/main/java/org/hibernate/LockOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/LockOptions.java @@ -124,7 +124,12 @@ public class LockOptions implements Serializable { if ( aliasSpecificLockModes == null ) { aliasSpecificLockModes = new LinkedHashMap<>(); } - aliasSpecificLockModes.put( alias, lockMode ); + if ( lockMode == null ) { + aliasSpecificLockModes.remove( alias ); + } + else { + aliasSpecificLockModes.put( alias, lockMode ); + } return this; } @@ -338,13 +343,16 @@ public class LockOptions implements Serializable { return destination; } - public boolean isCompatible(LockOptions that) { - if ( that == null ) { - return isEmpty(); - } - else if ( this == that ) { + @Override + public boolean equals(Object o) { + if ( this == o ) { return true; } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + + LockOptions that = (LockOptions) o; if ( timeout != that.timeout ) { return false; @@ -363,4 +371,14 @@ public class LockOptions implements Serializable { return followOnLocking != null ? followOnLocking.equals( that.followOnLocking ) : that.followOnLocking == null; } + @Override + public int hashCode() { + int result = lockMode != null ? lockMode.hashCode() : 0; + result = 31 * result + timeout; + result = 31 * result + ( aliasSpecificLockModes != null ? aliasSpecificLockModes.hashCode() : 0 ); + result = 31 * result + ( followOnLocking != null ? followOnLocking.hashCode() : 0 ); + result = 31 * result + ( scope ? 1 : 0 ); + return result; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractBagSemantics.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractBagSemantics.java index 8082d76e0e..e0313a74bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractBagSemantics.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractBagSemantics.java @@ -11,7 +11,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.collection.spi.BagSemantics; import org.hibernate.collection.spi.CollectionInitializerProducer; import org.hibernate.engine.FetchTiming; @@ -68,7 +67,6 @@ public abstract class AbstractBagSemantics implements BagSemantics implements BagSemantics implements BagSemantics implements BagSemantics implements BagSemantics implements BagSemantics, K, V> implement FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, DomainResultCreationState creationState) { return new MapInitializerProducer( attributeMapping, @@ -89,7 +87,6 @@ public abstract class AbstractMapSemantics, K, V> implement navigablePath.append( CollectionPart.Nature.INDEX.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ), @@ -98,7 +95,6 @@ public abstract class AbstractMapSemantics, K, V> implement navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ) @@ -112,7 +108,6 @@ public abstract class AbstractMapSemantics, K, V> implement FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, Fetch indexFetch, Fetch elementFetch, DomainResultCreationState creationState){ @@ -122,7 +117,6 @@ public abstract class AbstractMapSemantics, K, V> implement navigablePath.append( CollectionPart.Nature.INDEX.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ); @@ -133,7 +127,6 @@ public abstract class AbstractMapSemantics, K, V> implement navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractSetSemantics.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractSetSemantics.java index 2d9a4e3486..b4f565dac9 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractSetSemantics.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractSetSemantics.java @@ -10,7 +10,6 @@ import java.util.Iterator; import java.util.Set; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.collection.spi.CollectionInitializerProducer; import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.engine.FetchTiming; @@ -53,7 +52,6 @@ public abstract class AbstractSetSemantics,E> implements Colle FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, DomainResultCreationState creationState) { return new SetInitializerProducer( attributeMapping, @@ -62,7 +60,6 @@ public abstract class AbstractSetSemantics,E> implements Colle navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ) @@ -76,7 +73,6 @@ public abstract class AbstractSetSemantics,E> implements Colle FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, Fetch indexFetch, Fetch elementFetch, DomainResultCreationState creationState){ @@ -87,7 +83,6 @@ public abstract class AbstractSetSemantics,E> implements Colle fetchParent, selected, resultVariable, - lockMode, creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardArraySemantics.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardArraySemantics.java index a607c556a9..e280d471cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardArraySemantics.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardArraySemantics.java @@ -10,7 +10,6 @@ import java.util.Arrays; import java.util.Iterator; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.collection.spi.CollectionInitializerProducer; import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; @@ -101,7 +100,6 @@ public class StandardArraySemantics implements CollectionSemantics { FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, DomainResultCreationState creationState) { return new ArrayInitializerProducer( attributeMapping, @@ -110,7 +108,6 @@ public class StandardArraySemantics implements CollectionSemantics { navigablePath.append( CollectionPart.Nature.INDEX.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ), @@ -119,7 +116,6 @@ public class StandardArraySemantics implements CollectionSemantics { navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ) @@ -133,7 +129,6 @@ public class StandardArraySemantics implements CollectionSemantics { FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, Fetch indexFetch, Fetch elementFetch, DomainResultCreationState creationState){ @@ -143,7 +138,6 @@ public class StandardArraySemantics implements CollectionSemantics { navigablePath.append( CollectionPart.Nature.INDEX.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ); @@ -154,7 +148,6 @@ public class StandardArraySemantics implements CollectionSemantics { navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardListSemantics.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardListSemantics.java index ba6ee267dc..9804c68293 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardListSemantics.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/StandardListSemantics.java @@ -10,7 +10,6 @@ import java.util.Iterator; import java.util.List; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.collection.spi.CollectionInitializerProducer; import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; @@ -75,7 +74,6 @@ public class StandardListSemantics implements CollectionSemantics, E> FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, DomainResultCreationState creationState) { return new ListInitializerProducer( attributeMapping, @@ -84,7 +82,6 @@ public class StandardListSemantics implements CollectionSemantics, E> navigablePath.append( CollectionPart.Nature.INDEX.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ), @@ -93,7 +90,6 @@ public class StandardListSemantics implements CollectionSemantics, E> navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ) @@ -107,7 +103,6 @@ public class StandardListSemantics implements CollectionSemantics, E> FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, Fetch indexFetch, Fetch elementFetch, DomainResultCreationState creationState) { @@ -117,7 +112,6 @@ public class StandardListSemantics implements CollectionSemantics, E> navigablePath.append( CollectionPart.Nature.INDEX.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ); @@ -128,7 +122,6 @@ public class StandardListSemantics implements CollectionSemantics, E> navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), FetchTiming.IMMEDIATE, selected, - lockMode, null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java index 95512479b1..55a6bef25a 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/CollectionSemantics.java @@ -65,7 +65,6 @@ public interface CollectionSemantics { FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, DomainResultCreationState creationState); CollectionInitializerProducer createInitializerProducer( @@ -74,7 +73,6 @@ public interface CollectionSemantics { FetchParent fetchParent, boolean selected, String resultVariable, - LockMode lockMode, Fetch indexFetch, Fetch elementFetch, DomainResultCreationState creationState); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java index 0e350a6388..2b77bb459b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractHANADialect.java @@ -923,6 +923,11 @@ public abstract class AbstractHANADialect extends Dialect { return true; } + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return RowLockStrategy.COLUMN; + } + @Override public String getAddColumnString() { return "add ("; 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 6cae7c7fde..e0240b4b53 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -158,6 +158,11 @@ abstract class AbstractTransactSQLDialect extends Dialect { return ""; } + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return RowLockStrategy.TABLE; + } + @Override public String appendLockHint(LockOptions lockOptions, String tableName) { return lockOptions.getLockMode().greaterThan( LockMode.READ ) ? tableName + " holdlock" : tableName; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CacheSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/CacheSqlAstTranslator.java index 5c05f26ed6..b61360f9dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CacheSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CacheSqlAstTranslator.java @@ -34,6 +34,19 @@ public class CacheSqlAstTranslator extends AbstractSqlA super( sessionFactory, statement ); } + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + return LockStrategy.NONE; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Cache does not support the FOR UPDATE clause + } + @Override protected boolean needsRowsToSkip() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index a0fb506047..adaae56a72 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -6,6 +6,8 @@ */ package org.hibernate.dialect; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.OffsetFetchLimitHandler; @@ -24,6 +26,8 @@ import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.type.StandardBasicTypes; import java.sql.Types; +import java.util.Iterator; +import java.util.Map; import javax.persistence.TemporalType; @@ -43,10 +47,10 @@ public class CockroachDialect extends Dialect { // * no support for java.sql.Clob - private int version; + private final int version; public CockroachDialect() { - this(192); + this( 1920 ); } public CockroachDialect(DialectResolutionInfo info) { @@ -354,4 +358,151 @@ public class CockroachDialect extends Dialect { return OffsetFetchLimitHandler.INSTANCE; } + @Override + public String getForUpdateString(String aliases) { + return getForUpdateString() + " of " + aliases; + } + + @Override + public String getForUpdateString(LockOptions lockOptions) { + // Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html + if ( getVersion() < 2010 ) { + return ""; + } + return super.getForUpdateString( lockOptions ); + } + + @Override + public String getForUpdateString(String aliases, LockOptions lockOptions) { + // Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html + if ( getVersion() < 2010 ) { + return ""; + } + /* + * Parent's implementation for (aliases, lockOptions) ignores aliases. + */ + if ( aliases.isEmpty() ) { + LockMode lockMode = lockOptions.getLockMode(); + final Iterator> itr = lockOptions.getAliasLockIterator(); + while ( itr.hasNext() ) { + // seek the highest lock mode + final Map.Entry entry = itr.next(); + final LockMode lm = entry.getValue(); + if ( lm.greaterThan( lockMode ) ) { + aliases = entry.getKey(); + } + } + } + LockMode lockMode = lockOptions.getAliasSpecificLockMode( aliases ); + if (lockMode == null ) { + lockMode = lockOptions.getLockMode(); + } + switch ( lockMode ) { + //noinspection deprecation + case UPGRADE: + return getForUpdateString(aliases); + case PESSIMISTIC_READ: + return getReadLockString( aliases, lockOptions.getTimeOut() ); + case PESSIMISTIC_WRITE: + return getWriteLockString( aliases, lockOptions.getTimeOut() ); + case UPGRADE_NOWAIT: + //noinspection deprecation + case FORCE: + case PESSIMISTIC_FORCE_INCREMENT: + return getForUpdateNowaitString(aliases); + case UPGRADE_SKIPLOCKED: + return getForUpdateSkipLockedString(aliases); + default: + return ""; + } + } + + private String withTimeout(String lockString, int timeout) { + switch (timeout) { + case LockOptions.NO_WAIT: + return supportsNoWait() ? lockString + " nowait" : lockString; + case LockOptions.SKIP_LOCKED: + return supportsSkipLocked() ? lockString + " skip locked" : lockString; + default: + return lockString; + } + } + + @Override + public String getWriteLockString(int timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, int timeout) { + return withTimeout( getForUpdateString( aliases ), timeout ); + } + + @Override + public String getReadLockString(int timeout) { + return withTimeout(" for share", timeout ); + } + + @Override + public String getReadLockString(String aliases, int timeout) { + return withTimeout(" for share of " + aliases, timeout ); + } + + @Override + public String getForUpdateNowaitString() { + return supportsNoWait() + ? " for update nowait" + : getForUpdateString(); + } + + @Override + public String getForUpdateNowaitString(String aliases) { + return supportsNoWait() + ? " for update of " + aliases + " nowait" + : getForUpdateString(aliases); + } + + @Override + public String getForUpdateSkipLockedString() { + return supportsSkipLocked() + ? " for update skip locked" + : getForUpdateString(); + } + + @Override + public String getForUpdateSkipLockedString(String aliases) { + return supportsSkipLocked() + ? " for update of " + aliases + " skip locked" + : getForUpdateString( aliases ); + } + + @Override + public boolean supportsOuterJoinForUpdate() { + return false; + } + + @Override + public boolean supportsNoWait() { + return getVersion() >= 2010; + } + + @Override + public boolean supportsWait() { + return false; + } + + @Override + public boolean supportsSkipLocked() { + return getVersion() >= 2010; + } + + @Override + public boolean forUpdateOfColumns() { + return getVersion() >= 2010; + } + + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return getVersion() >= 2010 ? RowLockStrategy.TABLE : RowLockStrategy.NONE; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachSqlAstTranslator.java index f52b68d0c7..56298fd57c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachSqlAstTranslator.java @@ -29,6 +29,32 @@ public class CockroachSqlAstTranslator extends Abstract super( sessionFactory, statement ); } + @Override + protected String getForShare() { + return " for share"; + } + + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + // Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html + if ( getDialect().getVersion() < 2010 ) { + return LockStrategy.NONE; + } + return super.determineLockingStrategy( querySpec, forUpdateClause, followOnLocking ); + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html + if ( getDialect().getVersion() < 2010 ) { + return; + } + super.renderForUpdateClause( querySpec, forUpdateClause ); + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Check if current query part is already row numbering to avoid infinite recursion return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart 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 96b8e2a266..24df298752 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -57,11 +57,6 @@ import javax.persistence.TemporalType; */ public class DB2Dialect extends Dialect { - // KNOWN LIMITATIONS: - - // * can't select a parameter unless wrapped - // in a cast or function call - private static final String FOR_READ_ONLY_SQL = " for read only with rs"; private static final String FOR_SHARE_SQL = FOR_READ_ONLY_SQL + " use and keep share locks"; private static final String FOR_UPDATE_SQL = FOR_READ_ONLY_SQL + " use and keep update locks"; @@ -71,8 +66,7 @@ public class DB2Dialect extends Dialect { private final int version; - private LimitHandler limitHandler; - + private final LimitHandler limitHandler; private final UniqueDelegate uniqueDelegate; public DB2Dialect(DialectResolutionInfo info) { @@ -80,7 +74,7 @@ public class DB2Dialect extends Dialect { } public DB2Dialect() { - this(900); + this( 900 ); } public DB2Dialect(int version) { @@ -292,6 +286,10 @@ public class DB2Dialect extends Dialect { pattern.append("+("); // DB2 supports temporal arithmetic. See https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0023457.html switch (unit) { + case NATIVE: + // AFAICT the native format is seconds with fractional parts after the decimal point + pattern.append("?2) seconds"); + break; case NANOSECOND: pattern.append("(?2)/1e9) seconds"); break; @@ -339,20 +337,6 @@ public class DB2Dialect extends Dialect { } } - @Override - public String getReadLockString(int timeout) { - return timeout==LockOptions.SKIP_LOCKED - ? FOR_SHARE_SKIP_LOCKED_SQL - : FOR_SHARE_SQL; - } - - @Override - public String getWriteLockString(int timeout) { - return timeout==LockOptions.SKIP_LOCKED - ? FOR_UPDATE_SKIP_LOCKED_SQL - : FOR_UPDATE_SQL; - } - @Override public String getForUpdateString() { return FOR_UPDATE_SQL; @@ -360,13 +344,36 @@ public class DB2Dialect extends Dialect { @Override public boolean supportsSkipLocked() { - return true; + // Introduced in 11.5: https://www.ibm.com/docs/en/db2/11.5?topic=statement-concurrent-access-resolution-clause + return getVersion() >= 1150; } @Override public String getForUpdateSkipLockedString() { - return FOR_UPDATE_SKIP_LOCKED_SQL; + return supportsSkipLocked() + ? FOR_UPDATE_SKIP_LOCKED_SQL + : FOR_UPDATE_SQL; } + + @Override + public String getForUpdateSkipLockedString(String aliases) { + return getForUpdateSkipLockedString(); + } + + @Override + public String getWriteLockString(int timeout) { + return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + ? FOR_UPDATE_SKIP_LOCKED_SQL + : FOR_UPDATE_SQL; + } + + @Override + public String getReadLockString(int timeout) { + return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked() + ? FOR_SHARE_SKIP_LOCKED_SQL + : FOR_SHARE_SQL; + } + @Override public boolean supportsOuterJoinForUpdate() { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java index 9a64c960ec..cb68147cd0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2SqlAstTranslator.java @@ -42,6 +42,21 @@ public class DB2SqlAstTranslator extends AbstractSqlAst super( sessionFactory, statement ); } + @Override + protected String getForUpdate() { + return " for read only with rs use and keep update locks"; + } + + @Override + protected String getForShare() { + return " for read only with rs use and keep share locks"; + } + + @Override + protected String getSkipLocked() { + return " skip locked data"; + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Percent fetches or ties fetches aren't supported in DB2 // According to LegacyDB2LimitHandler, variable limit also isn't supported before 11.1 @@ -59,7 +74,7 @@ public class DB2SqlAstTranslator extends AbstractSqlAst @Override public void visitQueryGroup(QueryGroup queryGroup) { final boolean emulateFetchClause = shouldEmulateFetchClause( queryGroup ); - if ( emulateFetchClause || hasOffset( queryGroup ) && !supportsOffsetClause() ) { + if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( queryGroup ) ) { emulateFetchOffsetWithWindowFunctions( queryGroup, emulateFetchClause ); } else { @@ -70,7 +85,7 @@ public class DB2SqlAstTranslator extends AbstractSqlAst @Override public void visitQuerySpec(QuerySpec querySpec) { final boolean emulateFetchClause = shouldEmulateFetchClause( querySpec ); - if ( emulateFetchClause || hasOffset( querySpec ) && !supportsOffsetClause() ) { + if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( querySpec ) ) { emulateFetchOffsetWithWindowFunctions( querySpec, emulateFetchClause ); } else { @@ -81,7 +96,7 @@ public class DB2SqlAstTranslator extends AbstractSqlAst @Override public void visitOffsetFetchClause(QueryPart queryPart) { if ( !isRowNumberingCurrentQueryPart() ) { - if ( !hasOffset( queryPart ) || supportsOffsetClause() ) { + if ( supportsOffsetClause() || !hasOffset( queryPart ) ) { renderOffsetFetchClause( queryPart, true ); } else if ( queryPart.isRoot() && hasLimit() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java index 0eec2ae8f6..7a71e0d9d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2iDialect.java @@ -6,6 +6,7 @@ */ package org.hibernate.dialect; +import org.hibernate.LockOptions; import org.hibernate.dialect.identity.DB2390IdentityColumnSupport; import org.hibernate.dialect.identity.DB2IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; @@ -87,11 +88,6 @@ public class DB2iDialect extends DB2Dialect { } } - @Override - public String getForUpdateString() { - return " for update with rs"; - } - @Override public LimitHandler getLimitHandler() { if ( getIVersion() >= 730) { @@ -112,6 +108,11 @@ public class DB2iDialect extends DB2Dialect { } } + @Override + public boolean supportsSkipLocked() { + return true; + } + @Override public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { return new StandardSqlAstTranslatorFactory() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index c01ddce3fb..b63da2ec8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -10,6 +10,7 @@ import java.sql.Types; import javax.persistence.TemporalType; +import org.hibernate.LockOptions; import org.hibernate.dialect.identity.DB2390IdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.FetchLimitHandler; @@ -36,11 +37,11 @@ public class DB2zDialect extends DB2Dialect { private final int version; public DB2zDialect(DialectResolutionInfo info) { - this( info.getDatabaseMajorVersion() ); + this( info.getDatabaseMajorVersion() * 100 + info.getDatabaseMinorVersion() * 10 ); } public DB2zDialect() { - this(7); + this( 700 ); } public DB2zDialect(int version) { @@ -55,7 +56,7 @@ public class DB2zDialect extends DB2Dialect { @Override public boolean supportsTimezoneTypes() { - return getVersion() > 1000; + return getZVersion() > 1000; } int getZVersion() { @@ -64,14 +65,14 @@ public class DB2zDialect extends DB2Dialect { @Override public SequenceSupport getSequenceSupport() { - return getZVersion() < 8 + return getZVersion() < 800 ? NoSequenceSupport.INSTANCE : DB2390SequenceSupport.INSTANCE; } @Override public String getQuerySequencesString() { - return getZVersion() < 8 ? null : "select * from sysibm.syssequences"; + return getZVersion() < 800 ? null : "select * from sysibm.syssequences"; } @Override @@ -84,6 +85,11 @@ public class DB2zDialect extends DB2Dialect { return new DB2390IdentityColumnSupport(); } + @Override + public boolean supportsSkipLocked() { + return true; + } + @Override public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) { StringBuilder pattern = new StringBuilder(); 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 073b73e20e..4d3d3ed870 100755 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -401,6 +401,16 @@ public class DerbyDialect extends Dialect { return false; } + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return RowLockStrategy.NONE; + } + + @Override + public RowLockStrategy getReadRowLockStrategy() { + return RowLockStrategy.NONE; + } + @Override public String getForUpdateString() { return " for update with rs"; @@ -416,11 +426,6 @@ public class DerbyDialect extends Dialect { return " for read only with rs"; } - @Override - public String getWriteLockString(String aliases, int timeout) { - return " for update of " + aliases + " with rs"; - } - @Override public boolean supportsOuterJoinForUpdate() { //TODO: check this! diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java index e0a64a2dc3..43f9b92c16 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbySqlAstTranslator.java @@ -39,6 +39,21 @@ public class DerbySqlAstTranslator extends AbstractSqlA super( sessionFactory, statement ); } + @Override + protected String getForUpdate() { + return " for update"; + } + + @Override + protected String getForShare() { + return " for read only"; + } + + @Override + protected String getForUpdateWithClause() { + return " with rs"; + } + @Override public void visitCteContainer(CteContainer cteContainer) { if ( cteContainer.isWithRecursive() ) { 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 4f999a2274..c008e9c378 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -1725,12 +1725,29 @@ public abstract class Dialect implements ConversionContext { * * @return True if the database supports FOR UPDATE OF syntax; * false otherwise. + * @deprecated Use {@link #getWriteRowLockStrategy()} instead */ + @Deprecated public boolean forUpdateOfColumns() { // by default we report no support return false; } + /** + * The row lock strategy to use for write locks. + */ + public RowLockStrategy getWriteRowLockStrategy() { + // by default we report no support + return RowLockStrategy.NONE; + } + + /** + * The row lock strategy to use for read locks. + */ + public RowLockStrategy getReadRowLockStrategy() { + return getWriteRowLockStrategy(); + } + /** * Does this dialect support FOR UPDATE in conjunction with * outer joined rows? @@ -1843,7 +1860,9 @@ public abstract class Dialect implements ConversionContext { * @param lockOptions The lock options to apply * @param tableName The name of the table to which to apply the lock hint. * @return The table with any required lock hints. + * @deprecated This was moved to {@link AbstractSqlAstTranslator} */ + @Deprecated public String appendLockHint(LockOptions lockOptions, String tableName){ return tableName; } @@ -3515,6 +3534,15 @@ public abstract class Dialect implements ConversionContext { return false; } + /** + * Does this dialect/database support WAIT timeout. + * + * @return {@code true} if WAIT is supported + */ + public boolean supportsWait() { + return supportsNoWait(); + } + /** * Inline String literal. * @@ -3732,6 +3760,23 @@ public abstract class Dialect implements ConversionContext { return "X'" + StandardBasicTypes.BINARY.toString( bytes ) + "'"; } + public RowLockStrategy getLockRowIdentifier(LockMode lockMode) { + switch ( lockMode ) { + case PESSIMISTIC_READ: + return getReadRowLockStrategy(); + case WRITE: + case FORCE: + case PESSIMISTIC_FORCE_INCREMENT: + case PESSIMISTIC_WRITE: + case UPGRADE: + case UPGRADE_SKIPLOCKED: + case UPGRADE_NOWAIT: + return getWriteRowLockStrategy(); + default: + return RowLockStrategy.NONE; + } + } + /** * Pluggable strategy for determining the Size to use for columns of * a given SQL type. diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdSqlAstTranslator.java index e6fbaa166c..74ff31fbde 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/FirebirdSqlAstTranslator.java @@ -13,7 +13,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.collections.Stack; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.query.ComparisonOperator; -import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; @@ -27,7 +26,6 @@ import org.hibernate.sql.ast.tree.expression.FunctionExpression; import org.hibernate.sql.ast.tree.expression.JdbcLiteral; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.sql.ast.tree.expression.NullnessLiteral; import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; import org.hibernate.sql.ast.tree.expression.SqlTuple; @@ -52,6 +50,11 @@ public class FirebirdSqlAstTranslator extends AbstractS super( sessionFactory, statement ); } + @Override + protected String getForUpdate() { + return " with lock"; + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Percent fetches or ties fetches aren't supported in Firebird // Before 3.0 there was also no support for window functions 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 1ca9752e7b..69a37e5195 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -101,7 +101,7 @@ public class H2Dialect extends Dialect { // Prior to 1.4.200 the 'cascade' in 'drop table' was implicit cascadeConstraints = version >= 104200; // 1.4.200 introduced changes in current_time and current_timestamp - useLocalTime = version >= 140199; + useLocalTime = version >= 104199; getDefaultProperties().setProperty( AvailableSettings.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); // http://code.google.com/p/h2database/issues/detail?id=235 @@ -193,7 +193,13 @@ public class H2Dialect extends Dialect { CommonFunctionFactory.median( queryEngine ); CommonFunctionFactory.stddevPopSamp( queryEngine ); CommonFunctionFactory.varPopSamp( queryEngine ); - CommonFunctionFactory.format_formatdatetime( queryEngine ); + if ( version == 104200 ) { + // See https://github.com/h2database/h2database/issues/2518 + CommonFunctionFactory.format_toChar( queryEngine ); + } + else { + CommonFunctionFactory.format_formatdatetime( queryEngine ); + } CommonFunctionFactory.rownum( queryEngine ); } @@ -432,6 +438,10 @@ public class H2Dialect extends Dialect { @Override public String translateDatetimeFormat(String format) { + if ( version == 104200 ) { + // See https://github.com/h2database/h2database/issues/2518 + return OracleDialect.datetimeFormat( format, true, true ).result(); + } return new Replacer( format, "'", "''" ) .replace("e", "u") .replace( "xxx", "XXX" ) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java index bd1b7915f8..b5bb7601dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLSqlAstTranslator.java @@ -10,18 +10,16 @@ import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; -import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.expression.Expression; -import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.Literal; -import org.hibernate.sql.ast.tree.expression.NullnessLiteral; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.select.QueryPart; +import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.exec.spi.JdbcOperation; /** @@ -35,6 +33,30 @@ public class HSQLSqlAstTranslator extends AbstractSqlAs super( sessionFactory, statement ); } + @Override + public boolean supportsFilterClause() { + return true; + } + + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + if ( getDialect().getVersion() < 200 ) { + return LockStrategy.NONE; + } + return super.determineLockingStrategy( querySpec, forUpdateClause, followOnLocking ); + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + if ( getDialect().getVersion() < 200 ) { + return; + } + super.renderForUpdateClause( querySpec, forUpdateClause ); + } + @Override public void visitOffsetFetchClause(QueryPart queryPart) { if ( supportsOffsetFetchClause() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/IngresSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/IngresSqlAstTranslator.java index be69951826..eb3f53702e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/IngresSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/IngresSqlAstTranslator.java @@ -34,6 +34,19 @@ public class IngresSqlAstTranslator extends AbstractSql super( sessionFactory, statement ); } + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + return LockStrategy.NONE; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Ingres does not support the FOR UPDATE clause + } + @Override protected void renderFetchPlusOffsetExpression( Expression fetchClauseExpression, diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java index 22fbf9d23c..01d7e30ce5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBSqlAstTranslator.java @@ -30,6 +30,11 @@ public class MariaDBSqlAstTranslator extends AbstractSq super( sessionFactory, statement ); } + @Override + protected String getForShare() { + return " lock in share mode"; + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Check if current query part is already row numbering to avoid infinite recursion return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart && supportsWindowFunctions() && !isRowsOnlyFetchClauseType( queryPart ); 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 1b97fe85e9..d639d77517 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -956,11 +956,17 @@ public class MySQLDialect extends Dialect { return getMySQLVersion() >= 800; } + @Override public boolean supportsWait() { //only supported on MariaDB return false; } + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return supportsAliasLocks() ? RowLockStrategy.TABLE : RowLockStrategy.NONE; + } + boolean supportsForShare() { return getMySQLVersion() >= 800; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java index 6a4ab95881..7ab190c3d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLSqlAstTranslator.java @@ -30,6 +30,11 @@ public class MySQLSqlAstTranslator extends AbstractSqlA super( sessionFactory, statement ); } + @Override + protected String getForShare() { + return getDialect().getVersion() >= 800 ? " for share" : " lock in share mode"; + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Check if current query part is already row numbering to avoid infinite recursion return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index fdc2451459..061b7a40bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -1058,6 +1058,11 @@ public class OracleDialect extends Dialect { return true; } + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return RowLockStrategy.COLUMN; + } + @Override public String getForUpdateNowaitString() { return " for update nowait"; @@ -1092,7 +1097,7 @@ public class OracleDialect extends Dialect { case LockOptions.WAIT_FOREVER: return lockString; default: - return supportsNoWait() ? lockString + " wait " + Math.round(timeout / 1e3f) : lockString; + return supportsWait() ? lockString + " wait " + Math.round(timeout / 1e3f) : lockString; } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java index fea474aeda..78dff7e9ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java @@ -6,11 +6,14 @@ */ package org.hibernate.dialect; +import java.util.Collections; import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.collections.Stack; import org.hibernate.query.ComparisonOperator; +import org.hibernate.query.FetchClauseType; +import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; @@ -20,11 +23,13 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.UnionTableGroup; import org.hibernate.sql.ast.tree.insert.Values; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; +import org.hibernate.sql.ast.tree.select.SortSpecification; import org.hibernate.sql.exec.spi.JdbcOperation; /** @@ -38,11 +43,126 @@ public class OracleSqlAstTranslator extends AbstractSql super( sessionFactory, statement ); } + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + LockStrategy strategy = super.determineLockingStrategy( querySpec, forUpdateClause, followOnLocking ); + final boolean followOnLockingDisabled = Boolean.FALSE.equals( followOnLocking ); + if ( strategy != LockStrategy.FOLLOW_ON && querySpec.hasSortSpecifications() ) { + if ( followOnLockingDisabled ) { + throw new IllegalQueryOperationException( "Locking with ORDER BY is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + // Oracle also doesn't support locks with set operators + // See https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_10002.htm#i2066346 + if ( strategy != LockStrategy.FOLLOW_ON && isPartOfQueryGroup() ) { + if ( followOnLockingDisabled ) { + throw new IllegalQueryOperationException( "Locking with set operators is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + if ( strategy != LockStrategy.FOLLOW_ON && hasSetOperations( querySpec ) ) { + if ( followOnLockingDisabled ) { + throw new IllegalQueryOperationException( "Locking with set operators is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + if ( strategy != LockStrategy.FOLLOW_ON && useOffsetFetchClause( querySpec ) && !isRowsOnlyFetchClauseType( querySpec ) ) { + if ( followOnLockingDisabled ) { + throw new IllegalQueryOperationException( "Locking with FETCH is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + if ( strategy != LockStrategy.FOLLOW_ON ) { + final boolean hasOffset; + if ( querySpec.isRoot() && hasLimit() && getLimit().getFirstRowJpa() != 0 ) { + hasOffset = true; + // We must record that the generated SQL depends on the fact that there is an offset + addAppliedParameterBinding( getOffsetParameter(), null ); + } + else { + hasOffset = querySpec.getOffsetClauseExpression() != null; + } + if ( hasOffset ) { + if ( followOnLockingDisabled ) { + throw new IllegalQueryOperationException( "Locking with OFFSET is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + } + return strategy; + } + + private boolean hasSetOperations(QuerySpec querySpec) { + return querySpec.getFromClause().queryTableGroups( group -> group instanceof UnionTableGroup ? group : null ) != null; + } + + private boolean isPartOfQueryGroup() { + return getQueryPartStack().findCurrentFirst( part -> part instanceof QueryGroup ? part : null ) != null; + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Check if current query part is already row numbering to avoid infinite recursion - return getQueryPartForRowNumbering() != queryPart && !supportsOffsetFetchClause() && ( - queryPart.isRoot() && hasLimit() || queryPart.getFetchClauseExpression() != null || queryPart.getOffsetClauseExpression() != null - ); + if (getQueryPartForRowNumbering() == queryPart) { + return false; + } + final boolean hasLimit = queryPart.isRoot() && hasLimit() || queryPart.getFetchClauseExpression() != null + || queryPart.getOffsetClauseExpression() != null; + if ( !hasLimit ) { + return false; + } + // Even if Oracle supports the OFFSET/FETCH clause, there are conditions where we still want to use the ROWNUM pagination + if ( supportsOffsetFetchClause() ) { + // When the query has no sort specifications and offset, we want to use the ROWNUM pagination as that is a special locking case + return !queryPart.hasSortSpecifications() && !hasOffset( queryPart ); + } + return true; + } + + @Override + protected void emulateFetchOffsetWithWindowFunctions( + QueryPart queryPart, + Expression offsetExpression, + Expression fetchExpression, + FetchClauseType fetchClauseType, + boolean emulateFetchClause) { + if ( queryPart instanceof QuerySpec && !queryPart.hasSortSpecifications() && offsetExpression == null && fetchClauseType == FetchClauseType.ROWS_ONLY ) { + // Special case for Oracle to support locking along with simple max results paging + final QuerySpec querySpec = (QuerySpec) queryPart; + withRowNumbering( + querySpec, + () -> { + appendSql( "select * from (" ); + super.visitQuerySpec( querySpec ); + appendSql( ") where rownum <= " ); + final Stack clauseStack = getClauseStack(); + clauseStack.push( Clause.WHERE ); + try { + fetchExpression.accept( this ); + + // We render the FOR UPDATE clause in the outer query + clauseStack.pop(); + clauseStack.push( Clause.FOR_UPDATE ); + visitForUpdateClause( querySpec ); + } + finally { + clauseStack.pop(); + } + } + ); + } + else { + super.emulateFetchOffsetWithWindowFunctions( + queryPart, + offsetExpression, + fetchExpression, + fetchClauseType, + emulateFetchClause + ); + } } @Override @@ -104,7 +224,18 @@ public class OracleSqlAstTranslator extends AbstractSql @Override protected void renderRowNumber(SelectClause selectClause, QueryPart queryPart) { if ( supportsOffsetFetchClause() || selectClause.isDistinct() ) { - super.renderRowNumber( selectClause, queryPart ); + final List sortSpecifications = getSortSpecificationsRowNumbering( selectClause, queryPart ); + if ( selectClause.isDistinct() ) { + appendSql( "dense_rank()" ); + } + else { + if ( sortSpecifications.isEmpty() ) { + appendSql( "rownum" ); + return; + } + appendSql( "row_number()" ); + } + visitOverClause( Collections.emptyList(), sortSpecifications ); } else { appendSql( "rownum" ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 7061d62870..adff3353c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -784,11 +784,21 @@ public class PostgreSQLDialect extends Dialect { return getVersion() >= 810; } + @Override + public boolean supportsWait() { + return false; + } + @Override public boolean supportsSkipLocked() { return getVersion() >= 950; } + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return RowLockStrategy.TABLE; + } + @Override public GroupBySummarizationRenderingStrategy getGroupBySummarizationRenderingStrategy() { return getVersion() >= 950 ? GroupBySummarizationRenderingStrategy.FUNCTION : GroupBySummarizationRenderingStrategy.NONE; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java index 996673ea87..d1155175dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLSqlAstTranslator.java @@ -10,6 +10,7 @@ import org.hibernate.query.FetchClauseType; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.tree.Statement; +import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; @@ -30,6 +31,26 @@ public class PostgreSQLSqlAstTranslator extends Abstrac super( sessionFactory, statement ); } + @Override + protected void renderMaterializationHint(CteMaterialization materialization) { + if ( getDialect().getVersion() >= 1200 ) { + if ( materialization == CteMaterialization.NOT_MATERIALIZED ) { + appendSql( "not " ); + } + appendSql( "materialized " ); + } + } + + @Override + public boolean supportsFilterClause() { + return getDialect().getVersion() >= 940; + } + + @Override + protected String getForShare() { + return " for share"; + } + protected boolean shouldEmulateFetchClause(QueryPart queryPart) { // Check if current query part is already row numbering to avoid infinite recursion if ( getQueryPartForRowNumbering() == queryPart || isRowsOnlyFetchClauseType( queryPart ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java index f56face4f6..6d70230802 100755 --- a/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200Dialect.java @@ -176,7 +176,7 @@ public class RDMSOS2200Dialect extends Dialect { @Override protected SqlAstTranslator buildTranslator( SessionFactoryImplementor sessionFactory, Statement statement) { - return new RDBMSOS2200SqlAstTranslator<>( sessionFactory, statement ); + return new RDMSOS2200SqlAstTranslator<>( sessionFactory, statement ); } }; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/RDBMSOS2200SqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200SqlAstTranslator.java similarity index 86% rename from hibernate-core/src/main/java/org/hibernate/dialect/RDBMSOS2200SqlAstTranslator.java rename to hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200SqlAstTranslator.java index c24b908b3d..00294c531d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/RDBMSOS2200SqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/RDMSOS2200SqlAstTranslator.java @@ -11,7 +11,6 @@ import java.util.List; import org.hibernate.query.FetchClauseType; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; -import org.hibernate.query.Limit; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; @@ -22,6 +21,7 @@ import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.select.QueryPart; +import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.exec.spi.JdbcOperation; /** @@ -29,12 +29,25 @@ import org.hibernate.sql.exec.spi.JdbcOperation; * * @author Christian Beikov */ -public class RDBMSOS2200SqlAstTranslator extends AbstractSqlAstTranslator { +public class RDMSOS2200SqlAstTranslator extends AbstractSqlAstTranslator { - public RDBMSOS2200SqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { + public RDMSOS2200SqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { super( sessionFactory, statement ); } + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + return LockStrategy.NONE; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Unisys 2200 does not support the FOR UPDATE clause + } + @Override public void visitOffsetFetchClause(QueryPart queryPart) { if ( queryPart.isRoot() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/RowLockStrategy.java b/hibernate-core/src/main/java/org/hibernate/dialect/RowLockStrategy.java new file mode 100644 index 0000000000..d4d87b5158 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/RowLockStrategy.java @@ -0,0 +1,27 @@ +/* + * 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 . + */ +package org.hibernate.dialect; + +/** + * The strategy for rendering which row to lock with the FOR UPDATE OF clause. + * + * @author Christian Beikov + */ +public enum RowLockStrategy { + /** + * Use a column name. + */ + COLUMN, + /** + * Use a table alias. + */ + TABLE, + /** + * No support for specifying rows to lock. + */ + NONE; +} diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 012e55d03f..a82a1b8309 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -394,6 +394,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { return getVersion() >= 9; } + @Override + public boolean supportsWait() { + return false; + } + @Override public GroupBySummarizationRenderingStrategy getGroupBySummarizationRenderingStrategy() { return GroupBySummarizationRenderingStrategy.CLAUSE; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java index 2b1783a34b..bf2ac41f8a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerSqlAstTranslator.java @@ -8,6 +8,8 @@ package org.hibernate.dialect; import java.util.List; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; import org.hibernate.query.FetchClauseType; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; @@ -20,6 +22,8 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; @@ -34,10 +38,98 @@ import org.hibernate.sql.exec.spi.JdbcOperation; */ public class SQLServerSqlAstTranslator extends AbstractSqlAstTranslator { + private static final String UNION_ALL = " union all "; + public SQLServerSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) { super( sessionFactory, statement ); } + @Override + protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) { + final String tableExpression = tableReference.getTableExpression(); + if ( tableReference instanceof UnionTableReference && lockMode != LockMode.NONE && tableExpression.charAt( 0 ) == '(' ) { + // SQL Server requires to push down the lock hint to the actual table names + int searchIndex = 0; + int unionIndex; + while ( ( unionIndex = tableExpression.indexOf( UNION_ALL, searchIndex ) ) != -1 ) { + appendSql( tableExpression.substring( searchIndex, unionIndex ) ); + renderLockHint( lockMode ); + appendSql( UNION_ALL ); + searchIndex = unionIndex + UNION_ALL.length(); + } + appendSql( tableExpression.substring( searchIndex, tableExpression.length() - 2 ) ); + renderLockHint( lockMode ); + appendSql( " )" ); + + registerAffectedTable( tableReference ); + final Clause currentClause = getClauseStack().getCurrent(); + if ( rendersTableReferenceAlias( currentClause ) ) { + final String identificationVariable = tableReference.getIdentificationVariable(); + if ( identificationVariable != null ) { + appendSql( getDialect().getTableAliasSeparator() ); + appendSql( identificationVariable ); + } + } + } + else { + super.renderTableReference( tableReference, lockMode ); + renderLockHint( lockMode ); + } + // Just always return true because SQL Server doesn't support the FOR UPDATE clause + return true; + } + + private void renderLockHint(LockMode lockMode) { + if ( getDialect().getVersion() >= 9 ) { + final int effectiveLockTimeout = getEffectiveLockTimeout( lockMode ); + final String writeLockStr = effectiveLockTimeout == LockOptions.SKIP_LOCKED ? "updlock" : "updlock, holdlock"; + final String readLockStr = effectiveLockTimeout == LockOptions.SKIP_LOCKED ? "updlock" : "holdlock"; + + final String noWaitStr = effectiveLockTimeout == LockOptions.NO_WAIT ? ", nowait" : ""; + final String skipLockStr = effectiveLockTimeout == LockOptions.SKIP_LOCKED ? ", readpast" : ""; + + switch ( lockMode ) { + //noinspection deprecation + case UPGRADE: + case PESSIMISTIC_WRITE: + case WRITE: + appendSql( " with (" + writeLockStr + ", rowlock" + noWaitStr + skipLockStr + ")" ); + break; + case PESSIMISTIC_READ: + appendSql( " with (" + readLockStr + ", rowlock" + noWaitStr + skipLockStr + ")" ); + break; + case UPGRADE_SKIPLOCKED: + appendSql( " with (updlock, rowlock, readpast" + noWaitStr + ")" ); + break; + case UPGRADE_NOWAIT: + appendSql( " with (updlock, holdlock, rowlock, nowait)" ); + break; + } + } + else { + switch ( lockMode ) { + //noinspection deprecation + case UPGRADE: + case UPGRADE_NOWAIT: + case PESSIMISTIC_WRITE: + case WRITE: + appendSql( " with (updlock, rowlock)" ); + break; + case PESSIMISTIC_READ: + appendSql(" with (holdlock, rowlock)" ); + break; + case UPGRADE_SKIPLOCKED: + appendSql( " with (updlock, rowlock, readpast)" ); + break; + } + } + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // SQL Server does not support the FOR UPDATE clause + } + protected OffsetFetchClauseMode getOffsetFetchClauseMode(QueryPart queryPart) { final int version = getDialect().getVersion(); final boolean hasLimit; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java index 8aee479c55..abcc7752e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java @@ -109,25 +109,25 @@ public class SpannerDialect extends Dialect { super.initializeFunctionRegistry( queryEngine ); // Aggregate Functions - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "any_value" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "any_value" ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "array_agg" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "array_agg" ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "countif" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "countif" ) .setInvariantType( StandardBasicTypes.LONG ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "logical_and" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "logical_and" ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "logical_or" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "logical_or" ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "string_agg" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "string_agg" ) .setInvariantType( StandardBasicTypes.STRING ) .setArgumentCountBetween( 1, 2 ) .register(); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java index ae49cd2860..c53905aaa6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerSqlAstTranslator.java @@ -19,6 +19,7 @@ import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; import org.hibernate.sql.ast.tree.select.QueryPart; +import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.exec.spi.JdbcOperation; /** @@ -32,6 +33,19 @@ public class SpannerSqlAstTranslator extends AbstractSq super( sessionFactory, statement ); } + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + return LockStrategy.NONE; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Spanner does not support the FOR UPDATE clause + } + @Override public void visitOffsetFetchClause(QueryPart queryPart) { renderLimitOffsetClause( queryPart ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java index 87bbb629b6..4cca3d944b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java @@ -47,7 +47,7 @@ public class SybaseASEDialect extends SybaseDialect { } public SybaseASEDialect(int version) { - super(version); + super( version ); //On Sybase ASE, the 'bit' type cannot be null, //and cannot have indexes (while we don't use @@ -421,15 +421,20 @@ public class SybaseASEDialect extends SybaseDialect { return getVersion() >= 1570; } + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return getVersion() >= 1570 ? RowLockStrategy.COLUMN : RowLockStrategy.TABLE; + } + @Override public String getForUpdateString() { - return getVersion() < 1570 ? super.getForUpdateString() : " for update"; + return getVersion() < 1570 ? "" : " for update"; } @Override public String getForUpdateString(String aliases) { return getVersion() < 1570 - ? super.getForUpdateString( aliases ) + ? "" : getForUpdateString() + " of " + aliases; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java index e2cc4f7243..34fa53948e 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASESqlAstTranslator.java @@ -8,6 +8,7 @@ package org.hibernate.dialect; import java.util.List; +import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; @@ -18,6 +19,7 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; @@ -34,6 +36,26 @@ public class SybaseASESqlAstTranslator extends Abstract super( sessionFactory, statement ); } + @Override + protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) { + super.renderTableReference( tableReference, lockMode ); + if ( getDialect().getVersion() < 1570 ) { + if ( LockMode.READ.lessThan( lockMode ) ) { + appendSql( " holdlock" ); + } + return true; + } + return false; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + if ( getDialect().getVersion() < 1570 ) { + return; + } + super.renderForUpdateClause( querySpec, forUpdateClause ); + } + @Override protected void renderSearchClause(CteStatement cte) { // Sybase ASE does not support this, but it's just a hint anyway diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java index 10e9dd626b..59781d541f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereDialect.java @@ -7,12 +7,14 @@ package org.hibernate.dialect; +import org.hibernate.LockOptions; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.identity.SybaseAnywhereIdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.TopLimitHandler; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.sql.ForUpdateFragment; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; @@ -20,6 +22,7 @@ import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; import java.sql.Types; +import java.util.Map; /** * SQL Dialect for Sybase Anywhere @@ -28,7 +31,7 @@ import java.sql.Types; public class SybaseAnywhereDialect extends SybaseDialect { public SybaseAnywhereDialect() { - this(8); + this( 800 ); } public SybaseAnywhereDialect(DialectResolutionInfo info){ @@ -130,6 +133,41 @@ public class SybaseAnywhereDialect extends SybaseDialect { return new SybaseAnywhereIdentityColumnSupport(); } + @Override + public boolean forUpdateOfColumns() { + return getVersion() >= 1000; + } + + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return getVersion() >= 1000 ? RowLockStrategy.COLUMN : RowLockStrategy.TABLE; + } + + @Override + public String getForUpdateString() { + return getVersion() < 1000 ? "" : " for update"; + } + + @Override + public String getForUpdateString(String aliases) { + return getVersion() < 1000 + ? "" + : getForUpdateString() + " of " + aliases; + } + + @Override + public String appendLockHint(LockOptions mode, String tableName) { + return getVersion() < 1000 ? super.appendLockHint( mode, tableName ) : tableName; + } + + @Override + @SuppressWarnings("deprecation") + public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { + return getVersion() < 1000 + ? super.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames ) + : sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString(); + } + @Override public LimitHandler getLimitHandler() { //TODO: support 'TOP ? START AT ?' diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereSqlAstTranslator.java index 6f9b14a959..002024d8cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseAnywhereSqlAstTranslator.java @@ -8,6 +8,7 @@ package org.hibernate.dialect; import java.util.List; +import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; @@ -18,6 +19,7 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; @@ -34,6 +36,26 @@ public class SybaseAnywhereSqlAstTranslator extends Abs super( sessionFactory, statement ); } + @Override + protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) { + super.renderTableReference( tableReference, lockMode ); + if ( getDialect().getVersion() < 1000 ) { + if ( LockMode.READ.lessThan( lockMode ) ) { + appendSql( " holdlock" ); + } + return true; + } + return false; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + if ( getDialect().getVersion() < 1000 ) { + return; + } + super.renderForUpdateClause( querySpec, forUpdateClause ); + } + @Override protected boolean needsRowsToSkip() { return getDialect().getVersion() < 900; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java index f8e5b255b9..29f208a1fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java @@ -38,7 +38,7 @@ public class SybaseDialect extends AbstractTransactSQLDialect { private static final int PARAM_LIST_SIZE_LIMIT = 250000; public SybaseDialect(){ - this(1100); + this( 1100 ); } public SybaseDialect(DialectResolutionInfo info){ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java index b7df4ee23f..3897fc4ea5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseSqlAstTranslator.java @@ -8,6 +8,7 @@ package org.hibernate.dialect; import java.util.List; +import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; @@ -18,7 +19,9 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.select.QueryPart; +import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.exec.spi.JdbcOperation; /** @@ -32,6 +35,20 @@ public class SybaseSqlAstTranslator extends AbstractSql super( sessionFactory, statement ); } + @Override + protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) { + super.renderTableReference( tableReference, lockMode ); + if ( LockMode.READ.lessThan( lockMode ) ) { + appendSql( " holdlock" ); + } + return true; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Sybase does not support the FOR UPDATE clause + } + @Override protected void renderSearchClause(CteStatement cte) { // Sybase does not support this, but it's just a hint anyway 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 2a812eb37d..467c670066 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TeradataDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TeradataDialect.java @@ -482,6 +482,16 @@ public class TeradataDialect extends Dialect { return false; } + @Override + public boolean supportsNoWait() { + return true; + } + + @Override + public boolean supportsWait() { + return false; + } + @Override public boolean useFollowOnLocking(String sql, QueryOptions queryOptions) { return getVersion() >= 14; @@ -581,7 +591,6 @@ public class TeradataDialect extends Dialect { @Override public LimitHandler getLimitHandler() { - //TODO: is this right?! return TopLimitHandler.INSTANCE; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TeradataSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/TeradataSqlAstTranslator.java index 2e0649a981..a3e535126d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TeradataSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TeradataSqlAstTranslator.java @@ -34,6 +34,46 @@ public class TeradataSqlAstTranslator extends AbstractS super( sessionFactory, statement ); } + @Override + public void visitQuerySpec(QuerySpec querySpec) { + if ( querySpec.isRoot() && getDialect().getVersion() >= 14 ) { + final ForUpdateClause forUpdateClause = new ForUpdateClause(); + forUpdateClause.merge( getLockOptions() ); + super.renderForUpdateClause( querySpec, forUpdateClause ); + } + else { + super.visitQuerySpec( querySpec ); + } + } + + @Override + protected String getForUpdate() { + return "locking row for write "; + } + + @Override + protected String getForShare() { + return "locking row for read "; + } + + @Override + protected String getNoWait() { + return "nowait "; + } + + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + return LockStrategy.NONE; + } + + @Override + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + // Teradata does not support the FOR UPDATE clause but has a proprietary LOCKING clause + } + @Override protected boolean needsRowsToSkip() { return true; 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 f932ba0d9a..c1a0c2f77b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenDialect.java @@ -7,6 +7,7 @@ package org.hibernate.dialect; import org.hibernate.LockMode; +import org.hibernate.LockOptions; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.lock.*; @@ -215,11 +216,59 @@ public class TimesTenDialect extends Dialect { return true; } + @Override + public boolean forUpdateOfColumns() { + return true; + } + + @Override + public RowLockStrategy getWriteRowLockStrategy() { + return RowLockStrategy.COLUMN; + } + + @Override + public String getForUpdateString(String aliases) { + return " for update of " + aliases; + } + @Override public String getForUpdateNowaitString() { return " for update nowait"; } + @Override + public String getWriteLockString(int timeout) { + return withTimeout( getForUpdateString(), timeout ); + } + + @Override + public String getWriteLockString(String aliases, int timeout) { + return withTimeout( getForUpdateString(aliases), timeout ); + } + + @Override + public String getReadLockString(int timeout) { + return getWriteLockString( timeout ); + } + + @Override + public String getReadLockString(String aliases, int timeout) { + return getWriteLockString( aliases, timeout ); + } + + + private String withTimeout(String lockString, int timeout) { + switch (timeout) { + case LockOptions.NO_WAIT: + return supportsNoWait() ? lockString + " nowait" : lockString; + case LockOptions.SKIP_LOCKED: + case LockOptions.WAIT_FOREVER: + return lockString; + default: + return supportsWait() ? lockString + " wait " + Math.round( timeout / 1e3f ) : lockString; + } + } + @Override public boolean supportsColumnCheck() { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenSqlAstTranslator.java index ef5ba975ec..de916a75a8 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TimesTenSqlAstTranslator.java @@ -10,6 +10,8 @@ import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.query.ComparisonOperator; +import org.hibernate.query.IllegalQueryOperationException; +import org.hibernate.query.SemanticException; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; @@ -18,6 +20,7 @@ import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SelectClause; @@ -34,6 +37,23 @@ public class TimesTenSqlAstTranslator extends AbstractS super( sessionFactory, statement ); } + @Override + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + // TimesTen supports locks with aggregates but not with set operators + // See https://docs.oracle.com/cd/E11882_01/timesten.112/e21642/state.htm#TTSQL329 + LockStrategy strategy = LockStrategy.CLAUSE; + if ( getQueryPartStack().findCurrentFirst( part -> part instanceof QueryGroup ? part : null ) != null ) { + if ( Boolean.FALSE.equals( followOnLocking ) ) { + throw new IllegalQueryOperationException( "Locking with set operators is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + return strategy; + } + @Override protected void renderSearchClause(CteStatement cte) { // TimesTen does not support this, but it's just a hint anyway diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java index 1891b0fa02..c3df974ed4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java @@ -167,7 +167,7 @@ public class CommonFunctionFactory { } public static void median(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "median" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "median" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); @@ -189,7 +189,7 @@ public class CommonFunctionFactory { * - On Oracle, DB2, MySQL it means stdev_pop() */ public static void stddev(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "stddev" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "stddev" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); @@ -202,47 +202,47 @@ public class CommonFunctionFactory { * - On Oracle, DB2, MySQL it means var_pop() */ public static void variance(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "variance" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "variance" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); } public static void stddevPopSamp(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "stddev_pop" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "stddev_pop" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "stddev_samp" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "stddev_samp" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); } public static void varPopSamp(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "var_pop" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "var_pop" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "var_samp" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "var_samp" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); } public static void covarPopSamp(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "covar_pop" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "covar_pop" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 2 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "covar_samp" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "covar_samp" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 2 ) .register(); } public static void corr(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "corr" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "corr" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 2 ) .register(); @@ -254,7 +254,7 @@ public class CommonFunctionFactory { "regr_slope", "regr_sxx", "regr_sxy", "regr_syy" ) .forEach( fnName -> - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( fnName ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( fnName ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 2 ) .register() @@ -265,11 +265,11 @@ public class CommonFunctionFactory { * DB2 */ public static void stdevVarianceSamp(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "stddev_samp" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "stddev_samp" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "variance_samp" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "variance_samp" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); @@ -279,11 +279,11 @@ public class CommonFunctionFactory { * SQL Server-style */ public static void stddevPopSamp_stdevp(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "stdev" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "stdev" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "stdevp" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "stdevp" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); @@ -295,11 +295,11 @@ public class CommonFunctionFactory { * SQL Server-style */ public static void varPopSamp_varp(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "var" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "var" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "varp" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "varp" ) .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount( 1 ) .register(); @@ -638,11 +638,11 @@ public class CommonFunctionFactory { * These are aggregate functions taking one argument! */ public static void bitAndOr(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "bit_and" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "bit_and" ) .setExactArgumentCount( 1 ) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "bit_or" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "bit_or" ) .setExactArgumentCount( 1 ) .register(); @@ -656,13 +656,13 @@ public class CommonFunctionFactory { * These are aggregate functions taking one argument! */ public static void everyAny(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "every" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "every" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setArgumentListSignature("(predicate)") .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "any" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "any" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setArgumentListSignature("(predicate)") @@ -675,14 +675,14 @@ public class CommonFunctionFactory { * and predicates! */ public static void everyAny_boolAndOr(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "bool_and" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "bool_and" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setArgumentListSignature("(predicate)") .register(); queryEngine.getSqmFunctionRegistry().registerAlternateKey( "every", "bool_and" ); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "bool_or" ) + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "bool_or" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setArgumentListSignature("(predicate)") @@ -696,14 +696,14 @@ public class CommonFunctionFactory { * aggregation functions using sum() and case. */ public static void everyAny_sumCase(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "every", + queryEngine.getSqmFunctionRegistry().patternAggregateDescriptorBuilder( "every", "(sum(case when ?1 then 0 else 1 end)=0)" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setArgumentListSignature("(predicate)") .register(); - queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "any", + queryEngine.getSqmFunctionRegistry().patternAggregateDescriptorBuilder( "any", "(sum(case when ?1 then 1 else 0 end)>0)" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) @@ -716,14 +716,14 @@ public class CommonFunctionFactory { * for SQL Server. */ public static void everyAny_sumIif(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "every", + queryEngine.getSqmFunctionRegistry().patternAggregateDescriptorBuilder( "every", "min(iif(?1,1,0))" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setArgumentListSignature("(predicate)") .register(); - queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "any", + queryEngine.getSqmFunctionRegistry().patternAggregateDescriptorBuilder( "any", "max(iif(?1,1,0))" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) @@ -736,14 +736,14 @@ public class CommonFunctionFactory { * for Oracle. */ public static void everyAny_sumCaseCase(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "every", + queryEngine.getSqmFunctionRegistry().patternAggregateDescriptorBuilder( "every", "min(case when ?1 then 1 else 0 end)" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) .setArgumentListSignature("(predicate)") .register(); - queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "any", + queryEngine.getSqmFunctionRegistry().patternAggregateDescriptorBuilder( "any", "max(case when ?1 then 1 else 0 end)" ) .setExactArgumentCount( 1 ) .setInvariantType( StandardBasicTypes.BOOLEAN ) @@ -1482,11 +1482,11 @@ public class CommonFunctionFactory { } public static void aggregates(QueryEngine queryEngine) { - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("max") + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder("max") .setExactArgumentCount(1) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("min") + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder("min") .setExactArgumentCount(1) .register(); @@ -1500,7 +1500,7 @@ public class CommonFunctionFactory { // Double when applied to state fields of floating point types; // BigInteger when applied to state fields of type BigInteger; // and BigDecimal when applied to state fields of type BigDecimal. - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("sum") + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder("sum") .setReturnTypeResolver( new FunctionReturnTypeResolver() { @Override public AllowableFunctionReturnType resolveFunctionReturnType(AllowableFunctionReturnType impliedType, List> arguments, TypeConfiguration typeConfiguration) { @@ -1574,12 +1574,12 @@ public class CommonFunctionFactory { .setExactArgumentCount(1) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("avg") + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder("avg") .setInvariantType( StandardBasicTypes.DOUBLE ) .setExactArgumentCount(1) .register(); - queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder("count") + queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder("count") .setInvariantType( StandardBasicTypes.LONG ) .setExactArgumentCount(1) .setArgumentListSignature("([distinct ]{arg|*})") diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index eddf2ca4f1..bdd52a3489 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -801,6 +801,9 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont HqlQueryImplementor query = namedHqlDescriptor.toQuery( this, resultType ); query.setComment( "dynamic HQL query" ); applyQuerySettingsAndHints( query ); + if ( namedHqlDescriptor.getLockOptions() != null ) { + query.setLockOptions( namedHqlDescriptor.getLockOptions() ); + } return query; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java index 4ca9ff5929..426dc5905f 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java @@ -107,7 +107,6 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { navigablePath, fetchable.getMappedFetchOptions().getTiming(), true, - options.getLockOptions() != null ? options.getLockOptions().getLockMode() : LockMode.READ, null, creationState ); @@ -158,7 +157,6 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { else { lockOptions = LockOptions.READ; } - final LockMode lockMode = lockOptions.getLockMode(); final NavigablePath entityPath = new NavigablePath( entityDescriptor.getRootPathName() ); final QuerySpec rootQuerySpec = new QuerySpec( true ); @@ -176,7 +174,6 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( entityPath, null, - lockMode, () -> rootQuerySpec::applyPredicate, sqlAstCreationState, sessionFactory @@ -232,7 +229,7 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { @Override public Callback getCallback() { - return afterLoadAction -> {}; + throw new UnsupportedOperationException( "Follow-on locking not supported yet" ); } }, row -> (L) row[0], @@ -327,7 +324,6 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { navigablePath, fetchable.getMappedFetchOptions().getTiming(), true, - LockMode.READ, null, creationState ); @@ -410,8 +406,7 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { @Override public Callback getCallback() { - return afterLoadAction -> { - }; + throw new UnsupportedOperationException( "Follow-on locking not supported yet" ); } }, (row) -> { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java index 2d6cebbe00..06247fa94a 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; @@ -91,7 +90,6 @@ class DatabaseSnapshotExecutor { final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( rootPath, null, - LockMode.NONE, () -> rootQuerySpec::applyPredicate, state, sessionFactory diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java index 239a41190f..9f45d37ed5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java @@ -380,7 +380,6 @@ public class LoaderSelectBuilder { final TableGroup rootTableGroup = loadable.createRootTableGroup( rootNavigablePath, null, - lockOptions.getLockMode(), () -> rootQuerySpec::applyPredicate, sqlAstCreationState, creationContext @@ -705,7 +704,6 @@ public class LoaderSelectBuilder { } } - final LockMode lockMode = LockMode.READ; FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming(); boolean joined = fetchable.getMappedFetchOptions().getStyle() == FetchStyle.JOIN; @@ -779,7 +777,6 @@ public class LoaderSelectBuilder { fetchablePath, fetchTiming, joined, - lockMode, null, creationState ); @@ -865,7 +862,6 @@ public class LoaderSelectBuilder { final TableGroup rootTableGroup = loadable.createRootTableGroup( rootNavigablePath, null, - lockOptions.getLockMode(), () -> rootQuerySpec::applyPredicate, sqlAstCreationState, creationContext diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java index 41073c3a8e..89ca0d223b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java @@ -108,8 +108,8 @@ public class LoaderSqlAstCreationState } @Override - public LockMode determineLockMode(String identificationVariable) { - return lockOptions.getEffectiveLockMode( identificationVariable ); + public void registerLockMode(String identificationVariable, LockMode explicitLockMode) { + throw new UnsupportedOperationException( "Registering lock modes should only be done for result set mappings!" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SimpleNaturalIdLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SimpleNaturalIdLoader.java index dbce7617aa..91b9dec308 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SimpleNaturalIdLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SimpleNaturalIdLoader.java @@ -177,8 +177,7 @@ public class SimpleNaturalIdLoader extends AbstractNaturalIdLoader { @Override public Callback getCallback() { - return afterLoadAction -> { - }; + throw new UnsupportedOperationException( "Follow-on locking not supported yet" ); } }, row -> row[0], diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderStandardImpl.java index 802cdec794..a604bea7dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdEntityLoaderStandardImpl.java @@ -84,7 +84,16 @@ public class SingleIdEntityLoaderStandardImpl extends SingleIdEntityLoaderSup session.getFactory() ); - return loadPlan.load( key, lockOptions, entityInstance, readOnly, session ); + // It seems lock options were ignored in Hibernate 5.x + final LockOptions lockOptionsToUse; + if ( session.getLoadQueryInfluencers().getEnabledCascadingFetchProfile() != null + && LockMode.UPGRADE.greaterThan( lockOptions.getLockMode() ) ) { + lockOptionsToUse = lockOptions.makeCopy().setLockMode( LockMode.NONE ); + } + else { + lockOptionsToUse = lockOptions; + } + return loadPlan.load( key, lockOptionsToUse, entityInstance, readOnly, session ); } @Internal diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java index 6ab071a37b..a15a49a006 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java @@ -15,6 +15,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.ast.spi.Loadable; import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.query.internal.SimpleQueryOptions; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryOptionsAdapter; import org.hibernate.query.spi.QueryParameterBindings; @@ -22,6 +23,7 @@ import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.select.SelectStatement; +import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl; import org.hibernate.sql.exec.spi.Callback; @@ -111,10 +113,12 @@ public class SingleIdLoadPlan implements SingleEntityLoadPlan { ); } assert offset == jdbcParameters.size(); + final QueryOptions queryOptions = new SimpleQueryOptions( lockOptions, readOnly ); + final Callback callback = new CallbackImpl(); final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory, sqlAst ) - .translate( jdbcParameterBindings, QueryOptions.NONE ); + .translate( jdbcParameterBindings, queryOptions ); - final List list = JdbcSelectExecutorStandardImpl.INSTANCE.list( + final List list = JdbcSelectExecutorStandardImpl.INSTANCE.list( jdbcSelect, jdbcParameterBindings, new ExecutionContext() { @@ -135,12 +139,7 @@ public class SingleIdLoadPlan implements SingleEntityLoadPlan { @Override public QueryOptions getQueryOptions() { - return new QueryOptionsAdapter() { - @Override - public Boolean isReadOnly() { - return readOnly; - } - }; + return queryOptions; } @Override @@ -150,8 +149,7 @@ public class SingleIdLoadPlan implements SingleEntityLoadPlan { @Override public Callback getCallback() { - return afterLoadAction -> { - }; + return callback; } }, RowTransformerPassThruImpl.instance(), @@ -162,7 +160,6 @@ public class SingleIdLoadPlan implements SingleEntityLoadPlan { return null; } - //noinspection unchecked - return (T) list.get( 0 ); + return list.get( 0 ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java index c93c08202e..d27461c491 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java @@ -123,8 +123,7 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEn @Override public Callback getCallback() { - return afterLoadAction -> { - }; + throw new UnsupportedOperationException( "Follow-on locking not supported yet" ); } }, row -> row[0], @@ -201,8 +200,7 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEn @Override public Callback getCallback() { - return afterLoadAction -> { - }; + throw new UnsupportedOperationException( "Follow-on locking not supported yet" ); } }, row -> row[0], diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java index 6a98dca770..5dbe465b99 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java @@ -9,7 +9,6 @@ package org.hibernate.loader.ast.spi; import java.util.function.Consumer; import java.util.function.Supplier; -import org.hibernate.LockMode; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.metamodel.mapping.ModelPart; @@ -40,7 +39,6 @@ public interface Loadable extends ModelPart, RootTableGroupProducer { default TableGroup createRootTableGroup( NavigablePath navigablePath, String explicitSourceAlias, - LockMode lockMode, Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState, SqlAstCreationContext creationContext) { throw new NotYetImplementedFor6Exception( getClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java index 164f71227b..f0775bc1f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java @@ -9,7 +9,6 @@ package org.hibernate.metamodel.internal; import java.util.List; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -151,7 +150,6 @@ public abstract class AbstractCompositeIdentifierMapping NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return new EmbeddableFetchImpl( @@ -172,7 +170,6 @@ public abstract class AbstractCompositeIdentifierMapping String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { 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 faa35a1454..f74bd207cf 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 @@ -12,7 +12,6 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.Supplier; -import org.hibernate.LockMode; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.loader.ast.spi.Loadable; @@ -273,14 +272,12 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel default TableGroup createRootTableGroup( NavigablePath navigablePath, String explicitSourceAlias, - LockMode lockMode, Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState, SqlAstCreationContext creationContext) { return getEntityPersister().createRootTableGroup( navigablePath, explicitSourceAlias, - lockMode, additionalPredicateCollectorAccess, creationState, creationContext diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityDiscriminatorMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityDiscriminatorMapping.java index 322ed48ac6..8e5bc2e9c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityDiscriminatorMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityDiscriminatorMapping.java @@ -6,12 +6,10 @@ */ package org.hibernate.metamodel.mapping.internal; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.persister.entity.DiscriminatorType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.NavigablePath; @@ -138,7 +136,6 @@ public abstract class AbstractEntityDiscriminatorMapping implements EntityDiscri NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java index 3183ea6239..a47684dd6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal; import java.io.Serializable; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -181,7 +180,6 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions, NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java index 619eddeaa7..9c5ded47d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java @@ -9,7 +9,6 @@ package org.hibernate.metamodel.mapping.internal; import java.util.Collections; import java.util.List; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -138,7 +137,6 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions { NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { final FromClauseAccess fromClauseAccess = creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java index 0b07b9f494..a030a899c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal; import java.util.function.BiConsumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -270,7 +269,6 @@ public class BasicAttributeMapping NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java index 62821b1261..df1ab71fe6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal; import java.util.Locale; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -292,7 +291,6 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return new BasicFetch<>( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java index fda4d9d922..140b758b74 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java @@ -10,7 +10,6 @@ import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -208,7 +207,6 @@ public class BasicValuedCollectionPart NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { ResultsLogger.LOGGER.debugf( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java index 7254d710e7..b05a60198a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java @@ -6,7 +6,6 @@ */ package org.hibernate.metamodel.mapping.internal; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -145,7 +144,6 @@ public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierD NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { // get the collection TableGroup diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java index 40c6e1d262..e016361306 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java @@ -10,7 +10,6 @@ import java.io.Serializable; import java.util.function.BiConsumer; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.SharedSessionContract; import org.hibernate.engine.FetchStyle; @@ -120,7 +119,6 @@ public class DiscriminatedAssociationAttributeMapping NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return discriminatorMapping.generateFetch( @@ -128,7 +126,6 @@ public class DiscriminatedAssociationAttributeMapping fetchablePath, fetchTiming, selected, - lockMode, resultVariable, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationMapping.java index ec95a724f2..781f414d33 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationMapping.java @@ -315,7 +315,6 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return new AnyValuedFetch( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedCollectionPart.java index 81ed021c37..1963d9d32c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedCollectionPart.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.mapping.Any; @@ -106,7 +105,6 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return discriminatorMapping.generateFetch( @@ -114,7 +112,6 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode fetchablePath, fetchTiming, selected, - lockMode, resultVariable, creationState ); 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 76cf4423db..98975c4783 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 @@ -10,7 +10,6 @@ import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -209,7 +208,6 @@ public class EmbeddedAttributeMapping NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return new EmbeddableFetchImpl( @@ -277,7 +275,6 @@ public class EmbeddedAttributeMapping String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java index 5996f301c4..0d0572e76f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -151,7 +150,6 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return new EmbeddableFetchImpl( @@ -203,7 +201,6 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java index 61ae42db3f..cdfda4a661 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.IntFunction; -import org.hibernate.LockMode; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.mapping.IndexedConsumer; import org.hibernate.metamodel.mapping.AssociationKey; @@ -303,7 +302,6 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { null, SqlAstJoinType.INNER, true, - LockMode.NONE, creationState.getSqlAstCreationState() ); return tableGroupJoin.getJoinedGroup(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java index f00a20534c..d4549adf41 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal; import java.util.function.BiConsumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -144,7 +143,6 @@ public class EntityCollectionPart NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { // find or create the TableGroup associated with this `fetchablePath` @@ -166,7 +164,7 @@ public class EntityCollectionPart } ); - return new EntityFetchJoinedImpl( fetchParent, this, tableGroup, lockMode, selected, fetchablePath, creationState ); + return new EntityFetchJoinedImpl( fetchParent, this, tableGroup, selected, fetchablePath, creationState ); } @Override @@ -353,7 +351,6 @@ public class EntityCollectionPart String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -363,7 +360,6 @@ public class EntityCollectionPart explicitSourceAlias, sqlAstJoinType, fetched, - lockMode, aliasBaseGenerator, sqlExpressionResolver, creationContext diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java index 41f7573bc6..278b844741 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal; import java.util.function.BiConsumer; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -142,7 +141,6 @@ public class EntityVersionMappingImpl implements EntityVersionMapping, FetchOpti NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); 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 2f56e4b16c..df7417d69c 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,7 +12,6 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -import org.hibernate.LockMode; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStyle; @@ -478,7 +477,6 @@ public class PluralAttributeMappingImpl NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); @@ -499,7 +497,6 @@ public class PluralAttributeMappingImpl null, SqlAstJoinType.LEFT, true, - lockMode, creationState.getSqlAstCreationState() ); return tableGroupJoin.getJoinedGroup(); @@ -556,7 +553,6 @@ public class PluralAttributeMappingImpl String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -568,7 +564,6 @@ public class PluralAttributeMappingImpl explicitSourceAlias, sqlAstJoinType, fetched, - lockMode, aliasBaseGenerator, sqlExpressionResolver, creationContext @@ -581,7 +576,6 @@ public class PluralAttributeMappingImpl explicitSourceAlias, sqlAstJoinType, fetched, - lockMode, aliasBaseGenerator, sqlExpressionResolver, creationContext @@ -600,14 +594,13 @@ public class PluralAttributeMappingImpl String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { final TableGroup tableGroup = createOneToManyTableGroup( navigablePath, fetched, - lockMode, + explicitSourceAlias, aliasBaseGenerator, sqlExpressionResolver, creationContext @@ -634,7 +627,7 @@ public class PluralAttributeMappingImpl private TableGroup createOneToManyTableGroup( NavigablePath navigablePath, boolean fetched, - LockMode lockMode, + String sourceAlias, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -661,7 +654,7 @@ public class PluralAttributeMappingImpl navigablePath, this, fetched, - lockMode, + sourceAlias, primaryTableReference, true, sqlAliasBase, @@ -684,14 +677,13 @@ public class PluralAttributeMappingImpl String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { final TableGroup tableGroup = createCollectionTableGroup( navigablePath, fetched, - lockMode, + explicitSourceAlias, aliasBaseGenerator, sqlExpressionResolver, creationContext @@ -718,7 +710,7 @@ public class PluralAttributeMappingImpl private TableGroup createCollectionTableGroup( NavigablePath navigablePath, boolean fetched, - LockMode lockMode, + String sourceAlias, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -847,7 +839,7 @@ public class PluralAttributeMappingImpl navigablePath, this, fetched, - lockMode, + sourceAlias, collectionTableReference, true, sqlAliasBase, @@ -931,7 +923,6 @@ public class PluralAttributeMappingImpl public TableGroup createRootTableGroup( NavigablePath navigablePath, String explicitSourceAlias, - LockMode lockMode, Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState, SqlAstCreationContext creationContext) { @@ -939,7 +930,7 @@ public class PluralAttributeMappingImpl return createOneToManyTableGroup( navigablePath, false, - lockMode, + explicitSourceAlias, creationState.getSqlAliasBaseGenerator(), creationState.getSqlExpressionResolver(), creationContext @@ -949,7 +940,7 @@ public class PluralAttributeMappingImpl return createCollectionTableGroup( navigablePath, false, - lockMode, + explicitSourceAlias, creationState.getSqlAliasBaseGenerator(), creationState.getSqlExpressionResolver(), creationContext 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 bcccc22dab..fd4d34dd93 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 @@ -11,7 +11,6 @@ import java.util.List; import java.util.function.Function; import java.util.function.IntFunction; -import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -491,7 +490,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return null; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java index 1a71ac3392..fae8f1dc77 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java @@ -600,7 +600,6 @@ public class ToOneAttributeMapping NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { @@ -622,7 +621,7 @@ public class ToOneAttributeMapping fetchablePath, true, getJoinType( fetchablePath, parentTableGroup ), - lockMode, + resultVariable, creationState, parentTableGroup ); @@ -632,7 +631,7 @@ public class ToOneAttributeMapping tableGroup = fromClauseAccess.resolveTableGroup( fetchablePath, np -> - createTableGroupJoin( fetchablePath, true, lockMode, creationState, parentTableGroup ) + createTableGroupJoin( fetchablePath, true, resultVariable, creationState, parentTableGroup ) ); } @@ -641,7 +640,6 @@ public class ToOneAttributeMapping fetchParent, this, tableGroup, - lockMode, true, fetchablePath, creationState @@ -733,7 +731,6 @@ public class ToOneAttributeMapping null, tableGroup.isInnerJoinPossible() ? SqlAstJoinType.INNER : SqlAstJoinType.LEFT, true, - null, creationState.getSqlAstCreationState() ); @@ -764,14 +761,14 @@ public class ToOneAttributeMapping private TableGroup createTableGroupJoin( NavigablePath fetchablePath, boolean fetched, - LockMode lockMode, + String sourceAlias, DomainResultCreationState creationState, TableGroup parentTableGroup) { return createTableGroupJoin( fetchablePath, fetched, getDefaultSqlAstJoinType( parentTableGroup ), - lockMode, + sourceAlias, creationState, parentTableGroup ); @@ -793,16 +790,15 @@ public class ToOneAttributeMapping NavigablePath fetchablePath, boolean fetched, SqlAstJoinType sqlAstJoinType, - LockMode lockMode, + String sourceAlias, DomainResultCreationState creationState, TableGroup parentTableGroup) { final TableGroupJoin tableGroupJoin = createTableGroupJoin( fetchablePath, parentTableGroup, - null, + sourceAlias, sqlAstJoinType, fetched, - lockMode, creationState.getSqlAstCreationState() ); @@ -821,7 +817,6 @@ public class ToOneAttributeMapping String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -862,7 +857,7 @@ public class ToOneAttributeMapping ); }, this, - null, + explicitSourceAlias, sqlAliasBase, creationContext.getSessionFactory(), lhs @@ -910,7 +905,7 @@ public class ToOneAttributeMapping public TableGroup createTableGroupJoinInternal( NavigablePath navigablePath, boolean fetched, - LockMode lockMode, + String sourceAlias, final SqlAliasBase sqlAliasBase, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -924,7 +919,7 @@ public class ToOneAttributeMapping navigablePath, this, fetched, - lockMode, + sourceAlias, primaryTableReference, false, sqlAliasBase, 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 b2cd1506c2..6f3c82e398 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 @@ -1362,7 +1362,6 @@ public abstract class AbstractEntityPersister public TableGroup createRootTableGroup( NavigablePath navigablePath, String explicitSourceAlias, - LockMode lockMode, Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState, SqlAstCreationContext creationContext) { @@ -1379,7 +1378,7 @@ public abstract class AbstractEntityPersister return new StandardTableGroup( navigablePath, this, - lockMode, + explicitSourceAlias, primaryTableReference, true, sqlAliasBase, @@ -2245,7 +2244,7 @@ public abstract class AbstractEntityPersister if ( !isVersioned() ) { return this; } - return getVersionType().nullSafeGet( rs, VERSION_COLUMN_ALIAS, session, null ); + return getVersionMapping().getJdbcMapping().getJdbcValueExtractor().extract( rs, 1, session ); } finally { session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( rs, st ); 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 81b6b31df2..0049a3943c 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 @@ -18,7 +18,6 @@ import java.util.function.Consumer; import java.util.function.Supplier; import org.hibernate.HibernateException; -import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.boot.model.relational.Database; import org.hibernate.cache.spi.access.EntityDataAccess; @@ -924,16 +923,15 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { public TableGroup createRootTableGroup( NavigablePath navigablePath, String explicitSourceAlias, - LockMode lockMode, Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState, SqlAstCreationContext creationContext) { final TableGroup tableGroup = super.createRootTableGroup( navigablePath, explicitSourceAlias, - lockMode, additionalPredicateCollectorAccess, - creationState, creationContext + creationState, + creationContext ); if ( needsDiscriminator() ) { 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 148ac3595b..8bc46bfd86 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 @@ -21,7 +21,6 @@ import java.util.function.Supplier; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; -import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.boot.model.relational.Database; import org.hibernate.cache.spi.access.EntityDataAccess; @@ -54,7 +53,6 @@ import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.UnionTableGroup; import org.hibernate.sql.ast.tree.from.UnionTableReference; import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.Type; @@ -245,7 +243,6 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { public TableGroup createRootTableGroup( NavigablePath navigablePath, String explicitSourceAlias, - LockMode lockMode, Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState, SqlAstCreationContext creationContext) { @@ -253,7 +250,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { final TableReference tableReference = resolvePrimaryTableReference(sqlAliasBase); - return new UnionTableGroup( navigablePath, tableReference, this ); + return new UnionTableGroup( navigablePath, tableReference, this, explicitSourceAlias ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java index 7ae94b9206..3e6d844bec 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java @@ -87,7 +87,7 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation } final SelectQueryPlan plan = creator.get(); - queryPlanCache.put( key, plan ); + queryPlanCache.put( key.prepareForStore(), plan ); return plan; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/SimpleQueryOptions.java b/hibernate-core/src/main/java/org/hibernate/query/internal/SimpleQueryOptions.java new file mode 100644 index 0000000000..9af32abf36 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/SimpleQueryOptions.java @@ -0,0 +1,34 @@ +/* + * 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.internal; + +import org.hibernate.LockOptions; +import org.hibernate.query.spi.QueryOptionsAdapter; + +/** + * @author Christian Beikov + */ +public class SimpleQueryOptions extends QueryOptionsAdapter { + + private final LockOptions lockOptions; + private final Boolean readOnlyEnabled; + + public SimpleQueryOptions(LockOptions lockOptions, Boolean readOnlyEnabled) { + this.lockOptions = lockOptions; + this.readOnlyEnabled = readOnlyEnabled; + } + + @Override + public LockOptions getLockOptions() { + return lockOptions; + } + + @Override + public Boolean isReadOnly() { + return readOnlyEnabled; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/Builders.java b/hibernate-core/src/main/java/org/hibernate/query/results/Builders.java index 173c3738c4..708b247841 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/Builders.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/Builders.java @@ -43,11 +43,9 @@ import org.hibernate.query.results.implicit.ImplicitModelPartResultBuilderEntity import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.results.graph.DomainResultCreationState; -import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.collection.internal.EntityCollectionPartTableGroup; import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable; -import org.hibernate.sql.results.graph.entity.EntityValuedFetchable; import org.hibernate.type.BasicType; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; @@ -292,7 +290,6 @@ public class Builders { fetchablePath, FetchTiming.IMMEDIATE, true, - LockMode.NONE, null, domainResultCreationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java index 2e972a90f4..4fbd27c9cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java @@ -75,6 +75,7 @@ public class DomainResultCreationStateImpl private final Stack> fetchBuilderResolverStack = new StandardStack<>( fetchableName -> null ); private final Stack relativePathStack = new StandardStack<>(); + private Map registeredLockModes; private boolean processingKeyFetches = false; private boolean resolvingCircularFetch; private ForeignKeyDescriptor.Nature currentlyResolvingForeignKeySide; @@ -209,8 +210,15 @@ public class DomainResultCreationStateImpl } @Override - public LockMode determineLockMode(String identificationVariable) { - return LockMode.READ; + public void registerLockMode(String identificationVariable, LockMode explicitLockMode) { + if (registeredLockModes == null ) { + registeredLockModes = new HashMap<>(); + } + registeredLockModes.put( identificationVariable, explicitLockMode ); + } + + public Map getRegisteredLockModes() { + return registeredLockModes; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitAttributeFetchBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitAttributeFetchBuilder.java index deaab5a480..43fa5d2ea9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitAttributeFetchBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ImplicitAttributeFetchBuilder.java @@ -8,7 +8,6 @@ package org.hibernate.query.results; import java.util.function.BiFunction; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.query.NavigablePath; @@ -47,7 +46,6 @@ public class ImplicitAttributeFetchBuilder implements FetchBuilder, ImplicitFetc fetchPath, FetchTiming.IMMEDIATE, true, - LockMode.READ, null, domainResultCreationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java index d8c8b302a0..d279aaa1bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java @@ -7,9 +7,18 @@ package org.hibernate.query.results; import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import org.hibernate.LockMode; +import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlSelection; +import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.DomainResultAssembler; +import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.jdbc.internal.StandardJdbcValuesMapping; /** @@ -20,16 +29,54 @@ import org.hibernate.sql.results.jdbc.internal.StandardJdbcValuesMapping; public class JdbcValuesMappingImpl extends StandardJdbcValuesMapping { private final int rowSize; + private final Map registeredLockModes; public JdbcValuesMappingImpl( List sqlSelections, - List> domainResults, int rowSize) { + List> domainResults, + int rowSize, + Map registeredLockModes) { super( sqlSelections, domainResults ); this.rowSize = rowSize; + this.registeredLockModes = registeredLockModes; } @Override public int getRowSize() { return rowSize; } + + @Override + public List> resolveAssemblers(AssemblerCreationState creationState) { + final AssemblerCreationState finalCreationState; + if ( registeredLockModes == null ) { + finalCreationState = creationState; + } + else { + finalCreationState = new AssemblerCreationState() { + @Override + public LockMode determineEffectiveLockMode(String identificationVariable) { + final LockMode lockMode = registeredLockModes.get( identificationVariable ); + if ( lockMode == null ) { + return creationState.determineEffectiveLockMode( identificationVariable ); + } + return lockMode; + } + + @Override + public Initializer resolveInitializer( + NavigablePath navigablePath, + ModelPart fetchedModelPart, + Supplier producer) { + return creationState.resolveInitializer( navigablePath, fetchedModelPart, producer ); + } + + @Override + public SqlAstCreationContext getSqlAstCreationContext() { + return creationState.getSqlAstCreationContext(); + } + }; + } + return super.resolveAssemblers( finalCreationState ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java index b7111a3df7..6289c69e6f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java @@ -17,6 +17,7 @@ import java.util.function.Consumer; import org.hibernate.Incubating; import org.hibernate.Internal; +import org.hibernate.LockMode; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.StringHelper; @@ -182,8 +183,8 @@ public class ResultSetMappingImpl implements ResultSetMapping { domainResults.add( domainResult ); } - - return new JdbcValuesMappingImpl( sqlSelections, domainResults, rowSize ); + final Map registeredLockModes = creationState.getRegisteredLockModes(); + return new JdbcValuesMappingImpl( sqlSelections, domainResults, rowSize, registeredLockModes ); } private DomainResult makeImplicitDomainResult( diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java index 2e063cc58d..a923569b34 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java @@ -11,8 +11,6 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import org.hibernate.LockMode; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -33,7 +31,7 @@ public class TableGroupImpl implements TableGroup { private List tableGroupJoins; private final ModelPartContainer container; - private final LockMode lockMode; + private final String sourceAlias; public TableGroupImpl( @@ -41,12 +39,12 @@ public class TableGroupImpl implements TableGroup { String alias, TableReference primaryTableReference, ModelPartContainer container, - LockMode lockMode) { + String sourceAlias) { this.navigablePath = navigablePath; this.alias = alias; this.primaryTableReference = primaryTableReference; this.container = container; - this.lockMode = lockMode; + this.sourceAlias = sourceAlias; } @Override @@ -70,8 +68,8 @@ public class TableGroupImpl implements TableGroup { } @Override - public LockMode getLockMode() { - return lockMode; + public String getSourceAlias() { + return sourceAlias; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java index df99fc2afc..a9a5848769 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java @@ -8,7 +8,6 @@ package org.hibernate.query.results.complete; import java.util.function.BiFunction; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.query.NavigablePath; @@ -109,7 +108,6 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Mode fetchPath, FetchTiming.IMMEDIATE, true, - LockMode.READ, selectedAlias, domainResultCreationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityJpa.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityJpa.java index a3ca18ffde..f09d4301d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityJpa.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityJpa.java @@ -81,7 +81,6 @@ public class CompleteResultBuilderEntityJpa implements CompleteResultBuilderEnti np -> entityDescriptor.createRootTableGroup( navigablePath, null, - lockMode, () -> predicate -> {}, impl.getSqlAstCreationState(), impl.getSqlAstCreationState().getCreationContext() diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityStandard.java index 73fcc4d5ff..489a01170f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderEntityStandard.java @@ -73,7 +73,6 @@ public class CompleteResultBuilderEntityStandard implements CompleteResultBuilde np -> entityDescriptor.createRootTableGroup( navigablePath, null, - lockMode, () -> predicate -> {}, impl, impl.getCreationContext() diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java index 69b3ba1333..d2692af49f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java @@ -109,7 +109,6 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue tableAlias, SqlAstJoinType.INNER, true, - LockMode.NONE, s -> sqlAliasBase, creationState.getSqlExpressionResolver(), creationState.getCreationContext() diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java index f5a930c8e2..6709c94b55 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.BiFunction; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; @@ -93,7 +92,6 @@ public class DynamicFetchBuilderStandard fetchPath, FetchTiming.IMMEDIATE, true, - LockMode.NONE, null, creationStateImpl ); @@ -109,7 +107,6 @@ public class DynamicFetchBuilderStandard fetchPath, FetchTiming.DELAYED, false, - LockMode.NONE, null, creationStateImpl ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityCalculated.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityCalculated.java index c2d98eba0d..0671d99e9e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityCalculated.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityCalculated.java @@ -114,10 +114,13 @@ public class DynamicResultBuilderEntityCalculated implements DynamicResultBuilde tableAlias, tableReference, entityMapping, - explicitLockMode + tableAlias ); creationStateImpl.getFromClauseAccess().registerTableGroup( navigablePath, tableGroup ); + if ( explicitLockMode != null ) { + domainResultCreationState.getSqlAstCreationState().registerLockMode( tableAlias, explicitLockMode ); + } return (EntityResult) entityMapping.createDomainResult( navigablePath, diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java index 12a1dc5045..fc7e36d19e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicResultBuilderEntityStandard.java @@ -134,7 +134,6 @@ public class DynamicResultBuilderEntityStandard navigablePath, FetchTiming.IMMEDIATE, true, - lockMode, null, domainResultCreationState ), @@ -157,7 +156,11 @@ public class DynamicResultBuilderEntityStandard creationState.getSqlExpressionResolver(), creationState.getCreationContext() ); - return new TableGroupImpl( navigablePath, tableAlias, tableReference, entityMapping, lockMode ); + + if ( lockMode != null ) { + domainResultCreationState.getSqlAstCreationState().registerLockMode( tableAlias, lockMode ); + } + return new TableGroupImpl( navigablePath, tableAlias, tableReference, entityMapping, tableAlias ); } ); final TableReference tableReference = tableGroup.getPrimaryTableReference(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java index 518f3c4d75..829eed989d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java @@ -11,7 +11,6 @@ import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.query.NavigablePath; import org.hibernate.query.results.Builders; @@ -89,7 +88,6 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder { null, SqlAstJoinType.INNER, true, - LockMode.READ, creationStateImpl ); return tableGroupJoin.getJoinedGroup(); @@ -101,7 +99,6 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder { fetchPath, FetchTiming.IMMEDIATE, true, - LockMode.READ, null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEntity.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEntity.java index fc054cf7e4..ad685a5913 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEntity.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEntity.java @@ -12,7 +12,6 @@ import java.util.Map; import java.util.function.BiFunction; import java.util.function.Function; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; @@ -23,9 +22,6 @@ import org.hibernate.query.results.Builders; import org.hibernate.query.results.DomainResultCreationStateImpl; import org.hibernate.query.results.FetchBuilder; import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; -import org.hibernate.sql.ast.SqlAstJoinType; -import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParent; @@ -107,7 +103,6 @@ public class ImplicitFetchBuilderEntity implements ImplicitFetchBuilder { fetchPath, FetchTiming.DELAYED, false, - LockMode.READ, null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderPlural.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderPlural.java index a8ab3bacf2..03b1f103f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderPlural.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderPlural.java @@ -8,7 +8,6 @@ package org.hibernate.query.results.implicit; import java.util.function.BiFunction; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.query.NavigablePath; @@ -50,7 +49,6 @@ public class ImplicitFetchBuilderPlural implements ImplicitFetchBuilder { fetchPath, FetchTiming.DELAYED, false, - LockMode.READ, null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java index f2b611aec7..ae7e60984b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java @@ -8,7 +8,6 @@ package org.hibernate.query.results.implicit; import java.util.function.BiFunction; -import org.hibernate.LockMode; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.query.NavigablePath; import org.hibernate.query.results.DomainResultCreationStateImpl; @@ -66,7 +65,6 @@ public class ImplicitModelPartResultBuilderEmbeddable null, SqlAstJoinType.INNER, true, - LockMode.READ, creationStateImpl ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEntity.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEntity.java index 4cb6d7e2a4..e9a4858fe7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEntity.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEntity.java @@ -8,7 +8,6 @@ package org.hibernate.query.results.implicit; import java.util.function.BiFunction; -import org.hibernate.LockMode; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.query.NavigablePath; @@ -60,7 +59,6 @@ public class ImplicitModelPartResultBuilderEntity return modelPart.getEntityMappingType().createRootTableGroup( navigablePath, null, - LockMode.READ, () -> predicate -> { }, creationStateImpl, diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java index 4d73c29cf1..31c6605f9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java @@ -1184,6 +1184,8 @@ public abstract class AbstractQuery implements QueryImplementor { public T getParameterValue(Parameter param) { QueryLogging.QUERY_LOGGER.tracef( "#getParameterValue(%s)", param ); + getSession().checkOpen( false ); + final QueryParameterImplementor qp = getParameterMetadata().resolve( param ); if ( qp == null ) { throw new IllegalArgumentException( "The parameter [" + param + "] is not part of this Query" ); @@ -1204,6 +1206,8 @@ public abstract class AbstractQuery implements QueryImplementor { @Override public Object getParameterValue(String name) { + getSession().checkOpen( false ); + final QueryParameterImplementor parameter = getParameterMetadata().getQueryParameter( name ); if ( parameter == null ) { throw new IllegalArgumentException( "Could not resolve parameter by name - " + name ); @@ -1369,7 +1373,7 @@ public abstract class AbstractQuery implements QueryImplementor { throw new IllegalArgumentException( e ); } catch (HibernateException he) { - throw getSession().getExceptionConverter().convert( he ); + throw getSession().getExceptionConverter().convert( he, getLockOptions() ); } finally { afterQuery(); @@ -1394,7 +1398,7 @@ public abstract class AbstractQuery implements QueryImplementor { } catch ( HibernateException e ) { if ( getSession().getFactory().getSessionFactoryOptions().isJpaBootstrap() ) { - throw getSession().getExceptionConverter().convert( e ); + throw getSession().getExceptionConverter().convert( e, getLockOptions() ); } else { throw e; @@ -1459,12 +1463,7 @@ public abstract class AbstractQuery implements QueryImplementor { throw new IllegalArgumentException( e ); } catch ( HibernateException e) { - if ( getSession().getFactory().getSessionFactoryOptions().isJpaBootstrap() ) { - throw getSession().getExceptionConverter().convert( e ); - } - else { - throw e; - } + throw getSession().getExceptionConverter().convert( e ); } finally { afterQuery(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java index b4f8c3b811..8792538fb1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java @@ -21,6 +21,12 @@ import org.hibernate.query.sqm.tree.SqmStatement; @Incubating public interface QueryInterpretationCache { interface Key { + /** + * The possibility for a cache key to do defensive copying in case it has mutable state. + */ + default Key prepareForStore() { + return this; + } } int getNumberOfCachedHqlInterpretations(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java b/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java index 311634ae06..78ea425b0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/SqlOmittingQueryOptions.java @@ -31,7 +31,7 @@ public class SqlOmittingQueryOptions extends DelegatingQueryOptions { } public static ExecutionContext omitSqlQueryOptions(ExecutionContext context, JdbcSelect select) { - return omitSqlQueryOptions( context, !select.usesLimitParameters(), true ); + return omitSqlQueryOptions( context, !select.usesLimitParameters(), false ); } public static ExecutionContext omitSqlQueryOptions(ExecutionContext context, boolean omitLimit, boolean omitLocks) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 20914d330a..6e0e0d6a61 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -51,6 +51,7 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.metamodel.model.domain.BasicDomainType; +import org.hibernate.persister.entity.Loadable; import org.hibernate.query.Limit; import org.hibernate.query.ParameterMetadata; import org.hibernate.query.Query; @@ -86,6 +87,7 @@ import org.hibernate.query.sql.spi.NativeSelectQueryPlan; import org.hibernate.query.sql.spi.NonSelectInterpretationsKey; import org.hibernate.query.sql.spi.ParameterInterpretation; import org.hibernate.query.sql.spi.SelectInterpretationsKey; +import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; @@ -114,6 +116,7 @@ public class NativeQueryImpl private final QueryOptionsImpl queryOptions = new QueryOptionsImpl(); private Set querySpaces; + private Callback callback; private Object collectionKey; private NativeQueryInterpreter nativeQueryInterpreter; @@ -345,7 +348,17 @@ public class NativeQueryImpl @Override public Callback getCallback() { - throw new NotYetImplementedFor6Exception(); + if ( callback == null ) { + callback = new CallbackImpl(); + } + return callback; + } + + @Override + public void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { + if ( callback != null ) { + callback.invokeAfterLoadActions( session, entity, persister ); + } } public SessionFactoryImplementor getSessionFactory() { @@ -438,6 +451,8 @@ public class NativeQueryImpl if ( shouldFlush() ) { getSession().flush(); } + // Reset the callback before every execution + callback = null; } private boolean shouldFlush() { @@ -536,9 +551,12 @@ public class NativeQueryImpl @SuppressWarnings("RedundantIfStatement") private static boolean isCacheable(NativeQueryImpl query) { - if ( hasLimit( query.getQueryOptions().getLimit() ) ) { - return false; - } + // todo (6.0): unless we move the limit rendering from DeferredResultSetAccess to NativeSelectQueryPlanImpl + // we don't need to consider the limit here at all because that is applied on demand. + // It certainly is better for performance to include the limit early, but then we might trash the cache +// if ( hasLimit( query.getQueryOptions().getLimit() ) ) { +// return false; +// } return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java index 5e569f42c1..c21e0844af 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SemanticQueryWalker.java @@ -85,7 +85,6 @@ import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; import org.hibernate.query.sqm.tree.select.SqmJpaCompoundSelection; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQueryGroup; -import org.hibernate.query.sqm.tree.select.SqmQueryPart; import org.hibernate.query.sqm.tree.select.SqmQuerySpec; import org.hibernate.query.sqm.tree.select.SqmSelectClause; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmFunctionDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmFunctionDescriptor.java index b739384def..f0651e65ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmFunctionDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmFunctionDescriptor.java @@ -15,6 +15,7 @@ import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolv import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.SqmVisitableNode; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.type.spi.TypeConfiguration; @@ -120,5 +121,27 @@ public abstract class AbstractSqmFunctionDescriptor implements SqmFunctionDescri AllowableFunctionReturnType impliedResultType, QueryEngine queryEngine, TypeConfiguration typeConfiguration); + + /** + * Return an SQM node or subtree representing an invocation of this aggregate function + * with the given arguments. This method may be overridden in the case of + * function descriptors that wish to customize creation of the node. + * + * @param arguments the arguments of the function invocation + * @param impliedResultType the function return type as inferred from its usage + */ + protected SelfRenderingSqmAggregateFunction generateSqmAggregateFunctionExpression( + List> arguments, + SqmPredicate filter, + AllowableFunctionReturnType impliedResultType, + QueryEngine queryEngine, + TypeConfiguration typeConfiguration) { + return (SelfRenderingSqmAggregateFunction) generateSqmExpression( + arguments, + impliedResultType, + queryEngine, + typeConfiguration + ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java index c7d9069ca2..94aabba095 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/AbstractSqmSelfRenderingFunctionDescriptor.java @@ -11,9 +11,11 @@ import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sqm.produce.function.ArgumentsValidator; import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.type.spi.TypeConfiguration; import java.util.List; @@ -24,8 +26,16 @@ import java.util.List; public abstract class AbstractSqmSelfRenderingFunctionDescriptor extends AbstractSqmFunctionDescriptor { + private final boolean isAggregate; + public AbstractSqmSelfRenderingFunctionDescriptor(String name, ArgumentsValidator argumentsValidator, FunctionReturnTypeResolver returnTypeResolver) { super( name, argumentsValidator, returnTypeResolver ); + this.isAggregate = false; + } + + public AbstractSqmSelfRenderingFunctionDescriptor(String name, boolean isAggregate, ArgumentsValidator argumentsValidator, FunctionReturnTypeResolver returnTypeResolver) { + super( name, argumentsValidator, returnTypeResolver ); + this.isAggregate = isAggregate; } @Override @@ -34,6 +44,9 @@ public abstract class AbstractSqmSelfRenderingFunctionDescriptor AllowableFunctionReturnType impliedResultType, QueryEngine queryEngine, TypeConfiguration typeConfiguration) { + if ( isAggregate ) { + return generateAggregateSqmExpression( arguments, null, impliedResultType, queryEngine, typeConfiguration ); + } return new SelfRenderingSqmFunction<>( this, this::render, @@ -45,6 +58,28 @@ public abstract class AbstractSqmSelfRenderingFunctionDescriptor ); } + @Override + public SelfRenderingSqmFunction generateAggregateSqmExpression( + List> arguments, + SqmPredicate filter, + AllowableFunctionReturnType impliedResultType, + QueryEngine queryEngine, + TypeConfiguration typeConfiguration) { + if ( !isAggregate ) { + throw new UnsupportedOperationException( "The function " + getName() + " is not an aggregate function!" ); + } + return new SelfRenderingSqmAggregateFunction<>( + this, + this::render, + arguments, + filter, + impliedResultType, + getReturnTypeResolver(), + queryEngine.getCriteriaBuilder(), + getName() + ); + } + /** * Must be overridden by subclasses */ @@ -53,4 +88,12 @@ public abstract class AbstractSqmSelfRenderingFunctionDescriptor List sqlAstArguments, SqlAstTranslator walker); + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + Predicate filter, + SqlAstTranslator walker) { + render( sqlAppender, sqlAstArguments, walker ); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/FunctionRenderingSupport.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/FunctionRenderingSupport.java index c28970d088..5e22200789 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/FunctionRenderingSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/FunctionRenderingSupport.java @@ -10,6 +10,7 @@ import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.predicate.Predicate; import java.util.List; @@ -29,4 +30,13 @@ public interface FunctionRenderingSupport { SqlAppender sqlAppender, List sqlAstArguments, SqlAstTranslator walker); + + default void render( + SqlAppender sqlAppender, + List sqlAstArguments, + Predicate filter, + SqlAstTranslator walker) { + // Ignore the filter by default. Subclasses will override this + render( sqlAppender, sqlAstArguments, walker ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java index 5697e343ac..acf1c7cc7d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/NamedSqmFunctionDescriptor.java @@ -12,6 +12,9 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Distinct; +import org.hibernate.sql.ast.tree.expression.Star; +import org.hibernate.sql.ast.tree.predicate.Predicate; import java.util.List; import java.util.Locale; @@ -43,6 +46,7 @@ public class NamedSqmFunctionDescriptor argumentsValidator, returnTypeResolver, functionName, + false, null, SqlAstNodeRenderingMode.DEFAULT ); @@ -54,9 +58,10 @@ public class NamedSqmFunctionDescriptor ArgumentsValidator argumentsValidator, FunctionReturnTypeResolver returnTypeResolver, String name, + boolean isAggregate, String argumentListSignature, SqlAstNodeRenderingMode argumentRenderingMode) { - super( name, argumentsValidator, returnTypeResolver ); + super( name, isAggregate, argumentsValidator, returnTypeResolver ); this.functionName = functionName; this.useParenthesesWhenNoArgs = useParenthesesWhenNoArgs; @@ -75,7 +80,7 @@ public class NamedSqmFunctionDescriptor @Override public String getArgumentListSignature() { - return argumentListSignature==null ? super.getArgumentListSignature() : argumentListSignature; + return argumentListSignature == null ? super.getArgumentListSignature() : argumentListSignature; } @Override @@ -87,8 +92,18 @@ public class NamedSqmFunctionDescriptor public void render( SqlAppender sqlAppender, List sqlAstArguments, - SqlAstTranslator walker) { + SqlAstTranslator translator) { + render( sqlAppender, sqlAstArguments, null, translator ); + } + + @Override + public void render( + SqlAppender sqlAppender, + List sqlAstArguments, + Predicate filter, + SqlAstTranslator translator) { final boolean useParens = useParenthesesWhenNoArgs || !sqlAstArguments.isEmpty(); + final boolean caseWrapper = filter != null && !translator.supportsFilterClause(); sqlAppender.appendSql( functionName ); if ( useParens ) { @@ -96,17 +111,32 @@ public class NamedSqmFunctionDescriptor } boolean firstPass = true; - for ( SqlAstNode sqlAstArgument : sqlAstArguments ) { + for ( SqlAstNode arg : sqlAstArguments ) { if ( !firstPass ) { sqlAppender.appendSql( ", " ); } - walker.render( sqlAstArgument, argumentRenderingMode ); + if ( caseWrapper && !( arg instanceof Distinct ) && !( arg instanceof Star ) ) { + sqlAppender.appendSql( "case when " ); + filter.accept( translator ); + sqlAppender.appendSql( " then " ); + translator.render( arg, argumentRenderingMode ); + sqlAppender.appendSql( " else null end" ); + } + else { + translator.render( arg, argumentRenderingMode ); + } firstPass = false; } if ( useParens ) { sqlAppender.appendSql( ")" ); } + + if ( filter != null && !caseWrapper ) { + sqlAppender.appendSql( " filter (where " ); + filter.accept( translator ); + sqlAppender.appendSql( ')' ); + } } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/PatternBasedSqmFunctionDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/PatternBasedSqmFunctionDescriptor.java index f13d8f0609..161144b3c5 100755 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/PatternBasedSqmFunctionDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/PatternBasedSqmFunctionDescriptor.java @@ -13,6 +13,7 @@ import org.hibernate.query.sqm.produce.function.internal.PatternRenderer; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.predicate.Predicate; import java.util.List; @@ -47,9 +48,11 @@ public class PatternBasedSqmFunctionDescriptor ArgumentsValidator argumentsValidator, FunctionReturnTypeResolver returnTypeResolver, String name, + boolean isAggregate, String argumentListSignature) { super( name, + isAggregate, argumentsValidator != null ? argumentsValidator // If no validator is given, it's still better to @@ -70,11 +73,16 @@ public class PatternBasedSqmFunctionDescriptor SqlAppender sqlAppender, List sqlAstArguments, SqlAstTranslator walker) { - renderer.render( sqlAppender, sqlAstArguments, walker ); + renderer.render( sqlAppender, sqlAstArguments, null, walker ); + } + + @Override + public void render(SqlAppender sqlAppender, List sqlAstArguments, Predicate filter, SqlAstTranslator walker) { + renderer.render( sqlAppender, sqlAstArguments, filter, walker ); } @Override public String getArgumentListSignature() { - return argumentListSignature==null ? super.getArgumentListSignature() : argumentListSignature; + return argumentListSignature == null ? super.getArgumentListSignature() : argumentListSignature; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingAggregateFunctionSqlAstExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingAggregateFunctionSqlAstExpression.java new file mode 100644 index 0000000000..be31e403ef --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingAggregateFunctionSqlAstExpression.java @@ -0,0 +1,54 @@ +/* + * 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.function; + +import java.util.List; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; +import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.AggregateFunctionExpression; +import org.hibernate.sql.ast.tree.predicate.Predicate; + +/** + * Representation of an aggregate function call in the SQL AST for impls that know how to + * render themselves. + * + * @author Christian Beikov + */ +public class SelfRenderingAggregateFunctionSqlAstExpression extends SelfRenderingFunctionSqlAstExpression + implements AggregateFunctionExpression { + + private final Predicate filter; + + public SelfRenderingAggregateFunctionSqlAstExpression( + String functionName, + FunctionRenderingSupport renderer, + List sqlAstArguments, + Predicate filter, + AllowableFunctionReturnType type, + JdbcMappingContainer expressable) { + super( functionName, renderer, sqlAstArguments, type, expressable ); + this.filter = filter; + } + + @Override + public Predicate getFilter() { + return filter; + } + + @Override + public void renderToSql( + SqlAppender sqlAppender, + SqlAstTranslator walker, + SessionFactoryImplementor sessionFactory) { + getRenderer().render( sqlAppender, getArguments(), filter, walker ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java index 0696f9ac23..d75a2fad0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java @@ -77,6 +77,10 @@ public class SelfRenderingFunctionSqlAstExpression return expressable; } + protected FunctionRenderingSupport getRenderer() { + return renderer; + } + @Override public SqlSelection createSqlSelection( int jdbcPosition, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java new file mode 100644 index 0000000000..8ecb401c99 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java @@ -0,0 +1,54 @@ +/* + * 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.function; + +import java.util.List; + +import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; +import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; +import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; +import org.hibernate.sql.ast.tree.predicate.Predicate; + +/** + * @author Christian Beikov + */ +public class SelfRenderingSqmAggregateFunction extends SelfRenderingSqmFunction { + + private final SqmPredicate filter; + + public SelfRenderingSqmAggregateFunction( + SqmFunctionDescriptor descriptor, + FunctionRenderingSupport renderingSupport, + List> arguments, + SqmPredicate filter, + AllowableFunctionReturnType impliedResultType, + FunctionReturnTypeResolver returnTypeResolver, + NodeBuilder nodeBuilder, + String name) { + super( descriptor, renderingSupport, arguments, impliedResultType, returnTypeResolver, nodeBuilder, name ); + this.filter = filter; + } + + @Override + public SelfRenderingFunctionSqlAstExpression convertToSqlAst(SqmToSqlAstConverter walker) { + final AllowableFunctionReturnType resultType = resolveResultType( + walker.getCreationContext().getDomainModel().getTypeConfiguration() + ); + + return new SelfRenderingAggregateFunctionSqlAstExpression( + getFunctionName(), + getRenderingSupport(), + resolveSqlAstArguments( getArguments(), walker ), + filter == null ? null : (Predicate) filter.accept( walker ), + resultType, + getMappingModelExpressable( walker, resultType ) + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionDescriptor.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionDescriptor.java index 15aa773732..fefb382ee6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionDescriptor.java @@ -9,6 +9,7 @@ package org.hibernate.query.sqm.function; import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.sqm.tree.SqmTypedNode; +import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.type.spi.TypeConfiguration; import java.util.List; @@ -45,6 +46,19 @@ public interface SqmFunctionDescriptor { QueryEngine queryEngine, TypeConfiguration typeConfiguration); + /** + * Like {@link #generateSqmExpression(List, AllowableFunctionReturnType, QueryEngine, TypeConfiguration)} + * but also accepts a filter predicate. This method is intended for aggregate functions. + */ + default SelfRenderingSqmFunction generateAggregateSqmExpression( + List> arguments, + SqmPredicate filter, + AllowableFunctionReturnType impliedResultType, + QueryEngine queryEngine, + TypeConfiguration typeConfiguration) { + throw new UnsupportedOperationException( "Not an aggregate function!" ); + } + /** * Convenience for single argument */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionRegistry.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionRegistry.java index 4c97c2de94..192f979b57 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SqmFunctionRegistry.java @@ -112,7 +112,19 @@ public class SqmFunctionRegistry { * @return The builder */ public PatternFunctionDescriptorBuilder patternDescriptorBuilder(String registrationKey, String pattern) { - return new PatternFunctionDescriptorBuilder( this, registrationKey, pattern ); + return new PatternFunctionDescriptorBuilder( this, registrationKey, false, pattern ); + } + + /** + * Get a builder for creating and registering a pattern-based aggregate function descriptor. + * + * @param registrationKey The name under which the descriptor will get registered + * @param pattern The pattern defining the the underlying function call + * + * @return The builder + */ + public PatternFunctionDescriptorBuilder patternAggregateDescriptorBuilder(String registrationKey, String pattern) { + return new PatternFunctionDescriptorBuilder( this, registrationKey, true, pattern ); } /** @@ -150,6 +162,19 @@ public class SqmFunctionRegistry { return namedDescriptorBuilder( name, name ); } + /** + * Get a builder for creating and registering a name-based aggregate function descriptor + * using the passed name as both the registration key and underlying SQL + * function name + * + * @param name The function name (and registration key) + * + * @return The builder + */ + public NamedFunctionDescriptorBuilder namedAggregateDescriptorBuilder(String name) { + return namedAggregateDescriptorBuilder( name, name ); + } + /** * Get a builder for creating and registering a name-based function descriptor. * @@ -159,7 +184,19 @@ public class SqmFunctionRegistry { * @return The builder */ public NamedFunctionDescriptorBuilder namedDescriptorBuilder(String registrationKey, String name) { - return new NamedFunctionDescriptorBuilder( this, registrationKey, name ); + return new NamedFunctionDescriptorBuilder( this, registrationKey, false, name ); + } + + /** + * Get a builder for creating and registering a name-based aggregate function descriptor. + * + * @param registrationKey The name under which the descriptor will get registered + * @param name The underlying SQL function name to use + * + * @return The builder + */ + public NamedFunctionDescriptorBuilder namedAggregateDescriptorBuilder(String registrationKey, String name) { + return new NamedFunctionDescriptorBuilder( this, registrationKey, true, name ); } public NamedFunctionDescriptorBuilder noArgsBuilder(String name) { 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 fa83636bcc..9fe09a0058 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 @@ -63,6 +63,8 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { private final SqmInterpreter, Void> listInterpreter; private final SqmInterpreter, ScrollMode> scrollInterpreter; + private volatile CacheableSqmInterpretation cacheableSqmInterpretation; + @SuppressWarnings("WeakerAccess") public ConcreteSqmSelectQueryPlan( SqmSelectStatement sqm, @@ -195,8 +197,6 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { return withCacheableSqmInterpretation( executionContext, scrollMode, scrollInterpreter ); } - private volatile CacheableSqmInterpretation cacheableSqmInterpretation; - private T withCacheableSqmInterpretation(ExecutionContext executionContext, X context, SqmInterpreter interpreter) { // NOTE : VERY IMPORTANT - intentional double-lock checking // The other option would be to leverage `java.util.concurrent.locks.ReadWriteLock` @@ -227,6 +227,8 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { if ( localCopy.jdbcSelect.dependsOnParameterBindings() ) { jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext ); } + // If the translation depends on the limit or lock options, we have to rebuild the JdbcSelect + // We could avoid this by putting the lock options into the cache key if ( !localCopy.jdbcSelect.isCompatibleWith( jdbcParameterBindings, executionContext.getQueryOptions() ) ) { localCopy = buildCacheableSqmInterpretation( sqm, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index 0b5e10566f..f40f466b09 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -31,6 +31,7 @@ import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.collections.IdentitySet; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Loadable; import org.hibernate.query.Query; import org.hibernate.query.QueryTypeMismatchException; import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl; @@ -69,6 +70,7 @@ 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.query.sqm.tree.update.SqmUpdateStatement; +import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.type.BasicType; @@ -98,6 +100,7 @@ public class QuerySqmImpl private final QueryParameterBindingsImpl parameterBindings; private final QueryOptionsImpl queryOptions = new QueryOptionsImpl(); + private Callback callback; /** * Creates a Query instance from a named HQL memento @@ -145,10 +148,6 @@ public class QuerySqmImpl setMaxResults( memento.getMaxResults() ); } - if ( memento.getLockOptions() != null ) { - setLockOptions( memento.getLockOptions() ); - } - if ( memento.getParameterTypes() != null ) { for ( Map.Entry entry : memento.getParameterTypes().entrySet() ) { final QueryParameterImplementor parameter = parameterMetadata.getQueryParameter( entry.getKey() ); @@ -400,6 +399,7 @@ public class QuerySqmImpl @Override public Set> getParameters() { + getSession().checkOpen( false ); Set> parameters = new HashSet<>(); parameterMetadata.collectAllParameters( parameters::add ); return parameters; @@ -713,10 +713,26 @@ public class QuerySqmImpl } @Override - public Callback getCallback() { - return afterLoadAction -> {}; + protected void prepareForExecution() { + super.prepareForExecution(); + // Reset the callback before every execution + callback = null; } + @Override + public Callback getCallback() { + if ( callback == null ) { + callback = new CallbackImpl(); + } + return callback; + } + + @Override + public void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { + if ( callback != null ) { + callback.invokeAfterLoadActions( session, entity, persister ); + } + } @Override public NamedHqlQueryMemento toMemento(String name) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java index 8a77bad1f7..988d7512bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmInterpretationsKey.java @@ -6,13 +6,10 @@ */ package org.hibernate.query.sqm.internal; -import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.query.Limit; import org.hibernate.query.QueryParameter; import org.hibernate.query.ResultListTransformer; import org.hibernate.query.TupleTransformer; -import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryInterpretationCache; /** @@ -20,7 +17,7 @@ import org.hibernate.query.spi.QueryInterpretationCache; */ public class SqmInterpretationsKey implements QueryInterpretationCache.Key { @SuppressWarnings("WeakerAccess") - public static SqmInterpretationsKey generateFrom(QuerySqmImpl query) { + public static SqmInterpretationsKey generateFrom(QuerySqmImpl query) { if ( ! isCacheable( query ) ) { return null; } @@ -28,12 +25,14 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key { return new SqmInterpretationsKey( query.getQueryString(), query.getResultType(), - query.getQueryOptions() + query.getLockOptions(), + query.getQueryOptions().getTupleTransformer(), + query.getQueryOptions().getResultListTransformer() ); } @SuppressWarnings("WeakerAccess") - public static QueryInterpretationCache.Key generateNonSelectKey(QuerySqmImpl query) { + public static QueryInterpretationCache.Key generateNonSelectKey(QuerySqmImpl query) { // todo (6.0) : do we want to cache non-select plans? If so, what requirements? // - very minimum is that it be a "simple" (non-multi-table) statement // @@ -73,37 +72,38 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key { return false; } - if ( definesLocking( query.getQueryOptions().getLockOptions() ) ) { - // cannot cache query plans if it defines locking - return false; - } - return true; } - private static boolean hasLimit(Limit limit) { - return limit.getFirstRow() != null || limit.getMaxRows() != null; - } - - private static boolean definesLocking(LockOptions lockOptions) { - final LockMode mostRestrictiveLockMode = lockOptions.findGreatestLockMode(); - return mostRestrictiveLockMode.greaterThan( LockMode.READ ); - } - - private final String query; - private final Class resultType; - private final TupleTransformer tupleTransformer; + private final Class resultType; + private final LockOptions lockOptions; + private final TupleTransformer tupleTransformer; private final ResultListTransformer resultListTransformer; private SqmInterpretationsKey( String query, - Class resultType, - QueryOptions queryOptions) { + Class resultType, + LockOptions lockOptions, + TupleTransformer tupleTransformer, + ResultListTransformer resultListTransformer) { this.query = query; this.resultType = resultType; - this.tupleTransformer = queryOptions.getTupleTransformer(); - this.resultListTransformer = queryOptions.getResultListTransformer(); + this.lockOptions = lockOptions; + this.tupleTransformer = tupleTransformer; + this.resultListTransformer = resultListTransformer; + } + + @Override + public QueryInterpretationCache.Key prepareForStore() { + return new SqmInterpretationsKey( + query, + resultType, + // Since lock options are mutable, we need a copy for the cache key + lockOptions.makeCopy(), + tupleTransformer, + resultListTransformer + ); } @Override @@ -118,6 +118,7 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key { final SqmInterpretationsKey that = (SqmInterpretationsKey) o; return query.equals( that.query ) && areEqual( resultType, that.resultType ) + && areEqual( lockOptions, that.lockOptions ) && areEqual( tupleTransformer, that.tupleTransformer ) && areEqual( resultListTransformer, that.resultListTransformer ); } @@ -135,6 +136,7 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key { public int hashCode() { int result = query.hashCode(); result = 31 * result + ( resultType != null ? resultType.hashCode() : 0 ); + result = 31 * result + ( lockOptions != null ? lockOptions.hashCode() : 0 ); result = 31 * result + ( tupleTransformer != null ? tupleTransformer.hashCode() : 0 ); result = 31 * result + ( resultListTransformer != null ? resultListTransformer.hashCode() : 0 ); return result; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java index 11c293d72c..261c1b1812 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java @@ -12,6 +12,9 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.FilterHelper; @@ -23,6 +26,7 @@ import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmUtil; import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAstTreeHelper; import org.hibernate.sql.ast.spi.SqlExpressionResolver; @@ -65,6 +69,7 @@ public class MatchingIdSelectionHelper { public static SelectStatement generateMatchingIdSelectStatement( EntityMappingType targetEntityDescriptor, SqmDeleteOrUpdateStatement sqmStatement, + boolean queryRoot, Predicate restriction, MultiTableSqmMutationConverter sqmConverter, ExecutionContext executionContext, @@ -77,7 +82,7 @@ public class MatchingIdSelectionHelper { ); } - final QuerySpec idSelectionQuery = new QuerySpec( true, 1 ); + final QuerySpec idSelectionQuery = new QuerySpec( queryRoot, 1 ); final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup(); idSelectionQuery.getFromClause().addRoot( mutatingTableGroup ); @@ -224,6 +229,7 @@ public class MatchingIdSelectionHelper { final SelectStatement matchingIdSelection = generateMatchingIdSelectStatement( entityDescriptor, sqmMutationStatement, + true, restriction, sqmConverter, executionContext, @@ -231,7 +237,8 @@ public class MatchingIdSelectionHelper { ); final JdbcServices jdbcServices = factory.getJdbcServices(); - final SqlAstTranslator sqlAstSelectTranslator = jdbcServices.getJdbcEnvironment() + final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); + final SqlAstTranslator sqlAstSelectTranslator = jdbcEnvironment .getSqlAstTranslatorFactory() .buildSelectTranslator( factory, matchingIdSelection ); @@ -244,10 +251,25 @@ public class MatchingIdSelectionHelper { sqmConverter.getSqmParameterMappingModelExpressableResolutions()::get, executionContext.getSession() ); + final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions(); + final LockMode lockMode = lockOptions.getLockMode(); + // Acquire a WRITE lock for the rows that are about to be modified + lockOptions.setLockMode( LockMode.WRITE ); + // Visit the table joins and reset the lock mode if we encounter OUTER joins that are not supported + if ( !jdbcEnvironment.getDialect().supportsOuterJoinForUpdate() ) { + matchingIdSelection.getQuerySpec().getFromClause().visitTableJoins( + tableJoin -> { + if ( tableJoin.getJoinType() != SqlAstJoinType.INNER ) { + lockOptions.setLockMode( lockMode ); + } + } + ); + } final JdbcSelect idSelectJdbcOperation = sqlAstSelectTranslator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); + lockOptions.setLockMode( lockMode ); return jdbcServices.getJdbcSelectExecutor().list( idSelectJdbcOperation, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java index 35a2077f6b..f02a2e91f7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java @@ -7,11 +7,9 @@ package org.hibernate.query.sqm.mutation.internal; import java.util.List; -import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; -import org.hibernate.LockMode; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.internal.util.collections.Stack; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -71,6 +69,27 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter predicate -> { }, this, 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 index a237e9cb53..2577432b95 100644 --- 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 @@ -13,6 +13,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -34,6 +36,7 @@ import org.hibernate.query.sqm.tree.expression.SqmStar; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.tree.cte.CteColumn; import org.hibernate.sql.ast.tree.cte.CteContainer; +import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteStatement; import org.hibernate.sql.ast.tree.cte.CteTableGroup; import org.hibernate.sql.ast.tree.expression.ColumnReference; @@ -101,10 +104,19 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler final SqmDeleteOrUpdateStatement sqmMutationStatement = getSqmDeleteOrUpdateStatement(); final SessionFactoryImplementor factory = executionContext.getSession().getFactory(); final EntityMappingType entityDescriptor = getEntityDescriptor(); + final String explicitDmlTargetAlias; + // We need an alias because we try to acquire a WRITE lock for these rows in the CTE + if ( sqmMutationStatement.getTarget().getExplicitAlias() == null ) { + explicitDmlTargetAlias = "dml_target"; + } + else { + explicitDmlTargetAlias = sqmMutationStatement.getTarget().getExplicitAlias(); + } final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter( entityDescriptor, sqmMutationStatement.getTarget().getExplicitAlias(), + explicitDmlTargetAlias, domainParameterXref, executionContext.getQueryOptions(), executionContext.getLoadQueryInfluencers(), @@ -132,11 +144,14 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler MatchingIdSelectionHelper.generateMatchingIdSelectStatement( entityDescriptor, sqmMutationStatement, + false, restriction, sqmConverter, executionContext, factory - ) + ), + // The id-select cte will be reused multiple times + CteMaterialization.MATERIALIZED ); // Create the main query spec that will return the count of @@ -181,7 +196,12 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler paramTypeResolutions::get, executionContext.getSession() ); + final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions(); + final LockMode lockMode = lockOptions.getAliasSpecificLockMode( explicitDmlTargetAlias ); + // Acquire a WRITE lock for the rows that are about to be modified + lockOptions.setAliasSpecificLockMode( explicitDmlTargetAlias, LockMode.WRITE ); final JdbcSelect select = translator.translate( jdbcParameterBindings, executionContext.getQueryOptions() ); + lockOptions.setAliasSpecificLockMode( explicitDmlTargetAlias, lockMode ); List list = jdbcServices.getJdbcSelectExecutor().list( select, jdbcParameterBindings, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java index d3d0b39ab6..f99d244188 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithIdTableHelper.java @@ -10,6 +10,7 @@ import java.util.function.Function; import java.util.function.Supplier; import org.hibernate.LockMode; +import org.hibernate.LockOptions; import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; @@ -21,6 +22,7 @@ import org.hibernate.query.ComparisonOperator; import org.hibernate.query.NavigablePath; import org.hibernate.query.spi.SqlOmittingQueryOptions; import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter; +import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.expression.ColumnReference; @@ -127,8 +129,23 @@ public final class ExecuteWithIdTableHelper { final JdbcServices jdbcServices = factory.getJdbcServices(); final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory(); + final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions(); + final LockMode lockMode = lockOptions.getLockMode(); + // Acquire a WRITE lock for the rows that are about to be modified + lockOptions.setLockMode( LockMode.WRITE ); + // Visit the table joins and reset the lock mode if we encounter OUTER joins that are not supported + if ( !jdbcEnvironment.getDialect().supportsOuterJoinForUpdate() ) { + matchingIdSelection.getFromClause().visitTableJoins( + tableJoin -> { + if ( tableJoin.getJoinType() != SqlAstJoinType.INNER ) { + lockOptions.setLockMode( lockMode ); + } + } + ); + } final JdbcInsert jdbcInsert = sqlAstTranslatorFactory.buildInsertTranslator( factory, idTableInsert ) .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); + lockOptions.setLockMode( lockMode ); return jdbcServices.getJdbcMutationExecutor().execute( jdbcInsert, @@ -158,7 +175,7 @@ public final class ExecuteWithIdTableHelper { final TableGroup idTableGroup = new StandardTableGroup( new NavigablePath( idTableReference.getTableExpression() ), entityDescriptor, - LockMode.NONE, + null, idTableReference, null, executionContext.getSession().getFactory() @@ -182,7 +199,7 @@ public final class ExecuteWithIdTableHelper { if ( idTableColumn != idTable.getSessionUidColumn() ) { querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - i+1, + i + 1, i, new ColumnReference( tableReference, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithoutIdTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithoutIdTableHelper.java index 2f2f818076..f8f53d038d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithoutIdTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/idtable/ExecuteWithoutIdTableHelper.java @@ -41,7 +41,7 @@ public final class ExecuteWithoutIdTableHelper { final StandardTableGroup matchingIdSelectTableGroup = new StandardTableGroup( navigablePath, rootEntityPersister, - LockMode.PESSIMISTIC_WRITE, + rootTableReference.getIdentificationVariable(), rootTableReference, null, sessionFactory diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/DisjunctionRestrictionProducer.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/DisjunctionRestrictionProducer.java deleted file mode 100644 index 0ad6e4a792..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/DisjunctionRestrictionProducer.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.inline; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.BasicValuedModelPart; -import org.hibernate.metamodel.mapping.SelectableConsumer; -import org.hibernate.metamodel.mapping.EntityIdentifierMapping; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.query.ComparisonOperator; -import org.hibernate.sql.ast.tree.expression.ColumnReference; -import org.hibernate.sql.ast.tree.expression.JdbcLiteral; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; -import org.hibernate.sql.ast.tree.predicate.Junction; -import org.hibernate.sql.exec.spi.ExecutionContext; - -/** - * MatchingIdRestrictionProducer producing a restriction based on a disjunction (OR) predicate. E.g.: - * - * ```` - * delete - * from - * entity-table - * where - * ( id = 1 ) - * or ( id = 2 ) - * or ( id = 3 ) - * or ( id = 4 ) - * ```` - * - * @author Steve Ebersole - */ -public class DisjunctionRestrictionProducer implements MatchingIdRestrictionProducer { - @Override - public Junction produceRestriction( - List matchingIdValues, - EntityMappingType entityDescriptor, - TableReference mutatingTableReference, - Supplier> columnsToMatchVisitationSupplier, - ExecutionContext executionContext) { - assert matchingIdValues != null; - assert ! matchingIdValues.isEmpty(); - - final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory(); - - final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - final int idColumnCount = identifierMapping.getJdbcTypeCount(); - assert idColumnCount > 0; - - final Junction predicate = new Junction( Junction.Nature.DISJUNCTION ); - - if ( idColumnCount == 1 ) { - final BasicValuedModelPart basicIdMapping = (BasicValuedModelPart) identifierMapping; - final String idColumn = basicIdMapping.getSelectionExpression(); - final ColumnReference idColumnReference = new ColumnReference( - mutatingTableReference, - idColumn, - // id columns cannot be formulas and cannot have custom read and write expressions - false, - null, - null, - basicIdMapping.getJdbcMapping(), - sessionFactory - ); - - matchingIdValues.forEach( - matchingId -> predicate.add( - new ComparisonPredicate( - idColumnReference, - ComparisonOperator.EQUAL, - new JdbcLiteral<>( matchingId, basicIdMapping.getJdbcMapping() ) - ) - ) - ); - } - else { - final List columnReferences = new ArrayList<>( idColumnCount ); - final List jdbcMappings = new ArrayList<>( idColumnCount ); - identifierMapping.forEachSelectable( - (columnIndex, selection) -> { - columnReferences.add( - new ColumnReference( - mutatingTableReference, - selection, - sessionFactory - ) - ); - jdbcMappings.add( selection.getJdbcMapping() ); - } - ); - - matchingIdValues.forEach( - matchingId -> { - final Junction idMatch = new Junction( Junction.Nature.CONJUNCTION ); - - assert matchingId instanceof Object[]; - - final Object[] matchingIdParts = (Object[]) matchingId; - - for ( int p = 0; p < matchingIdParts.length; p++ ) { - idMatch.add( - new ComparisonPredicate( - columnReferences.get( p ), - ComparisonOperator.EQUAL, - new JdbcLiteral<>( matchingIdParts[ p ], jdbcMappings.get( p ) ) - ) - ); - } - - predicate.add( idMatch ); - } - ); - } - - return predicate; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java index 92c22132a6..be3528af5b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineStrategy.java @@ -34,7 +34,7 @@ public class InlineStrategy implements SqmMultiTableMutationStrategy { } private static Function determinePredicateProducer(Dialect dialect) { - throw new NotYetImplementedFor6Exception(); + return statement -> new InPredicateRestrictionProducer(); } public InlineStrategy(Function matchingIdsStrategy) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/TableValueConstructorRestrictionProducer.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/TableValueConstructorRestrictionProducer.java deleted file mode 100644 index 2e5c79540e..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/TableValueConstructorRestrictionProducer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.inline; - -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.hibernate.NotYetImplementedFor6Exception; -import org.hibernate.metamodel.mapping.SelectableConsumer; -import org.hibernate.metamodel.mapping.EntityMappingType; -import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; -import org.hibernate.sql.exec.spi.ExecutionContext; - -/** - * MatchingIdRestrictionProducer producing a restriction based on a SQL table-value-constructor. E.g.: - * - * ```` - * delete - * from - * entity-table - * where - * ( id ) in ( - * select - * id - * from ( - * values - * ( 1 ), - * ( 2 ), - * ( 3 ), - * ( 4 ) - * ) as HT (id) - * ) - * ```` - * - * @author Vlad Mihalcea - * @author Steve Ebersole - */ -@SuppressWarnings("unused") -public class TableValueConstructorRestrictionProducer implements MatchingIdRestrictionProducer { - @Override - public InSubQueryPredicate produceRestriction( - List matchingIdValues, - EntityMappingType entityDescriptor, - TableReference mutatingTableReference, - Supplier> columnsToMatchVisitationSupplier, - ExecutionContext executionContext) { - // Not "yet" implemented. Not sure we will. This requires the ability to define - // "in-line views" with a table-ctor which the SQL AST does not yet define support for - throw new NotYetImplementedFor6Exception( getClass() ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/NamedFunctionDescriptorBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/NamedFunctionDescriptorBuilder.java index 637fd37490..c683265c06 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/NamedFunctionDescriptorBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/NamedFunctionDescriptorBuilder.java @@ -19,6 +19,7 @@ public class NamedFunctionDescriptorBuilder { private final SqmFunctionRegistry registry; private final String registrationKey; + private final boolean isAggregate; private final String functionName; @@ -29,9 +30,14 @@ public class NamedFunctionDescriptorBuilder { private String argumentListSignature; private SqlAstNodeRenderingMode argumentRenderingMode = SqlAstNodeRenderingMode.DEFAULT; - public NamedFunctionDescriptorBuilder(SqmFunctionRegistry registry, String registrationKey, String functionName) { + public NamedFunctionDescriptorBuilder( + SqmFunctionRegistry registry, + String registrationKey, + boolean isAggregate, + String functionName) { this.registry = registry; this.registrationKey = registrationKey; + this.isAggregate = isAggregate; this.functionName = functionName; } @@ -88,6 +94,7 @@ public class NamedFunctionDescriptorBuilder { argumentsValidator, returnTypeResolver, registrationKey, + isAggregate, argumentListSignature, argumentRenderingMode ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/PatternFunctionDescriptorBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/PatternFunctionDescriptorBuilder.java index f277b2a69e..4b4884b3dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/PatternFunctionDescriptorBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/PatternFunctionDescriptorBuilder.java @@ -19,6 +19,7 @@ import org.hibernate.type.BasicType; public class PatternFunctionDescriptorBuilder { private final SqmFunctionRegistry registry; private final String registrationKey; + private final boolean isAggregate; private final String pattern; private String argumentListSignature; @@ -26,9 +27,10 @@ public class PatternFunctionDescriptorBuilder { private FunctionReturnTypeResolver returnTypeResolver; private SqlAstNodeRenderingMode argumentRenderingMode = SqlAstNodeRenderingMode.DEFAULT; - public PatternFunctionDescriptorBuilder(SqmFunctionRegistry registry, String registrationKey, String pattern) { + public PatternFunctionDescriptorBuilder(SqmFunctionRegistry registry, String registrationKey, boolean isAggregate, String pattern) { this.registry = registry; this.registrationKey = registrationKey; + this.isAggregate = isAggregate; this.pattern = pattern; } @@ -71,6 +73,7 @@ public class PatternFunctionDescriptorBuilder { argumentsValidator, returnTypeResolver, registrationKey, + isAggregate, argumentListSignature ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/internal/PatternRenderer.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/internal/PatternRenderer.java index 48bc9e1e83..3b66d47ea6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/internal/PatternRenderer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/internal/PatternRenderer.java @@ -12,6 +12,9 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.expression.Distinct; +import org.hibernate.sql.ast.tree.expression.Star; +import org.hibernate.sql.ast.tree.predicate.Predicate; import java.util.ArrayList; import java.util.HashSet; @@ -132,19 +135,37 @@ public class PatternRenderer { public void render( SqlAppender sqlAppender, List args, - SqlAstTranslator walker) { + SqlAstTranslator translator) { + render( sqlAppender, args, null, translator ); + } + + public void render( + SqlAppender sqlAppender, + List args, + Predicate filter, + SqlAstTranslator translator) { final int numberOfArguments = args.size(); + final boolean caseWrapper = filter != null && !translator.supportsFilterClause(); if ( numberOfArguments < maxParamIndex ) { LOG.missingArguments( maxParamIndex, numberOfArguments ); } for ( int i = 0; i < chunks.length; i++ ) { - if ( i==varargParam ) { + if ( i == varargParam ) { for ( int j = i; j < numberOfArguments; j++ ) { final SqlAstNode arg = args.get( j ); if ( arg != null ) { sqlAppender.appendSql( chunks[i] ); - walker.render( arg, argumentRenderingMode ); + if ( caseWrapper && !( arg instanceof Distinct ) && !( arg instanceof Star ) ) { + sqlAppender.appendSql( "case when " ); + filter.accept( translator ); + sqlAppender.appendSql( " then " ); + translator.render( arg, argumentRenderingMode ); + sqlAppender.appendSql( " else null end" ); + } + else { + translator.render( arg, argumentRenderingMode ); + } } } } @@ -155,12 +176,27 @@ public class PatternRenderer { sqlAppender.appendSql( chunks[i] ); } if ( arg != null ) { - walker.render( arg, argumentRenderingMode ); + if ( caseWrapper && !( arg instanceof Distinct ) && !( arg instanceof Star ) ) { + sqlAppender.appendSql( "case when " ); + filter.accept( translator ); + sqlAppender.appendSql( " then " ); + translator.render( arg, argumentRenderingMode ); + sqlAppender.appendSql( " else null end" ); + } + else { + translator.render( arg, argumentRenderingMode ); + } } } else { sqlAppender.appendSql( chunks[i] ); } } + + if ( filter != null && !caseWrapper ) { + sqlAppender.appendSql( " filter (where " ); + filter.accept( translator ); + sqlAppender.appendSql( ')' ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java index 3198570923..a664a983f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/spi/BaseSemanticQueryWalker.java @@ -100,7 +100,6 @@ import org.hibernate.query.sqm.tree.update.SqmAssignment; import org.hibernate.query.sqm.tree.update.SqmSetClause; import org.hibernate.query.sqm.tree.update.SqmUpdateStatement; import org.hibernate.service.ServiceRegistry; -import org.hibernate.sql.ast.tree.Statement; /** * Base support for an SQM walker diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 3495603b4f..1d1d7ffc40 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -25,7 +25,6 @@ import javax.persistence.TemporalType; import org.hibernate.HibernateException; import org.hibernate.Internal; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.QueryException; import org.hibernate.boot.model.process.internal.InferredBasicValueResolver; @@ -89,7 +88,7 @@ import org.hibernate.query.sqm.InterpretationException; import org.hibernate.query.sqm.SqmExpressable; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor; -import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; +import org.hibernate.query.sqm.function.SelfRenderingAggregateFunctionSqlAstExpression; import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.SqmMappingModelHelper; import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker; @@ -505,11 +504,8 @@ public abstract class BaseSqmToSqlAstConverter extends Base } @Override - public LockMode determineLockMode(String identificationVariable) { - final LockOptions lockOptions = getQueryOptions().getLockOptions(); - return lockOptions.getScope() || identificationVariable == null - ? lockOptions.getLockMode() - : lockOptions.getEffectiveLockMode( identificationVariable ); + public void registerLockMode(String identificationVariable, LockMode explicitLockMode) { + throw new UnsupportedOperationException( "Registering lock modes should only be done for result set mappings!" ); } public QueryOptions getQueryOptions() { @@ -589,7 +585,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( rootPath, sqmStatement.getRoot().getAlias(), - LockMode.WRITE, () -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate ), this, getCreationContext() @@ -847,7 +842,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( rootPath, statement.getRoot().getAlias(), - LockMode.WRITE, () -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate ), this, getCreationContext() @@ -930,7 +924,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( rootPath, sqmStatement.getTarget().getExplicitAlias(), - LockMode.WRITE, () -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate ), this, getCreationContext() @@ -1029,7 +1022,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup( rootPath, sqmStatement.getTarget().getExplicitAlias(), - LockMode.WRITE, () -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate ), this, getCreationContext() @@ -1083,6 +1075,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base public SelectStatement visitSelectStatement(SqmSelectStatement statement) { Map cteStatements = this.visitCteContainer( statement ); final QueryPart queryPart = visitQueryPart( statement.getQueryPart() ); + final List> domainResults = queryPart.isRoot() ? this.domainResults : Collections.emptyList(); return new SelectStatement( statement.isWithRecursive(), cteStatements, queryPart, domainResults ); } @@ -1147,6 +1140,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base return new CteStatement( cteTable, visitStatement( sqmCteStatement.getCteDefinition() ), + sqmCteStatement.getMaterialization(), sqmCteStatement.getSearchClauseKind(), visitSearchBySpecifications( cteTable, sqmCteStatement.getSearchBySpecifications() ), visitCycleColumns( cteTable, sqmCteStatement.getCycleColumns() ), @@ -1751,7 +1745,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base if ( fromClauseIndex.isResolved( sqmRoot ) ) { log.tracef( "Already resolved SqmRoot [%s] to TableGroup", sqmRoot ); } - final SqlExpressionResolver sqlExpressionResolver = getSqlExpressionResolver(); final TableGroup tableGroup; if ( sqmRoot.isCorrelated() ) { final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory(); @@ -1784,7 +1777,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base tableGroup = entityDescriptor.createRootTableGroup( sqmRoot.getNavigablePath(), sqmRoot.getExplicitAlias(), - LockMode.NONE, () -> predicate -> {}, this, creationContext @@ -1849,7 +1841,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base tableGroup = entityDescriptor.createRootTableGroup( sqmRoot.getNavigablePath(), sqmRoot.getExplicitAlias(), - LockMode.NONE, () -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate @@ -1935,7 +1926,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base sqmJoin.getExplicitAlias(), sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(), sqmJoin.isFetched(), - determineLockMode( sqmJoin.getExplicitAlias() ), this ); } @@ -1950,7 +1940,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base sqmJoin.getExplicitAlias(), sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(), sqmJoin.isFetched(), - determineLockMode( sqmJoin.getExplicitAlias() ), this ); } @@ -2002,7 +1991,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup tableGroup = entityDescriptor.createRootTableGroup( sqmJoin.getNavigablePath(), sqmJoin.getExplicitAlias(), - determineLockMode( sqmJoin.getExplicitAlias() ), () -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate @@ -2031,7 +2019,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup tableGroup = entityDescriptor.createRootTableGroup( sqmJoin.getNavigablePath(), sqmJoin.getExplicitAlias(), - determineLockMode( sqmJoin.getExplicitAlias() ), () -> predicate -> additionalRestrictions = SqlAstTreeHelper.combinePredicates( additionalRestrictions, predicate @@ -2102,7 +2089,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base null, parentTableGroup.isInnerJoinPossible() ? SqlAstJoinType.INNER : SqlAstJoinType.LEFT, false, - null, this ); @@ -2709,7 +2695,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base null, SqlAstJoinType.INNER, false, - LockMode.READ, sqlAliasBaseManager, getSqlExpressionResolver(), creationContext @@ -3834,7 +3819,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup tableGroup = collectionPart.createRootTableGroup( pluralPath.getNavigablePath(), null, - LockOptions.NONE.getLockMode(), () -> subQuerySpec::applyPredicate, this, creationContext @@ -3851,10 +3835,11 @@ public abstract class BaseSqmToSqlAstConverter extends Base final BasicType integerType = creationContext.getDomainModel() .getTypeConfiguration() .getBasicTypeForJavaType( Integer.class ); - final Expression expression = new SelfRenderingFunctionSqlAstExpression( + final Expression expression = new SelfRenderingAggregateFunctionSqlAstExpression( functionDescriptor.getName(), functionDescriptor::render, Collections.singletonList( new QueryLiteral<>( 1, integerType ) ), + null, integerType, integerType ); @@ -3902,7 +3887,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup tableGroup = mappingModelExpressable.createRootTableGroup( pluralPartPath.getNavigablePath(), null, - LockOptions.NONE.getLockMode(), () -> subQuerySpec::applyPredicate, this, creationContext @@ -3932,10 +3916,11 @@ public abstract class BaseSqmToSqlAstConverter extends Base ); } ); - final Expression expression = new SelfRenderingFunctionSqlAstExpression( + final Expression expression = new SelfRenderingAggregateFunctionSqlAstExpression( functionDescriptor.getName(), functionDescriptor::render, arguments, + null, (AllowableFunctionReturnType) collectionPart.getJdbcMappings().get( 0 ), collectionPart ); @@ -4050,7 +4035,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base final TableGroup tableGroup = mappingModelExpressable.createRootTableGroup( pluralPath.getNavigablePath(), null, - LockOptions.NONE.getLockMode(), () -> subQuerySpec::applyPredicate, this, creationContext @@ -4164,7 +4148,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base sqmPluralPath.getExplicitAlias(), SqlAstJoinType.INNER, false, - LockMode.NONE, sqlAliasBaseManager, subQueryState, creationContext @@ -4527,7 +4510,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base // "initializing" state as part of AfterLoadAction final String alias; - LockMode lockMode = LockMode.READ; FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming(); boolean joined = false; @@ -4547,7 +4529,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base } joined = true; alias = fetchedJoin.getExplicitAlias(); - lockMode = determineLockMode( alias ); } else { // there was not an explicit fetch in the SQM @@ -4611,7 +4592,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base alias, SqlAstJoinType.LEFT, true, - LockMode.NONE, this ); return tableGroupJoin.getJoinedGroup(); @@ -4627,7 +4607,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base fetchablePath, fetchTiming, joined, - lockMode, alias, this ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java index f45dc3d2b2..3cc4b1b294 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java @@ -9,7 +9,6 @@ package org.hibernate.query.sqm.sql.internal; import java.util.ArrayList; import java.util.List; -import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicValuedModelPart; @@ -220,7 +219,6 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta null, SqlAstJoinType.INNER, false, - LockMode.READ, sqlAstCreationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java index e00631ae0b..96c70ba369 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/cte/SqmCteStatement.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.tree.cte; import java.util.List; +import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -22,6 +23,7 @@ import org.hibernate.query.sqm.tree.SqmVisitableNode; public class SqmCteStatement extends AbstractSqmNode implements SqmVisitableNode { private final SqmCteContainer cteContainer; private final SqmCteTable cteTable; + private final CteMaterialization materialization; private final SqmStatement cteDefinition; private final CteSearchClauseKind searchClauseKind; private final List searchBySpecifications; @@ -33,10 +35,12 @@ public class SqmCteStatement extends AbstractSqmNode implements SqmVisitableN public SqmCteStatement( SqmCteTable cteTable, SqmStatement cteDefinition, + CteMaterialization materialization, NodeBuilder nodeBuilder) { super( nodeBuilder ); this.cteTable = cteTable; this.cteDefinition = cteDefinition; + this.materialization = materialization; this.cteContainer = null; this.searchClauseKind = null; this.searchBySpecifications = null; @@ -49,10 +53,12 @@ public class SqmCteStatement extends AbstractSqmNode implements SqmVisitableN public SqmCteStatement( SqmCteTable cteTable, SqmStatement cteDefinition, + CteMaterialization materialization, SqmCteContainer cteContainer) { super( cteContainer.nodeBuilder() ); this.cteTable = cteTable; this.cteDefinition = cteDefinition; + this.materialization = materialization; this.cteContainer = cteContainer; this.searchClauseKind = null; this.searchBySpecifications = null; @@ -74,6 +80,10 @@ public class SqmCteStatement extends AbstractSqmNode implements SqmVisitableN return cteContainer; } + public CteMaterialization getMaterialization() { + return materialization; + } + public CteSearchClauseKind getSearchClauseKind() { return searchClauseKind; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Conversion.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Conversion.java index c1bd032af4..d59a23f74e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Conversion.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Conversion.java @@ -27,7 +27,7 @@ import org.hibernate.sql.results.graph.basic.BasicResult; */ public class Conversion implements Expression, DomainResultProducer { - private Duration duration; + private final Duration duration; private final TemporalUnit unit; private final BasicValuedMapping type; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java index 0875a9abdf..3ac79bcdda 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java @@ -23,7 +23,7 @@ import java.util.ArrayList; import java.util.List; /** - * An SQM function + * A SQM function * * @author Steve Ebersole */ @@ -34,17 +34,7 @@ public abstract class SqmFunction extends AbstractSqmExpression private final String functionName; private final SqmFunctionDescriptor functionDescriptor; - private List> arguments; - - public SqmFunction( - String functionName, - SqmFunctionDescriptor functionDescriptor, - SqmExpressable type, - NodeBuilder criteriaBuilder) { - super( type, criteriaBuilder ); - this.functionName = functionName; - this.functionDescriptor = functionDescriptor; - } + private final List> arguments; public SqmFunction( String functionName, @@ -71,15 +61,6 @@ public abstract class SqmFunction extends AbstractSqmExpression return arguments; } - public void addArgument(SqmTypedNode argument) { - assert argument != null; - - if ( arguments == null ) { - arguments = new ArrayList<>(); - } - arguments.add( argument ); - } - public abstract Expression convertToSqlAst(SqmToSqlAstConverter walker); @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java b/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java index aa16a7fe86..ffe7e1f0f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ForUpdateFragment.java @@ -20,35 +20,27 @@ import org.hibernate.internal.util.StringHelper; */ public class ForUpdateFragment { private final StringBuilder aliases = new StringBuilder(); - private boolean isNowaitEnabled; - private boolean isSkipLockedEnabled; private final Dialect dialect; - private LockMode lockMode; - private LockOptions lockOptions; - - public ForUpdateFragment(Dialect dialect) { - this.dialect = dialect; - } + private final LockOptions lockOptions; public ForUpdateFragment(Dialect dialect, LockOptions lockOptions, Map keyColumnNames) throws QueryException { - this( dialect ); + this.dialect = dialect; LockMode upgradeType = null; - Iterator iter = lockOptions.getAliasLockIterator(); + Iterator> iter = lockOptions.getAliasLockIterator(); this.lockOptions = lockOptions; if ( !iter.hasNext()) { // no tables referenced final LockMode lockMode = lockOptions.getLockMode(); if ( LockMode.READ.lessThan( lockMode ) ) { upgradeType = lockMode; - this.lockMode = lockMode; } } while ( iter.hasNext() ) { - final Map.Entry me = ( Map.Entry ) iter.next(); - final LockMode lockMode = ( LockMode ) me.getValue(); + final Map.Entry me = iter.next(); + final LockMode lockMode = me.getValue(); if ( LockMode.READ.lessThan( lockMode ) ) { - final String tableAlias = ( String ) me.getKey(); + final String tableAlias = me.getKey(); if ( dialect.forUpdateOfColumns() ) { String[] keyColumns = keyColumnNames.get( tableAlias ); //use the id column alias if ( keyColumns == null ) { @@ -68,14 +60,6 @@ public class ForUpdateFragment { upgradeType = lockMode; } } - - if ( upgradeType == LockMode.UPGRADE_NOWAIT || lockOptions.getTimeOut() == LockOptions.NO_WAIT ) { - setNowaitEnabled( true ); - } - - if ( upgradeType == LockMode.UPGRADE_SKIPLOCKED || lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED ) { - setSkipLockedEnabled( true ); - } } public ForUpdateFragment addTableAlias(String alias) { @@ -87,40 +71,13 @@ public class ForUpdateFragment { } public String toFragmentString() { - if ( lockOptions!= null ) { - if ( aliases.length() == 0) { - return dialect.getForUpdateString( lockOptions ); - } - else { - return dialect.getForUpdateString( aliases.toString(), lockOptions ); - } - } - else if ( aliases.length() == 0) { - if ( lockMode != null ) { - return dialect.getForUpdateString( lockMode ); - } - return ""; - } - // TODO: pass lock mode - if(isNowaitEnabled) { - return dialect.getForUpdateNowaitString( aliases.toString() ); - } - else if (isSkipLockedEnabled) { - return dialect.getForUpdateSkipLockedString( aliases.toString() ); + if ( aliases.length() == 0) { + return dialect.getForUpdateString( lockOptions ); } else { - return dialect.getForUpdateString( aliases.toString() ); + return dialect.getForUpdateString( aliases.toString(), lockOptions ); } } - public ForUpdateFragment setNowaitEnabled(boolean nowait) { - isNowaitEnabled = nowait; - return this; - } - - public ForUpdateFragment setSkipLockedEnabled(boolean skipLocked) { - isSkipLockedEnabled = skipLocked; - return this; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/Clause.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/Clause.java index a95533bde8..53fcba17b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/Clause.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/Clause.java @@ -50,6 +50,7 @@ public enum Clause { ORDER, OFFSET, FETCH, + FOR_UPDATE, OVER, PARTITION, CALL, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslator.java index f4518dcf7f..9ef8892811 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/SqlAstTranslator.java @@ -23,6 +23,11 @@ public interface SqlAstTranslator extends SqlAstWalker */ void render(SqlAstNode sqlAstNode, SqlAstNodeRenderingMode renderingMode); + /** + * Whether the FILTER clause for aggregate functions is supported. + */ + boolean supportsFilterClause(); + /** * Not the best spot for this. Its the table names collected while walking the SQL AST. * Its ok here because the translator is consider a one-time-use. It just needs to be called diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index dafeed8b0f..c9e8b53731 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; @@ -19,8 +20,15 @@ import java.util.Map; import java.util.Set; import java.util.TimeZone; +import org.hibernate.LockMode; +import org.hibernate.QueryException; +import org.hibernate.dialect.RowLockStrategy; +import org.hibernate.metamodel.mapping.EntityAssociationMapping; +import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Queryable; +import org.hibernate.query.IllegalQueryOperationException; +import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteSearchClauseKind; import org.hibernate.query.FetchClauseType; import org.hibernate.LockOptions; @@ -137,6 +145,7 @@ import org.hibernate.sql.exec.internal.JdbcParametersImpl; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcDelete; import org.hibernate.sql.exec.spi.JdbcInsert; +import org.hibernate.sql.exec.spi.JdbcLockStrategy; import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBinding; @@ -144,6 +153,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.exec.spi.JdbcUpdate; import org.hibernate.sql.results.jdbc.internal.JdbcValuesMappingProducerStandard; +import org.hibernate.type.BasicType; import org.hibernate.type.IntegerType; import org.hibernate.type.StringType; import org.hibernate.type.descriptor.WrapperOptions; @@ -194,6 +204,7 @@ public abstract class AbstractSqlAstTranslator implemen private Limit limit; private JdbcParameter offsetParameter; private JdbcParameter limitParameter; + private ForUpdateClause forUpdate; public Dialect getDialect() { return dialect; @@ -320,6 +331,12 @@ public abstract class AbstractSqlAstTranslator implemen return affectedTableNames; } + @Override + public boolean supportsFilterClause() { + // By default we report false because not many dialects support this + return false; + } + @Override public void appendSql(String fragment) { sqlBuffer.append( fragment ); @@ -354,6 +371,10 @@ public abstract class AbstractSqlAstTranslator implemen return appliedParameterBindings; } + protected JdbcLockStrategy getJdbcLockStrategy() { + return lockOptions == null ? JdbcLockStrategy.FOLLOW_ON : JdbcLockStrategy.NONE; + } + protected JdbcParameterBindings getJdbcParameterBindings() { return jdbcParameterBindings; } @@ -371,8 +392,8 @@ public abstract class AbstractSqlAstTranslator implemen } protected boolean hasOffset(QueryPart queryPart) { - if ( queryPart.isRoot() && hasLimit() ) { - return limit.getFirstRowJpa() != 0; + if ( queryPart.isRoot() && hasLimit() && limit.getFirstRowJpa() != 0 ) { + return true; } else { return queryPart.getOffsetClauseExpression() != null; @@ -532,79 +553,37 @@ public abstract class AbstractSqlAstTranslator implemen protected JdbcDelete translateDelete(DeleteStatement sqlAst) { visitDeleteStatement( sqlAst ); - return new JdbcDelete() { - @Override - public String getSql() { - return AbstractSqlAstTranslator.this.getSql(); - } - - @Override - public List getParameterBinders() { - return AbstractSqlAstTranslator.this.getParameterBinders(); - } - - @Override - public Set getAffectedTableNames() { - return AbstractSqlAstTranslator.this.getAffectedTableNames(); - } - - @Override - public Set getFilterJdbcParameters() { - return AbstractSqlAstTranslator.this.getFilterJdbcParameters(); - } - }; + return new JdbcDelete( + getSql(), + getParameterBinders(), + getAffectedTableNames(), + getFilterJdbcParameters(), + getAppliedParameterBindings() + ); } protected JdbcUpdate translateUpdate(UpdateStatement sqlAst) { visitUpdateStatement( sqlAst ); - return new JdbcUpdate() { - @Override - public String getSql() { - return AbstractSqlAstTranslator.this.getSql(); - } - - @Override - public List getParameterBinders() { - return AbstractSqlAstTranslator.this.getParameterBinders(); - } - - @Override - public Set getFilterJdbcParameters() { - return AbstractSqlAstTranslator.this.getFilterJdbcParameters(); - } - - @Override - public Set getAffectedTableNames() { - return AbstractSqlAstTranslator.this.getAffectedTableNames(); - } - }; + return new JdbcUpdate( + getSql(), + getParameterBinders(), + getAffectedTableNames(), + getFilterJdbcParameters(), + getAppliedParameterBindings() + ); } protected JdbcInsert translateInsert(InsertStatement sqlAst) { visitInsertStatement( sqlAst ); - return new JdbcInsert() { - @Override - public String getSql() { - return AbstractSqlAstTranslator.this.getSql(); - } - - @Override - public List getParameterBinders() { - return AbstractSqlAstTranslator.this.getParameterBinders(); - } - - @Override - public Set getAffectedTableNames() { - return AbstractSqlAstTranslator.this.getAffectedTableNames(); - } - - @Override - public Set getFilterJdbcParameters() { - return AbstractSqlAstTranslator.this.getFilterJdbcParameters(); - } - }; + return new JdbcInsert( + getSql(), + getParameterBinders(), + getAffectedTableNames(), + getFilterJdbcParameters(), + getAppliedParameterBindings() + ); } protected JdbcSelect translateSelect(SelectStatement sqlAstSelect) { @@ -626,7 +605,7 @@ public abstract class AbstractSqlAstTranslator implemen rowsToSkip = getRowsToSkip( sqlAstSelect, getJdbcParameterBindings() ), getMaxRows( sqlAstSelect, getJdbcParameterBindings(), rowsToSkip ), getAppliedParameterBindings(), - getLockOptions(), + getJdbcLockStrategy(), getOffsetParameter(), getLimitParameter() ); @@ -676,40 +655,58 @@ public abstract class AbstractSqlAstTranslator implemen final Limit limit = getLimit(); if ( limit.getFirstRow() != null ) { setOffsetParameter( - new JdbcParameterImpl( IntegerType.INSTANCE ) { - @Override - public void bindParameterValue( - PreparedStatement statement, - int startPosition, - JdbcParameterBindings jdbcParamBindings, - ExecutionContext executionContext) throws SQLException { - IntegerType.INSTANCE.getJdbcValueBinder().bind( - statement, - executionContext.getQueryOptions().getLimit().getFirstRow(), - startPosition, - executionContext.getSession() - ); - } - } + new OffsetJdbcParameter( + sessionFactory.getTypeConfiguration().getBasicTypeForJavaType( Integer.class ) + ) ); } if ( limit.getMaxRows() != null ) { setLimitParameter( - new JdbcParameterImpl( IntegerType.INSTANCE ) { - @Override - public void bindParameterValue( - PreparedStatement statement, - int startPosition, - JdbcParameterBindings jdbcParamBindings, - ExecutionContext executionContext) throws SQLException { - IntegerType.INSTANCE.getJdbcValueBinder().bind( - statement, - executionContext.getQueryOptions().getLimit().getMaxRows(), - startPosition, - executionContext.getSession() - ); - } - } + new LimitJdbcParameter( + sessionFactory.getTypeConfiguration().getBasicTypeForJavaType( Integer.class ) + ) + ); + } + } + + private static class OffsetJdbcParameter extends JdbcParameterImpl { + + public OffsetJdbcParameter(BasicType type) { + super( type ); + } + + @Override + public void bindParameterValue( + PreparedStatement statement, + int startPosition, + JdbcParameterBindings jdbcParamBindings, + ExecutionContext executionContext) throws SQLException { + getJdbcMapping().getJdbcValueBinder().bind( + statement, + executionContext.getQueryOptions().getLimit().getFirstRow(), + startPosition, + executionContext.getSession() + ); + } + } + + private static class LimitJdbcParameter extends JdbcParameterImpl { + + public LimitJdbcParameter(BasicType type) { + super( type ); + } + + @Override + public void bindParameterValue( + PreparedStatement statement, + int startPosition, + JdbcParameterBindings jdbcParamBindings, + ExecutionContext executionContext) throws SQLException { + getJdbcMapping().getJdbcValueBinder().bind( + statement, + executionContext.getQueryOptions().getLimit().getMaxRows(), + startPosition, + executionContext.getSession() ); } } @@ -779,7 +776,7 @@ public abstract class AbstractSqlAstTranslator implemen final Stack clauseStack = getClauseStack(); try { clauseStack.push( Clause.DELETE ); - renderTableReference( statement.getTargetTable() ); + renderTableReference( statement.getTargetTable(), LockMode.NONE ); } finally { clauseStack.pop(); @@ -804,7 +801,7 @@ public abstract class AbstractSqlAstTranslator implemen final Stack clauseStack = getClauseStack(); try { clauseStack.push( Clause.UPDATE ); - renderTableReference( statement.getTargetTable() ); + renderTableReference( statement.getTargetTable(), LockMode.NONE ); } finally { clauseStack.pop(); @@ -924,6 +921,262 @@ public abstract class AbstractSqlAstTranslator implemen } } + protected void visitForUpdateClause(QuerySpec querySpec) { + if ( querySpec.isRoot() ) { + if ( forUpdate != null ) { + final Boolean followOnLocking = getLockOptions().getFollowOnLocking(); + if ( Boolean.TRUE.equals( followOnLocking ) ) { + lockOptions = null; + } + else { + forUpdate.merge( getLockOptions() ); + forUpdate.applyAliases( dialect.getWriteRowLockStrategy(), querySpec ); + final LockStrategy lockStrategy = determineLockingStrategy( querySpec, forUpdate, followOnLocking ); + switch ( lockStrategy ) { + case CLAUSE: + renderForUpdateClause( querySpec, forUpdate ); + break; + case FOLLOW_ON: + lockOptions = null; + break; + } + } + forUpdate = null; + } + else { + // Since we get here, we know that no alias locks were applied. + // We only apply locking on the root query though if there is a global lock mode + final LockOptions lockOptions = getLockOptions(); + final Boolean followOnLocking = lockOptions.getFollowOnLocking(); + if ( Boolean.TRUE.equals( followOnLocking )) { + this.lockOptions = null; + } + else if ( lockOptions.getLockMode() != LockMode.NONE ) { + final ForUpdateClause forUpdateClause = new ForUpdateClause(); + forUpdateClause.merge( getLockOptions() ); + forUpdateClause.applyAliases( dialect.getWriteRowLockStrategy(), querySpec ); + final LockStrategy lockStrategy = determineLockingStrategy( + querySpec, + forUpdateClause, + followOnLocking + ); + switch ( lockStrategy ) { + case CLAUSE: + renderForUpdateClause( + querySpec, + forUpdateClause + ); + break; + case FOLLOW_ON: + if ( Boolean.FALSE.equals( followOnLocking ) ) { + throw new UnsupportedOperationException( "" ); + } + this.lockOptions = null; + break; + } + } + } + } + else if ( forUpdate != null ) { + forUpdate.merge( getLockOptions() ); + forUpdate.applyAliases( dialect.getWriteRowLockStrategy(), querySpec ); + final LockStrategy lockStrategy = determineLockingStrategy( querySpec, forUpdate, null ); + switch ( lockStrategy ) { + case CLAUSE: + renderForUpdateClause( querySpec, forUpdate ); + break; + case FOLLOW_ON: + throw new UnsupportedOperationException( "Follow-on locking for subqueries is not supported" ); + } + forUpdate = null; + } + } + + protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) { + int timeoutMillis = forUpdateClause.getTimeoutMillis(); + LockKind lockKind = LockKind.NONE; + switch ( forUpdateClause.getLockMode() ) { + //noinspection deprecation + case UPGRADE: + timeoutMillis = LockOptions.WAIT_FOREVER; + case PESSIMISTIC_WRITE: + lockKind = LockKind.UPDATE; + break; + case PESSIMISTIC_READ: + lockKind = LockKind.SHARE; + break; + case UPGRADE_NOWAIT: + //noinspection deprecation + case FORCE: + case PESSIMISTIC_FORCE_INCREMENT: + timeoutMillis = LockOptions.NO_WAIT; + lockKind = LockKind.UPDATE; + break; + case UPGRADE_SKIPLOCKED: + timeoutMillis = LockOptions.SKIP_LOCKED; + lockKind = LockKind.UPDATE; + break; + default: + break; + } + if ( lockKind != LockKind.NONE ) { + if ( lockKind == LockKind.SHARE ) { + appendSql( getForShare() ); + if ( forUpdateClause.hasAliases() && getDialect().getReadRowLockStrategy() != RowLockStrategy.NONE ) { + appendSql( " of " ); + forUpdateClause.appendAliases( this ); + } + } + else { + appendSql( getForUpdate() ); + if ( forUpdateClause.hasAliases() && getDialect().getWriteRowLockStrategy() != RowLockStrategy.NONE ) { + appendSql( " of " ); + forUpdateClause.appendAliases( this ); + } + } + appendSql( getForUpdateWithClause() ); + switch ( timeoutMillis ) { + case LockOptions.NO_WAIT: + if ( getDialect().supportsNoWait() ) { + appendSql( getNoWait() ); + } + break; + case LockOptions.SKIP_LOCKED: + if ( getDialect().supportsSkipLocked() ) { + appendSql( getSkipLocked() ); + } + break; + case LockOptions.WAIT_FOREVER: + break; + default: + if ( getDialect().supportsWait() ) { + appendSql( " wait " ); + appendSql( Integer.toString( Math.round( timeoutMillis / 1e3f ) ) ); + } + break; + } + } + } + + private enum LockKind { + NONE, + SHARE, + UPDATE; + } + + protected String getForUpdate() { + return " for update"; + } + + protected String getForShare() { + return " for update"; + } + + protected String getForUpdateWithClause() { + // This is a clause to specify the lock isolation for e.g. Derby + return ""; + } + + protected String getNoWait() { + return " nowait"; + } + + protected String getSkipLocked() { + return " skip locked"; + } + + protected LockMode getEffectiveLockMode(String alias) { + final QueryPart currentQueryPart = getQueryPartStack().getCurrent(); + LockMode lockMode = getLockOptions().getAliasSpecificLockMode( alias ); + if ( currentQueryPart.isRoot() && lockMode == null ) { + lockMode = getLockOptions().getLockMode(); + } + return lockMode == null ? LockMode.NONE : lockMode; + } + + protected int getEffectiveLockTimeout(LockMode lockMode) { + int timeoutMillis = getLockOptions().getTimeOut(); + switch ( lockMode ) { + //noinspection deprecation + case UPGRADE: + timeoutMillis = LockOptions.WAIT_FOREVER; + break; + case UPGRADE_NOWAIT: + //noinspection deprecation + case FORCE: + case PESSIMISTIC_FORCE_INCREMENT: + timeoutMillis = LockOptions.NO_WAIT; + break; + case UPGRADE_SKIPLOCKED: + timeoutMillis = LockOptions.SKIP_LOCKED; + break; + default: + break; + } + return timeoutMillis; + } + + protected boolean hasAggregateFunctions(QuerySpec querySpec) { + return AggregateFunctionChecker.hasAggregateFunctions( querySpec ); + } + + protected LockStrategy determineLockingStrategy( + QuerySpec querySpec, + ForUpdateClause forUpdateClause, + Boolean followOnLocking) { + LockStrategy strategy = LockStrategy.CLAUSE; + if ( !querySpec.getGroupByClauseExpressions().isEmpty() ) { + if ( Boolean.FALSE.equals( followOnLocking ) ) { + throw new IllegalQueryOperationException( "Locking with GROUP BY is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + if ( querySpec.getHavingClauseRestrictions() != null ) { + if ( Boolean.FALSE.equals( followOnLocking ) ) { + throw new IllegalQueryOperationException( "Locking with HAVING is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + if ( querySpec.getSelectClause().isDistinct() ) { + if ( Boolean.FALSE.equals( followOnLocking ) ) { + throw new IllegalQueryOperationException( "Locking with DISTINCT is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + if ( !getDialect().supportsOuterJoinForUpdate() ) { + if ( forUpdateClause.hasAliases() ) { + // Only need to visit the TableGroupJoins for which the alias is registered + querySpec.getFromClause().visitTableGroupJoins( + tableGroupJoin -> { + final TableGroup group = tableGroupJoin.getJoinedGroup(); + if ( forUpdateClause.hasAlias( group.getSourceAlias() ) ) { + if ( tableGroupJoin.getJoinType() != SqlAstJoinType.INNER && !( group instanceof VirtualTableGroup ) ) { + throw new IllegalQueryOperationException( "Locking with OUTER joins is not supported!" ); + } + } + } + ); + } + else { + // Visit TableReferenceJoin and TableGroupJoin to see if all use INNER + querySpec.getFromClause().visitTableJoins( + tableJoin -> { + if ( tableJoin.getJoinType() != SqlAstJoinType.INNER && !( tableJoin.getJoinedNode() instanceof VirtualTableGroup ) ) { + throw new IllegalQueryOperationException( "Locking with OUTER joins is not supported!" ); + } + } + ); + } + } + if ( hasAggregateFunctions( querySpec ) ) { + if ( Boolean.FALSE.equals( followOnLocking ) ) { + throw new IllegalQueryOperationException( "Locking with aggregate functions is not supported!" ); + } + strategy = LockStrategy.FOLLOW_ON; + } + return strategy; + } + protected void visitReturningColumns(MutationStatement mutationStatement) { final List returningColumns = mutationStatement.getReturningColumns(); final int size = returningColumns.size(); @@ -966,8 +1219,13 @@ public abstract class AbstractSqlAstTranslator implemen separator = ", "; } - appendSql( ") as (" ); + appendSql( ") as " ); + if ( cte.getMaterialization() != CteMaterialization.UNDEFINED ) { + renderMaterializationHint( cte.getMaterialization() ); + } + + appendSql( '(' ); cte.getCteDefinition().accept( this ); appendSql( ')' ); @@ -980,6 +1238,10 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( ' ' ); } + protected void renderMaterializationHint(CteMaterialization materialization) { + // No-op by default + } + protected void renderSearchClause(CteStatement cte) { String separator; if ( cte.getSearchClauseKind() != null ) { @@ -1088,7 +1350,9 @@ public abstract class AbstractSqlAstTranslator implemen public void visitQuerySpec(QuerySpec querySpec) { final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering; final boolean needsSelectAliases = this.needsSelectAliases; + final ForUpdateClause forUpdate = this.forUpdate; try { + this.forUpdate = null; final QueryPart currentQueryPart = queryPartStack.getCurrent(); if ( currentQueryPart != null && queryPartForRowNumbering != currentQueryPart ) { this.queryPartForRowNumbering = null; @@ -1124,6 +1388,10 @@ public abstract class AbstractSqlAstTranslator implemen visitHavingClause( querySpec ); visitOrderBy( querySpec.getSortSpecifications() ); visitOffsetFetchClause( querySpec ); + // We render the FOR UPDATE clause in the parent query + if ( queryPartForRowNumbering == null ) { + visitForUpdateClause( querySpec ); + } if ( needsParenthesis ) { appendSql( ")" ); @@ -1131,9 +1399,12 @@ public abstract class AbstractSqlAstTranslator implemen } } finally { - queryPartStack.pop(); + this.queryPartStack.pop(); this.queryPartForRowNumbering = queryPartForRowNumbering; this.needsSelectAliases = needsSelectAliases; + if ( queryPartForRowNumbering == null ) { + this.forUpdate = forUpdate; + } } } @@ -2252,7 +2523,7 @@ public abstract class AbstractSqlAstTranslator implemen } appendSql( " from (" ); queryPart.accept( this ); - appendSql( " ) "); + appendSql( ") "); appendSql( alias ); appendSql( " where " ); final Stack clauseStack = getClauseStack(); @@ -2271,7 +2542,7 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( alias ); appendSql( ".cnt * " ); fetchExpression.accept( this ); - appendSql( " / 100 )" ); + appendSql( " / 100)" ); break; case ROWS_ONLY: appendSql( alias ); @@ -2289,7 +2560,7 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( alias ); appendSql( ".cnt * " ); fetchExpression.accept( this ); - appendSql( " / 100 )" ); + appendSql( " / 100)" ); break; case ROWS_WITH_TIES: appendSql( alias ); @@ -2326,6 +2597,13 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( alias ); appendSql( ".rn" ); } + + // We render the FOR UPDATE clause in the outer query + if ( queryPart instanceof QuerySpec ) { + clauseStack.pop(); + clauseStack.push( Clause.FOR_UPDATE ); + visitForUpdateClause( (QuerySpec) queryPart ); + } } finally { clauseStack.pop(); @@ -2337,6 +2615,21 @@ public abstract class AbstractSqlAstTranslator implemen } } + protected final void withRowNumbering(QueryPart queryPart, Runnable r) { + final QueryPart queryPartForRowNumbering = this.queryPartForRowNumbering; + final boolean needsSelectAliases = this.needsSelectAliases; + try { + this.queryPartForRowNumbering = queryPart; + this.needsSelectAliases = false; + r.run(); + } + finally { + this.queryPartForRowNumbering = queryPartForRowNumbering; + this.needsSelectAliases = needsSelectAliases; + } + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SELECT clause @@ -2378,7 +2671,7 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( "count(*) over () cnt," ); case ROWS_ONLY: renderRowNumber( selectClause, queryPartForRowNumbering ); - appendSql( " rn " ); + appendSql( " rn" ); break; case PERCENT_WITH_TIES: appendSql( "count(*) over () cnt," ); @@ -2451,7 +2744,13 @@ public abstract class AbstractSqlAstTranslator implemen protected List getSortSpecificationsRowNumbering( SelectClause selectClause, QueryPart queryPart) { - final List sortSpecifications = queryPart.getSortSpecifications(); + final List sortSpecifications; + if ( queryPart.hasSortSpecifications() ) { + sortSpecifications = queryPart.getSortSpecifications(); + } + else { + sortSpecifications = Collections.emptyList(); + } if ( selectClause.isDistinct() ) { // When select distinct is used, we need to add all select items to the order by clause final List sqlSelections = new ArrayList<>( selectClause.getSqlSelections() ); @@ -2477,7 +2776,7 @@ public abstract class AbstractSqlAstTranslator implemen final List sortSpecificationsRowNumbering = new ArrayList<>( sqlSelectionsSize + specificationsSize ); sortSpecificationsRowNumbering.addAll( sortSpecifications ); for ( int i = 0; i < sqlSelectionsSize; i++ ) { - sortSpecifications.add( + sortSpecificationsRowNumbering.add( new SortSpecification( new SqlSelectionExpression( sqlSelections.get( i ) ), null, @@ -2604,7 +2903,8 @@ public abstract class AbstractSqlAstTranslator implemen // sqlAppender.appendSql( OPEN_PARENTHESIS ); // } - renderTableReference( tableGroup.getPrimaryTableReference() ); + final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() ); + final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode ); renderTableReferenceJoins( tableGroup ); @@ -2622,6 +2922,15 @@ public abstract class AbstractSqlAstTranslator implemen registerAffectedTable( querySpaces[i] ); } } + if ( !usesLockHint && tableGroup.getSourceAlias() != null && LockMode.READ.lessThan( effectiveLockMode ) ) { + if ( forUpdate == null ) { + forUpdate = new ForUpdateClause( effectiveLockMode ); + } + else { + forUpdate.setLockMode( effectiveLockMode ); + } + forUpdate.applyAliases( getDialect().getLockRowIdentifier( effectiveLockMode ), tableGroup ); + } } protected void renderTableGroup(TableGroup tableGroup, Predicate predicate) { @@ -2632,7 +2941,8 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( '(' ); } - renderTableReference( tableGroup.getPrimaryTableReference() ); + final LockMode effectiveLockMode = getEffectiveLockMode( tableGroup.getSourceAlias() ); + final boolean usesLockHint = renderTableReference( tableGroup.getPrimaryTableReference(), effectiveLockMode ); if ( realTableGroup ) { renderTableReferenceJoins( tableGroup ); @@ -2654,21 +2964,30 @@ public abstract class AbstractSqlAstTranslator implemen registerAffectedTable( querySpaces[i] ); } } + if ( !usesLockHint && tableGroup.getSourceAlias() != null && LockMode.READ.lessThan( effectiveLockMode ) ) { + if ( forUpdate == null ) { + forUpdate = new ForUpdateClause( effectiveLockMode ); + } + else { + forUpdate.setLockMode( effectiveLockMode ); + } + forUpdate.applyAliases( getDialect().getLockRowIdentifier( effectiveLockMode ), tableGroup ); + } } @SuppressWarnings("WeakerAccess") - protected void renderTableReference(TableReference tableReference) { + protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) { appendSql( tableReference.getTableExpression() ); registerAffectedTable( tableReference ); final Clause currentClause = clauseStack.getCurrent(); - if ( !rendersTableReferenceAlias( currentClause ) ) { - return; - } - final String identificationVariable = tableReference.getIdentificationVariable(); - if ( identificationVariable != null ) { - appendSql( getDialect().getTableAliasSeparator() ); - appendSql( identificationVariable ); + if ( rendersTableReferenceAlias( currentClause ) ) { + final String identificationVariable = tableReference.getIdentificationVariable(); + if ( identificationVariable != null ) { + appendSql( getDialect().getTableAliasSeparator() ); + appendSql( identificationVariable ); + } } + return false; } public static boolean rendersTableReferenceAlias(Clause clause) { @@ -2702,11 +3021,11 @@ public abstract class AbstractSqlAstTranslator implemen appendSql( tableJoin.getJoinType().getText() ); appendSql( " join " ); - renderTableReference( tableJoin.getJoinedTableReference() ); + renderTableReference( tableJoin.getJoinedTableReference(), LockMode.NONE ); - if ( tableJoin.getJoinPredicate() != null && !tableJoin.getJoinPredicate().isEmpty() ) { + if ( tableJoin.getPredicate() != null && !tableJoin.getPredicate().isEmpty() ) { appendSql( " on " ); - tableJoin.getJoinPredicate().accept( this ); + tableJoin.getPredicate().accept( this ); } } } @@ -4164,4 +4483,173 @@ public abstract class AbstractSqlAstTranslator implemen return ""; } + protected enum LockStrategy { + CLAUSE, + FOLLOW_ON, + NONE; + } + + protected static class ForUpdateClause { + private LockMode lockMode; + private int timeoutMillis = LockOptions.WAIT_FOREVER; + private Map keyColumnNames; + private Map aliases; + + public ForUpdateClause(LockMode lockMode) { + this.lockMode = lockMode; + } + + public ForUpdateClause() { + this.lockMode = LockMode.NONE; + } + + public void applyAliases(RowLockStrategy lockIdentifier, QuerySpec querySpec) { + if ( lockIdentifier != RowLockStrategy.NONE ) { + querySpec.getFromClause().visitTableGroups( tableGroup -> applyAliases( lockIdentifier, tableGroup ) ); + } + } + + public void applyAliases(RowLockStrategy lockIdentifier, TableGroup tableGroup) { + if ( aliases != null && lockIdentifier != RowLockStrategy.NONE ) { + final String tableAlias = tableGroup.getPrimaryTableReference().getIdentificationVariable(); + if ( aliases.containsKey( tableGroup.getSourceAlias() ) ) { + addAlias( tableGroup.getSourceAlias(), tableAlias ); + if ( lockIdentifier == RowLockStrategy.COLUMN ) { + addKeyColumnNames( tableGroup ); + } + } + } + } + + public LockMode getLockMode() { + return lockMode; + } + + public void setLockMode(LockMode lockMode) { + if ( this.lockMode != LockMode.NONE && lockMode != this.lockMode ) { + throw new QueryException( "mixed LockModes" ); + } + this.lockMode = lockMode; + } + + public void addKeyColumnNames(TableGroup tableGroup) { + final String[] keyColumnNames = determineKeyColumnNames( tableGroup.getModelPart() ); + if ( keyColumnNames == null ) { + throw new IllegalArgumentException( "Can't lock table group: " + tableGroup ); + } + addKeyColumnNames( + tableGroup.getSourceAlias(), + tableGroup.getPrimaryTableReference().getIdentificationVariable(), + keyColumnNames + ); + } + + private String[] determineKeyColumnNames(ModelPart modelPart) { + if ( modelPart instanceof Loadable ) { + return ( (Loadable) modelPart ).getIdentifierColumnNames(); + } + else if ( modelPart instanceof PluralAttributeMapping ) { + return ((PluralAttributeMapping) modelPart).getCollectionDescriptor().getKeyColumnAliases( null ); + } + else if ( modelPart instanceof EntityAssociationMapping ) { + return determineKeyColumnNames( ( (EntityAssociationMapping) modelPart ).getAssociatedEntityMappingType() ); + } + return null; + } + + private void addKeyColumnNames(String alias, String tableAlias, String[] keyColumnNames) { + if ( this.keyColumnNames == null ) { + this.keyColumnNames = new HashMap<>(); + } + this.keyColumnNames.put( tableAlias, keyColumnNames ); + } + + public boolean hasAlias(String alias) { + return aliases != null && aliases.containsKey( alias ); + } + + private void addAlias(String alias, String tableAlias) { + if ( aliases == null ) { + aliases = new HashMap<>(); + } + aliases.put( alias, tableAlias ); + } + + public int getTimeoutMillis() { + return timeoutMillis; + } + + public boolean hasAliases() { + return aliases != null; + } + + public void appendAliases(SqlAppender appender) { + if ( aliases == null ) { + return; + } + if ( keyColumnNames != null ) { + boolean first = true; + for ( String tableAlias : aliases.values() ) { + final String[] keyColumns = keyColumnNames.get( tableAlias ); //use the id column alias + if ( keyColumns == null ) { + throw new IllegalArgumentException( "alias not found: " + tableAlias ); + } + for ( String keyColumn : keyColumns ) { + if ( first ) { + first = false; + } + else { + appender.appendSql( ", " ); + } + appender.appendSql( tableAlias ); + appender.appendSql( '.' ); + appender.appendSql( keyColumn ); + } + } + } + else { + boolean first = true; + for ( String tableAlias : aliases.values() ) { + if ( first ) { + first = false; + } + else { + appender.appendSql( ", " ); + } + appender.appendSql( tableAlias ); + } + } + } + + public String getAliases() { + if ( aliases == null ) { + return null; + } + return aliases.toString(); + } + + public void merge(LockOptions lockOptions) { + if ( lockOptions != null ) { + LockMode upgradeType = LockMode.NONE; + if ( lockOptions.getAliasLockCount() == 0 ) { + upgradeType = lockOptions.getLockMode(); + } + else { + for ( Map.Entry entry : lockOptions.getAliasSpecificLocks() ) { + final LockMode lockMode = entry.getValue(); + if ( LockMode.READ.lessThan( lockMode ) ) { + addAlias( entry.getKey(), null ); + if ( upgradeType != LockMode.NONE && lockMode != upgradeType ) { + throw new QueryException( "mixed LockModes" ); + } + upgradeType = lockMode; + } + } + } + lockMode = upgradeType; + timeoutMillis = lockOptions.getTimeOut(); + } + } + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AggregateFunctionChecker.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AggregateFunctionChecker.java new file mode 100644 index 0000000000..66603a5ce7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AggregateFunctionChecker.java @@ -0,0 +1,363 @@ +/* + * 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 org.hibernate.query.sqm.tree.expression.Conversion; +import org.hibernate.sql.ast.SqlAstWalker; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.expression.AggregateFunctionExpression; +import org.hibernate.sql.ast.tree.expression.Any; +import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression; +import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; +import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression; +import org.hibernate.sql.ast.tree.expression.CastTarget; +import org.hibernate.sql.ast.tree.expression.Collate; +import org.hibernate.sql.ast.tree.expression.ColumnReference; +import org.hibernate.sql.ast.tree.expression.Distinct; +import org.hibernate.sql.ast.tree.expression.Duration; +import org.hibernate.sql.ast.tree.expression.DurationUnit; +import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral; +import org.hibernate.sql.ast.tree.expression.Every; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.ExtractUnit; +import org.hibernate.sql.ast.tree.expression.Format; +import org.hibernate.sql.ast.tree.expression.FunctionExpression; +import org.hibernate.sql.ast.tree.expression.JdbcLiteral; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; +import org.hibernate.sql.ast.tree.expression.NullnessLiteral; +import org.hibernate.sql.ast.tree.expression.QueryLiteral; +import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; +import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression; +import org.hibernate.sql.ast.tree.expression.SqlTuple; +import org.hibernate.sql.ast.tree.expression.Star; +import org.hibernate.sql.ast.tree.expression.Summarization; +import org.hibernate.sql.ast.tree.expression.TrimSpecification; +import org.hibernate.sql.ast.tree.expression.UnaryOperation; +import org.hibernate.sql.ast.tree.from.FromClause; +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; +import org.hibernate.sql.ast.tree.insert.InsertStatement; +import org.hibernate.sql.ast.tree.predicate.BetweenPredicate; +import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; +import org.hibernate.sql.ast.tree.predicate.ExistsPredicate; +import org.hibernate.sql.ast.tree.predicate.FilterPredicate; +import org.hibernate.sql.ast.tree.predicate.GroupedPredicate; +import org.hibernate.sql.ast.tree.predicate.InListPredicate; +import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; +import org.hibernate.sql.ast.tree.predicate.Junction; +import org.hibernate.sql.ast.tree.predicate.LikePredicate; +import org.hibernate.sql.ast.tree.predicate.NegatedPredicate; +import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; +import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate; +import org.hibernate.sql.ast.tree.select.QueryGroup; +import org.hibernate.sql.ast.tree.select.QueryPart; +import org.hibernate.sql.ast.tree.select.QuerySpec; +import org.hibernate.sql.ast.tree.select.SelectClause; +import org.hibernate.sql.ast.tree.select.SelectStatement; +import org.hibernate.sql.ast.tree.select.SortSpecification; +import org.hibernate.sql.ast.tree.update.Assignment; +import org.hibernate.sql.ast.tree.update.UpdateStatement; + +/** + * A simple walker that checks for aggregate functions. + * + * @author Christian Beikov + */ +class AggregateFunctionChecker implements SqlAstWalker { + + private static final AggregateFunctionChecker INSTANCE = new AggregateFunctionChecker(); + + private static class AggregateFunctionException extends RuntimeException {} + + public static boolean hasAggregateFunctions(QuerySpec querySpec) { + try { + querySpec.getSelectClause().accept( INSTANCE ); + querySpec.visitSortSpecifications( INSTANCE::visitSortSpecification ); + return false; + } + catch (AggregateFunctionException ex) { + return true; + } + } + + @Override + public void visitAny(Any any) { + throw new AggregateFunctionException(); + } + + @Override + public void visitEvery(Every every) { + throw new AggregateFunctionException(); + } + + @Override + public void visitSelfRenderingExpression(SelfRenderingExpression expression) { + if ( expression instanceof AggregateFunctionExpression ) { + throw new AggregateFunctionException(); + } + else if ( expression instanceof FunctionExpression ) { + for ( SqlAstNode argument : ( (FunctionExpression) expression ).getArguments() ) { + argument.accept( this ); + } + } + } + + @Override + public void visitSortSpecification(SortSpecification sortSpecification) { + sortSpecification.getSortExpression().accept( this ); + } + + @Override + public void visitSelectClause(SelectClause selectClause) { + for ( SqlSelection sqlSelection : selectClause.getSqlSelections() ) { + sqlSelection.accept( this ); + } + } + + @Override + public void visitSqlSelection(SqlSelection sqlSelection) { + sqlSelection.getExpression().accept( this ); + } + + @Override + public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) { + arithmeticExpression.getLeftHandOperand().accept( this ); + arithmeticExpression.getRightHandOperand().accept( this ); + } + + @Override + public void visitCaseSearchedExpression(CaseSearchedExpression caseSearchedExpression) { + for ( CaseSearchedExpression.WhenFragment whenFragment : caseSearchedExpression.getWhenFragments() ) { + whenFragment.getPredicate().accept( this ); + whenFragment.getResult().accept( this ); + } + if ( caseSearchedExpression.getOtherwise() != null ) { + caseSearchedExpression.getOtherwise().accept( this ); + } + } + + @Override + public void visitCaseSimpleExpression(CaseSimpleExpression caseSimpleExpression) { + caseSimpleExpression.getFixture().accept( this ); + for ( CaseSimpleExpression.WhenFragment whenFragment : caseSimpleExpression.getWhenFragments() ) { + whenFragment.getCheckValue().accept( this ); + whenFragment.getResult().accept( this ); + } + if ( caseSimpleExpression.getOtherwise() != null ) { + caseSimpleExpression.getOtherwise().accept( this ); + } + } + + @Override + public void visitTuple(SqlTuple tuple) { + for ( Expression expression : tuple.getExpressions() ) { + expression.accept( this ); + } + } + + @Override + public void visitCollate(Collate collate) { + collate.getExpression().accept( this ); + } + + @Override + public void visitUnaryOperationExpression(UnaryOperation unaryOperationExpression) { + unaryOperationExpression.getOperand().accept( this ); + } + + @Override + public void visitBetweenPredicate(BetweenPredicate betweenPredicate) { + betweenPredicate.getExpression().accept( this ); + betweenPredicate.getLowerBound().accept( this ); + betweenPredicate.getUpperBound().accept( this ); + } + + @Override + public void visitGroupedPredicate(GroupedPredicate groupedPredicate) { + groupedPredicate.getSubPredicate().accept( this ); + } + + @Override + public void visitJunction(Junction junction) { + for ( Predicate predicate : junction.getPredicates() ) { + predicate.accept( this ); + } + } + + @Override + public void visitLikePredicate(LikePredicate likePredicate) { + likePredicate.getMatchExpression().accept( this ); + if ( likePredicate.getEscapeCharacter() != null ) { + likePredicate.getEscapeCharacter().accept( this ); + } + likePredicate.getPattern().accept( this ); + } + + @Override + public void visitNegatedPredicate(NegatedPredicate negatedPredicate) { + negatedPredicate.getPredicate().accept( this ); + } + + @Override + public void visitNullnessPredicate(NullnessPredicate nullnessPredicate) { + nullnessPredicate.getExpression().accept( this ); + } + + @Override + public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) { + comparisonPredicate.getLeftHandExpression().accept( this ); + comparisonPredicate.getRightHandExpression().accept( this ); + } + + @Override + public void visitSelfRenderingPredicate(SelfRenderingPredicate selfRenderingPredicate) { + selfRenderingPredicate.getSelfRenderingExpression().accept( this ); + } + + // --- to ignore --- + + @Override + public void visitSelectStatement(SelectStatement statement) { + } + + @Override + public void visitDeleteStatement(DeleteStatement statement) { + } + + @Override + public void visitUpdateStatement(UpdateStatement statement) { + } + + @Override + public void visitInsertStatement(InsertStatement statement) { + } + + @Override + public void visitAssignment(Assignment assignment) { + } + + @Override + public void visitQueryGroup(QueryGroup queryGroup) { + } + + @Override + public void visitQuerySpec(QuerySpec querySpec) { + } + + @Override + public void visitColumnReference(ColumnReference columnReference) { + } + + @Override + public void visitExtractUnit(ExtractUnit extractUnit) { + } + + @Override + public void visitFormat(Format format) { + } + + @Override + public void visitDistinct(Distinct distinct) { + } + + @Override + public void visitStar(Star star) { + } + + @Override + public void visitOffsetFetchClause(QueryPart querySpec) { + } + + @Override + public void visitTrimSpecification(TrimSpecification trimSpecification) { + } + + @Override + public void visitCastTarget(CastTarget castTarget) { + } + + @Override + public void visitDurationUnit(DurationUnit durationUnit) { + } + + @Override + public void visitDuration(Duration duration) { + } + + @Override + public void visitConversion(Conversion conversion) { + } + + @Override + public void visitInListPredicate(InListPredicate inListPredicate) { + } + + @Override + public void visitInSubQueryPredicate(InSubQueryPredicate inSubQueryPredicate) { + } + + @Override + public void visitExistsPredicate(ExistsPredicate existsPredicate) { + } + + @Override + public void visitFilterPredicate(FilterPredicate filterPredicate) { + } + + @Override + public void visitParameter(JdbcParameter jdbcParameter) { + } + + @Override + public void visitJdbcLiteral(JdbcLiteral jdbcLiteral) { + } + + @Override + public void visitQueryLiteral(QueryLiteral queryLiteral) { + } + + @Override + public void visitNullnessLiteral(NullnessLiteral nullnessLiteral) { + } + + @Override + public void visitSummarization(Summarization every) { + } + + @Override + public void visitEntityTypeLiteral(EntityTypeLiteral expression) { + } + + @Override + public void visitSqlSelectionExpression(SqlSelectionExpression expression) { + } + + @Override + public void visitTableReference(TableReference tableReference) { + } + + @Override + public void visitTableReferenceJoin(TableReferenceJoin tableReferenceJoin) { + } + + @Override + public void visitFromClause(FromClause fromClause) { + } + + @Override + public void visitTableGroup(TableGroup tableGroup) { + } + + @Override + public void visitTableGroupJoin(TableGroupJoin tableGroupJoin) { + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstCreationState.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstCreationState.java index 2470c0be8c..2426a7103d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstCreationState.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlAstCreationState.java @@ -24,5 +24,5 @@ public interface SqlAstCreationState { SqlAliasBaseGenerator getSqlAliasBaseGenerator(); - LockMode determineLockMode(String identificationVariable); + void registerLockMode(String identificationVariable, LockMode explicitLockMode); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteMaterialization.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteMaterialization.java new file mode 100644 index 0000000000..45c48cfdb7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteMaterialization.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * The kind of materialization that should be used for a CTE. + * This is a hint that like e.g. for PostgreSQL which allows to control this. + * + * @author Christian Beikov + */ +public enum CteMaterialization { + /** + * It is undefined if the CTE should be materialized. + */ + UNDEFINED, + /** + * Materialize the CTE if possible. + */ + MATERIALIZED, + /** + * Do not materialize the CTE if possible. + */ + NOT_MATERIALIZED; +} 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 index dce03b55ed..8e5499a21c 100644 --- 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 @@ -19,6 +19,7 @@ import org.hibernate.sql.ast.tree.Statement; public class CteStatement { private final CteTable cteTable; private final Statement cteDefinition; + private final CteMaterialization materialization; private final CteSearchClauseKind searchClauseKind; private final List searchBySpecifications; private final List cycleColumns; @@ -27,8 +28,13 @@ public class CteStatement { private final char noCycleValue; public CteStatement(CteTable cteTable, Statement cteDefinition) { + this( cteTable, cteDefinition, CteMaterialization.UNDEFINED ); + } + + public CteStatement(CteTable cteTable, Statement cteDefinition, CteMaterialization materialization) { this.cteDefinition = cteDefinition; this.cteTable = cteTable; + this.materialization = materialization; this.searchClauseKind = null; this.searchBySpecifications = null; this.cycleColumns = null; @@ -40,6 +46,7 @@ public class CteStatement { public CteStatement( CteTable cteTable, Statement cteDefinition, + CteMaterialization materialization, CteSearchClauseKind searchClauseKind, List searchBySpecifications, List cycleColumns, @@ -48,6 +55,7 @@ public class CteStatement { char noCycleValue) { this.cteTable = cteTable; this.cteDefinition = cteDefinition; + this.materialization = materialization; this.searchClauseKind = searchClauseKind; this.searchBySpecifications = searchBySpecifications; this.cycleColumns = cycleColumns; @@ -64,6 +72,10 @@ public class CteStatement { return cteDefinition; } + public CteMaterialization getMaterialization() { + return materialization; + } + public CteSearchClauseKind getSearchClauseKind() { return searchClauseKind; } 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 index 052decf875..b1d137a498 100644 --- 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 @@ -80,7 +80,7 @@ public class CteTable { final StandardTableGroup tableValueCtorGroup = new StandardTableGroup( new NavigablePath( "cte" ), null, - LockMode.NONE, + null, tableValueConstructorReference, null, sessionFactory 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 index da0911937e..9bd7fa50c3 100644 --- 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 @@ -41,8 +41,8 @@ public class CteTableGroup implements TableGroup { } @Override - public LockMode getLockMode() { - return LockMode.NONE; + public String getSourceAlias() { + return null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/AggregateFunctionExpression.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/AggregateFunctionExpression.java new file mode 100644 index 0000000000..1e7b830992 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/AggregateFunctionExpression.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.sql.ast.tree.expression; + +import org.hibernate.sql.ast.tree.predicate.Predicate; + +/** + * Models an aggregate function expression at the SQL AST level. + * + * @author Christian Beikov + */ +public interface AggregateFunctionExpression extends FunctionExpression { + + Predicate getFilter(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Distinct.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Distinct.java index f822a8f5a1..9aca54648d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Distinct.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Distinct.java @@ -16,7 +16,7 @@ import org.hibernate.sql.ast.tree.SqlAstNode; * @author Gavin King */ public class Distinct implements Expression, SqlExpressable, SqlAstNode { - private Expression expression; + private final Expression expression; public Distinct(Expression expression) { this.expression = expression; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Duration.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Duration.java index 55d071a0b8..4177bf6dea 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Duration.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Duration.java @@ -24,7 +24,7 @@ import org.hibernate.sql.results.graph.basic.BasicResult; * @author Gavin King */ public class Duration implements Expression, DomainResultProducer { - private Expression magnitude; + private final Expression magnitude; private final TemporalUnit unit; private final BasicValuedMapping type; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/DurationUnit.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/DurationUnit.java index 26da7ec3c4..036432b391 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/DurationUnit.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/DurationUnit.java @@ -22,8 +22,8 @@ import org.hibernate.sql.ast.tree.SqlAstNode; * @author Gavin King */ public class DurationUnit implements Expression, SqlAstNode { - private TemporalUnit unit; - private BasicValuedMapping type; + private final TemporalUnit unit; + private final BasicValuedMapping type; public DurationUnit(TemporalUnit unit, BasicValuedMapping type) { this.unit = unit; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java index bb2dc697a2..310a2aebd3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java @@ -23,7 +23,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase; public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifier implements TableGroup { private final NavigablePath navigablePath; private final TableGroupProducer producer; - private final LockMode lockMode; + private final String sourceAlias; private final SqlAliasBase sqlAliasBase; private List tableGroupJoins; @@ -35,24 +35,24 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie public AbstractTableGroup( NavigablePath navigablePath, TableGroupProducer producer, - LockMode lockMode, + String sourceAlias, SqlAliasBase sqlAliasBase, SessionFactoryImplementor sessionFactory) { - this( navigablePath, producer, lockMode, sqlAliasBase, false, sessionFactory ); + this( navigablePath, producer, sourceAlias, sqlAliasBase, false, sessionFactory ); } @SuppressWarnings("WeakerAccess") public AbstractTableGroup( NavigablePath navigablePath, TableGroupProducer producer, - LockMode lockMode, + String sourceAlias, SqlAliasBase sqlAliasBase, boolean isInnerJoinPossible, SessionFactoryImplementor sessionFactory) { super(); this.navigablePath = navigablePath; this.producer = producer; - this.lockMode = lockMode; + this.sourceAlias = sourceAlias; this.sqlAliasBase = sqlAliasBase; this.isInnerJoinPossible = isInnerJoinPossible; this.sessionFactory = sessionFactory; @@ -83,8 +83,8 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie } @Override - public LockMode getLockMode() { - return lockMode; + public String getSourceAlias() { + return sourceAlias; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java index 13d63df79c..bf010f21d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java @@ -57,8 +57,8 @@ public class CompositeTableGroup implements VirtualTableGroup { } @Override - public LockMode getLockMode() { - return underlyingTableGroup.getLockMode(); + public String getSourceAlias() { + return underlyingTableGroup.getSourceAlias(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java index 176993e982..e756fb46f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java @@ -39,7 +39,7 @@ public class CorrelatedTableGroup extends AbstractTableGroup { super( correlatedTableGroup.getNavigablePath(), (TableGroupProducer) correlatedTableGroup.getExpressionType(), - LockMode.NONE, + null, sqlAliasBase, sessionFactory ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java index b689d5c291..a8f98ecb4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FromClause.java @@ -9,6 +9,7 @@ package org.hibernate.sql.ast.tree.from; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import java.util.function.Function; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.sql.ast.SqlAstWalker; @@ -42,6 +43,76 @@ public class FromClause implements SqlAstNode { roots.forEach( action ); } + public void visitTableGroups(Consumer action) { + for ( int i = 0; i < roots.size(); i++ ) { + visitTableGroups( roots.get( i ), action ); + } + } + + private void visitTableGroups(TableGroup tableGroup, Consumer action) { + action.accept( tableGroup ); + final List tableGroupJoins = tableGroup.getTableGroupJoins(); + for ( int i = 0; i < tableGroupJoins.size(); i++ ) { + visitTableGroups( tableGroupJoins.get( i ).getJoinedGroup(), action ); + } + } + + public T queryTableGroups(Function action) { + for ( int i = 0; i < roots.size(); i++ ) { + final T result = queryTableGroups( roots.get( i ), action ); + if ( result != null ) { + return result; + } + } + return null; + } + + private T queryTableGroups(TableGroup tableGroup, Function action) { + final T result = action.apply( tableGroup ); + if ( result != null ) { + return result; + } + final List tableGroupJoins = tableGroup.getTableGroupJoins(); + for ( int i = 0; i < tableGroupJoins.size(); i++ ) { + final T nestedResult = queryTableGroups( tableGroupJoins.get( i ).getJoinedGroup(), action ); + if ( nestedResult != null ) { + return nestedResult; + } + } + return null; + } + + public void visitTableJoins(Consumer action) { + for ( int i = 0; i < roots.size(); i++ ) { + visitTableJoins( roots.get( i ), action ); + } + } + + private void visitTableJoins(TableGroup tableGroup, Consumer action) { + tableGroup.getTableReferenceJoins().forEach( action ); + final List tableGroupJoins = tableGroup.getTableGroupJoins(); + for ( int i = 0; i < tableGroupJoins.size(); i++ ) { + final TableGroupJoin tableGroupJoin = tableGroupJoins.get( i ); + action.accept( tableGroupJoin ); + visitTableJoins( tableGroupJoin.getJoinedGroup(), action ); + } + } + + public void visitTableGroupJoins(Consumer action) { + for ( int i = 0; i < roots.size(); i++ ) { + visitTableGroupJoins( roots.get( i ), action ); + } + } + + private void visitTableGroupJoins(TableGroup tableGroup, Consumer action) { + final List tableGroupJoins = tableGroup.getTableGroupJoins(); + for ( int i = 0; i < tableGroupJoins.size(); i++ ) { + final TableGroupJoin tableGroupJoin = tableGroupJoins.get( i ); + action.accept( tableGroupJoin ); + visitTableGroupJoins( tableGroupJoin.getJoinedGroup(), action ); + } + } + @Override public void accept(SqlAstWalker sqlTreeWalker) { sqlTreeWalker.visitFromClause( this ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java index 81cdaa384a..c3fde913be 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java @@ -25,7 +25,7 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements private final NavigablePath navigablePath; private final TableGroupProducer producer; - private final LockMode lockMode; + private final String sourceAlias; private final SqlAliasBase sqlAliasBase; private final SessionFactoryImplementor sessionFactory; private final Supplier tableGroupSupplier; @@ -39,13 +39,13 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements Supplier tableGroupSupplier, Predicate navigablePathChecker, TableGroupProducer tableGroupProducer, - LockMode lockMode, + String sourceAlias, SqlAliasBase sqlAliasBase, SessionFactoryImplementor sessionFactory, TableGroup parentTableGroup) { this.navigablePath = navigablePath; this.producer = tableGroupProducer; - this.lockMode = lockMode; + this.sourceAlias = sourceAlias; this.sqlAliasBase = sqlAliasBase; this.tableGroupSupplier = tableGroupSupplier; this.navigablePathChecker = navigablePathChecker; @@ -133,8 +133,8 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements } @Override - public LockMode getLockMode() { - return lockMode; + public String getSourceAlias() { + return sourceAlias; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java index bb0c190c9d..ca31241de1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java @@ -78,8 +78,8 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup { } @Override - public LockMode getLockMode() { - return LockMode.WRITE; + public String getSourceAlias() { + return null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java index c43f54b970..a4d720ab35 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java @@ -29,7 +29,6 @@ public interface RootTableGroupProducer extends TableGroupProducer, ModelPartCon TableGroup createRootTableGroup( NavigablePath navigablePath, String explicitSourceAlias, - LockMode lockMode, Supplier> additionalPredicateCollectorAccess, SqlAstCreationState creationState, SqlAstCreationContext creationContext); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java index 0ead9b5dbb..f124124d4d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java @@ -33,11 +33,11 @@ public class StandardTableGroup extends AbstractTableGroup { public StandardTableGroup( NavigablePath navigablePath, TableGroupProducer tableGroupProducer, - LockMode lockMode, + String sourceAlias, TableReference primaryTableReference, SqlAliasBase sqlAliasBase, SessionFactoryImplementor sessionFactory) { - super( navigablePath, tableGroupProducer, lockMode, sqlAliasBase, sessionFactory ); + super( navigablePath, tableGroupProducer, sourceAlias, sqlAliasBase, sessionFactory ); this.primaryTableReference = primaryTableReference; this.realTableGroup = false; this.fetched = false; @@ -56,14 +56,14 @@ public class StandardTableGroup extends AbstractTableGroup { public StandardTableGroup( NavigablePath navigablePath, TableGroupProducer tableGroupProducer, - LockMode lockMode, + String sourceAlias, TableReference primaryTableReference, boolean realTableGroup, SqlAliasBase sqlAliasBase, Predicate tableReferenceJoinNameChecker, BiFunction tableReferenceJoinCreator, SessionFactoryImplementor sessionFactory) { - super( navigablePath, tableGroupProducer, lockMode, sqlAliasBase, sessionFactory ); + super( navigablePath, tableGroupProducer, sourceAlias, sqlAliasBase, sessionFactory ); this.primaryTableReference = primaryTableReference; this.realTableGroup = realTableGroup; this.fetched = false; @@ -76,14 +76,14 @@ public class StandardTableGroup extends AbstractTableGroup { NavigablePath navigablePath, TableGroupProducer tableGroupProducer, boolean fetched, - LockMode lockMode, + String sourceAlias, TableReference primaryTableReference, boolean realTableGroup, SqlAliasBase sqlAliasBase, Predicate tableReferenceJoinNameChecker, BiFunction tableReferenceJoinCreator, SessionFactoryImplementor sessionFactory) { - super( navigablePath, tableGroupProducer, lockMode, sqlAliasBase, sessionFactory ); + super( navigablePath, tableGroupProducer, sourceAlias, sqlAliasBase, sessionFactory ); this.primaryTableReference = primaryTableReference; this.realTableGroup = realTableGroup; this.fetched = fetched; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java index bb6709bf17..ed598da0c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java @@ -38,7 +38,7 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat ModelPartContainer getModelPart(); - LockMode getLockMode(); + String getSourceAlias(); List getTableGroupJoins(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java index 20042fbfef..82f72800d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java @@ -19,7 +19,7 @@ import org.hibernate.query.sqm.sql.internal.DomainResultProducer; /** * @author Steve Ebersole */ -public class TableGroupJoin implements SqlAstNode, DomainResultProducer { +public class TableGroupJoin implements TableJoin, DomainResultProducer { private final NavigablePath navigablePath; private final SqlAstJoinType sqlAstJoinType; private final TableGroup joinedGroup; @@ -44,6 +44,7 @@ public class TableGroupJoin implements SqlAstNode, DomainResultProducer { this( navigablePath, sqlAstJoinType, joinedGroup, null ); } + @Override public SqlAstJoinType getJoinType() { return sqlAstJoinType; } @@ -52,6 +53,12 @@ public class TableGroupJoin implements SqlAstNode, DomainResultProducer { return joinedGroup; } + @Override + public SqlAstNode getJoinedNode() { + return joinedGroup; + } + + @Override public Predicate getPredicate() { return predicate; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java index 9940b0e9a2..31cc091983 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java @@ -27,7 +27,6 @@ public interface TableGroupJoinProducer extends TableGroupProducer { String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAstCreationState creationState) { return createTableGroupJoin( navigablePath, @@ -35,7 +34,6 @@ public interface TableGroupJoinProducer extends TableGroupProducer { explicitSourceAlias, sqlAstJoinType, fetched, - lockMode, creationState.getSqlAliasBaseGenerator(), creationState.getSqlExpressionResolver(), creationState.getCreationContext() @@ -51,7 +49,6 @@ public interface TableGroupJoinProducer extends TableGroupProducer { String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, - LockMode lockMode, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableJoin.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableJoin.java new file mode 100644 index 0000000000..a28b9793fa --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableJoin.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.sql.ast.tree.from; + +import org.hibernate.sql.ast.SqlAstJoinType; +import org.hibernate.sql.ast.tree.SqlAstNode; +import org.hibernate.sql.ast.tree.predicate.Predicate; + +/** + * The commonalities between {@link TableGroupJoin} and {@link TableReferenceJoin}. + * + * @author Christian Beikov + */ +public interface TableJoin extends SqlAstNode { + SqlAstJoinType getJoinType(); + Predicate getPredicate(); + SqlAstNode getJoinedNode(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceJoin.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceJoin.java index 87c5aee600..22488ed6b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReferenceJoin.java @@ -18,7 +18,7 @@ import org.hibernate.sql.ast.tree.predicate.PredicateContainer; * * @author Steve Ebersole */ -public class TableReferenceJoin implements SqlAstNode, PredicateContainer { +public class TableReferenceJoin implements TableJoin, PredicateContainer { private final SqlAstJoinType sqlAstJoinType; private final TableReference joinedTableBinding; private Predicate predicate; @@ -35,6 +35,7 @@ public class TableReferenceJoin implements SqlAstNode, PredicateContainer { // } } + @Override public SqlAstJoinType getJoinType() { return sqlAstJoinType; } @@ -43,7 +44,13 @@ public class TableReferenceJoin implements SqlAstNode, PredicateContainer { return joinedTableBinding; } - public Predicate getJoinPredicate() { + @Override + public SqlAstNode getJoinedNode() { + return joinedTableBinding; + } + + @Override + public Predicate getPredicate() { return predicate; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java index 2380441738..15d6f8a1df 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java @@ -25,15 +25,18 @@ public class UnionTableGroup implements VirtualTableGroup { private List tableGroupJoins; private final UnionSubclassEntityPersister modelPart; + private final String sourceAlias; private final TableReference tableReference; public UnionTableGroup( NavigablePath navigablePath, TableReference tableReference, - UnionSubclassEntityPersister modelPart) { + UnionSubclassEntityPersister modelPart, + String sourceAlias) { this.navigablePath = navigablePath; this.tableReference = tableReference; this.modelPart = modelPart; + this.sourceAlias = sourceAlias; } @Override @@ -57,8 +60,8 @@ public class UnionTableGroup implements VirtualTableGroup { } @Override - public LockMode getLockMode() { - return LockMode.NONE; + public String getSourceAlias() { + return sourceAlias; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QueryPart.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QueryPart.java index 30b92d2d2b..1d4ad7cabf 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QueryPart.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QueryPart.java @@ -59,7 +59,7 @@ public abstract class QueryPart implements SqlAstNode, Expression, DomainResultP return sortSpecifications; } - void visitSortSpecifications(Consumer consumer) { + public void visitSortSpecifications(Consumer consumer) { if ( sortSpecifications != null ) { sortSpecifications.forEach( consumer ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/CallbackImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/CallbackImpl.java new file mode 100644 index 0000000000..da9b8e9b45 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/CallbackImpl.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.sql.exec.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.loader.ast.spi.AfterLoadAction; +import org.hibernate.persister.entity.Loadable; +import org.hibernate.sql.exec.spi.Callback; + +/** + * @author Christian Beikov + */ +public class CallbackImpl implements Callback { + + private final List afterLoadActions; + + public CallbackImpl() { + this.afterLoadActions = new ArrayList<>( 1 ); + } + + @Override + public void registerAfterLoadAction(AfterLoadAction afterLoadAction) { + afterLoadActions.add( afterLoadAction ); + } + + @Override + public void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { + for ( int i = 0; i < afterLoadActions.size(); i++ ) { + afterLoadActions.get( i ).afterLoad( session, entity, persister ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java index 834e49e1bb..ca8918cd7b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java @@ -7,7 +7,6 @@ package org.hibernate.sql.exec.internal; import java.sql.PreparedStatement; -import java.util.ArrayList; import java.util.List; import java.util.Spliterator; import java.util.Spliterators; @@ -16,17 +15,18 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.hibernate.CacheMode; +import org.hibernate.LockOptions; import org.hibernate.ScrollMode; import org.hibernate.cache.spi.QueryKey; import org.hibernate.cache.spi.QueryResultsCache; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.loader.ast.spi.AfterLoadAction; import org.hibernate.query.internal.ScrollableResultsIterator; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.sql.exec.SqlExecLogger; import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcLockStrategy; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.exec.spi.JdbcSelectExecutor; @@ -166,16 +166,17 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { Function statementCreator, ResultsConsumer resultsConsumer) { + final DeferredResultSetAccess deferredResultSetAccess = new DeferredResultSetAccess( + jdbcSelect, + jdbcParameterBindings, + executionContext, + statementCreator + ); final JdbcValues jdbcValues = resolveJdbcValuesSource( jdbcSelect, resultsConsumer.canResultsBeCached(), executionContext, - new DeferredResultSetAccess( - jdbcSelect, - jdbcParameterBindings, - executionContext, - statementCreator - ) + deferredResultSetAccess ); /* @@ -209,11 +210,15 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { executionContext::registerLoadingEntityEntry ); - final List afterLoadActions = new ArrayList<>(); - final RowReader rowReader = ResultsHelper.createRowReader( - executionContext.getSession().getFactory(), - afterLoadActions::add, + executionContext, + // If follow on locking is used, we must omit the lock options here, + // because these lock options are only for Initializers. + // If we wouldn't omit this, the follow on lock requests would be no-ops, + // because the EntityEntrys would already have the desired lock mode + deferredResultSetAccess.usesFollowOnLocking() + ? LockOptions.NONE + : executionContext.getQueryOptions().getLockOptions(), rowTransformer, jdbcValues ); @@ -234,11 +239,6 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { rowReader ); - for ( AfterLoadAction afterLoadAction : afterLoadActions ) { - // todo (6.0) : see notes on - afterLoadAction.afterLoad( executionContext.getSession(), null, null ); - } - return result; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/AbstractJdbcOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/AbstractJdbcOperation.java new file mode 100644 index 0000000000..6811aed571 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/AbstractJdbcOperation.java @@ -0,0 +1,100 @@ +/* + * 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; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.internal.FilterJdbcParameter; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; + +/** + * Executable JDBC command + * + * @author Steve Ebersole + */ +public class AbstractJdbcOperation implements JdbcOperation { + protected final String sql; + protected final List parameterBinders; + protected final Set affectedTableNames; + protected final Set filterJdbcParameters; + protected final Map appliedParameters; + + public AbstractJdbcOperation( + String sql, + List parameterBinders, + Set affectedTableNames, + Set filterJdbcParameters) { + this( + sql, + parameterBinders, + affectedTableNames, + filterJdbcParameters, + Collections.emptyMap() + ); + } + + public AbstractJdbcOperation( + String sql, + List parameterBinders, + Set affectedTableNames, + Set filterJdbcParameters, + Map appliedParameters) { + this.sql = sql; + this.parameterBinders = parameterBinders; + this.affectedTableNames = affectedTableNames; + this.filterJdbcParameters = filterJdbcParameters; + this.appliedParameters = appliedParameters; + } + + @Override + public String getSql() { + return sql; + } + + @Override + public List getParameterBinders() { + return parameterBinders; + } + + @Override + public Set getAffectedTableNames() { + return affectedTableNames; + } + + @Override + public Set getFilterJdbcParameters() { + return filterJdbcParameters; + } + + @Override + public boolean dependsOnParameterBindings() { + return !appliedParameters.isEmpty(); + } + + @Override + public boolean isCompatibleWith(JdbcParameterBindings jdbcParameterBindings, QueryOptions queryOptions) { + if ( !appliedParameters.isEmpty() ) { + if ( jdbcParameterBindings == null ) { + return false; + } + for ( Map.Entry entry : appliedParameters.entrySet() ) { + final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( entry.getKey() ); + final JdbcParameterBinding appliedBinding = entry.getValue(); + if ( binding == null || !appliedBinding.getBindType() + .getJavaTypeDescriptor() + .areEqual( binding.getBindValue(), appliedBinding.getBindValue() ) ) { + return false; + } + } + } + return true; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/Callback.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/Callback.java index c34106e414..d1b960658f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/Callback.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/Callback.java @@ -7,7 +7,9 @@ package org.hibernate.sql.exec.spi; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.ast.spi.AfterLoadAction; +import org.hibernate.persister.entity.Loadable; /** * Callback to allow SQM interpretation to trigger certain things within ORM. See the current @@ -18,4 +20,6 @@ import org.hibernate.loader.ast.spi.AfterLoadAction; */ public interface Callback { void registerAfterLoadAction(AfterLoadAction afterLoadAction); + + void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java index 61794b1c0c..7955d4803f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/ExecutionContext.java @@ -10,6 +10,7 @@ import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.persister.entity.Loadable; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; @@ -32,6 +33,10 @@ public interface ExecutionContext { Callback getCallback(); + default void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { + // No-op because by default there is callback + } + /** * Get the collection key for the collection which is to be loaded immediately. */ diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcDelete.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcDelete.java index 7ba05bb84e..ca20e59b08 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcDelete.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcDelete.java @@ -6,8 +6,25 @@ */ package org.hibernate.sql.exec.spi; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.LockOptions; +import org.hibernate.internal.FilterJdbcParameter; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; + /** * @author Steve Ebersole */ -public interface JdbcDelete extends JdbcMutation { +public class JdbcDelete extends AbstractJdbcOperation implements JdbcMutation { + + public JdbcDelete( + String sql, + List parameterBinders, + Set affectedTableNames, + Set filterJdbcParameters, + Map appliedParameters) { + super( sql, parameterBinders, affectedTableNames, filterJdbcParameters, appliedParameters ); + } } 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 index ea01d9081c..24bcbc2ed8 100644 --- 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 @@ -6,8 +6,25 @@ */ package org.hibernate.sql.exec.spi; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.LockOptions; +import org.hibernate.internal.FilterJdbcParameter; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; + /** * @author Steve Ebersole */ -public interface JdbcInsert extends JdbcMutation { +public class JdbcInsert extends AbstractJdbcOperation implements JdbcMutation { + + public JdbcInsert( + String sql, + List parameterBinders, + Set affectedTableNames, + Set filterJdbcParameters, + Map appliedParameters) { + super( sql, parameterBinders, affectedTableNames, filterJdbcParameters, appliedParameters ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcLockStrategy.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcLockStrategy.java new file mode 100644 index 0000000000..f6aee4ef8e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcLockStrategy.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * The strategy to use for applying locks to a {@link JdbcSelect}. + * + * @author Christian Beikov + */ +public enum JdbcLockStrategy { + + /** + * Use a dialect specific check to determine how to apply locks. + */ + AUTO, + /** + * Use follow on locking. + */ + FOLLOW_ON, + /** + * Do not apply locks. + */ + NONE; +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcOperation.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcOperation.java index 917db07f1f..4186efd15a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcOperation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcOperation.java @@ -38,15 +38,9 @@ public interface JdbcOperation { * Signals that the SQL depends on the parameter bindings e.g. due to the need for inlining * of parameter values or multiValued parameters. */ - default boolean dependsOnParameterBindings() { - return false; - } + boolean dependsOnParameterBindings(); - default boolean isCompatibleWith( - JdbcParameterBindings jdbcParameterBindings, - QueryOptions queryOptions) { - return true; - } + boolean isCompatibleWith(JdbcParameterBindings jdbcParameterBindings, QueryOptions queryOptions); default void bindFilterJdbcParameters(JdbcParameterBindings jdbcParameterBindings) { if ( CollectionHelper.isNotEmpty( getFilterJdbcParameters() ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelect.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelect.java index 48c5b885b7..03e006e32d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelect.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelect.java @@ -11,31 +11,24 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.hibernate.LockOptions; import org.hibernate.internal.FilterJdbcParameter; import org.hibernate.query.Limit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; -import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** * Executable JDBC command * * @author Steve Ebersole */ -public class JdbcSelect implements JdbcOperation { - private final String sql; - private final List parameterBinders; +public class JdbcSelect extends AbstractJdbcOperation { private final JdbcValuesMappingProducer jdbcValuesMappingProducer; - private final Set affectedTableNames; - private final Set filterJdbcParameters; private final int rowsToSkip; private final int maxRows; - private final Map appliedParameters; - private final LockOptions appliedLockOptions; private final JdbcParameter offsetParameter; private final JdbcParameter limitParameter; + private final JdbcLockStrategy jdbcLockStrategy; public JdbcSelect( String sql, @@ -52,7 +45,7 @@ public class JdbcSelect implements JdbcOperation { 0, Integer.MAX_VALUE, Collections.emptyMap(), - null, + JdbcLockStrategy.AUTO, null, null ); @@ -67,42 +60,18 @@ public class JdbcSelect implements JdbcOperation { int rowsToSkip, int maxRows, Map appliedParameters, - LockOptions appliedLockOptions, + JdbcLockStrategy jdbcLockStrategy, JdbcParameter offsetParameter, JdbcParameter limitParameter) { - this.sql = sql; - this.parameterBinders = parameterBinders; + super( sql, parameterBinders, affectedTableNames, filterJdbcParameters, appliedParameters ); this.jdbcValuesMappingProducer = jdbcValuesMappingProducer; - this.affectedTableNames = affectedTableNames; - this.filterJdbcParameters = filterJdbcParameters; this.rowsToSkip = rowsToSkip; this.maxRows = maxRows; - this.appliedParameters = appliedParameters; - this.appliedLockOptions = appliedLockOptions; + this.jdbcLockStrategy = jdbcLockStrategy; this.offsetParameter = offsetParameter; this.limitParameter = limitParameter; } - @Override - public String getSql() { - return sql; - } - - @Override - public List getParameterBinders() { - return parameterBinders; - } - - @Override - public Set getAffectedTableNames() { - return affectedTableNames; - } - - @Override - public Set getFilterJdbcParameters() { - return filterJdbcParameters; - } - public JdbcValuesMappingProducer getJdbcValuesMappingProducer() { return jdbcValuesMappingProducer; } @@ -119,9 +88,8 @@ public class JdbcSelect implements JdbcOperation { return offsetParameter != null || limitParameter != null; } - @Override - public boolean dependsOnParameterBindings() { - return !appliedParameters.isEmpty(); + public JdbcLockStrategy getLockStrategy() { + return jdbcLockStrategy; } @Override @@ -132,10 +100,30 @@ public class JdbcSelect implements JdbcOperation { } for ( Map.Entry entry : appliedParameters.entrySet() ) { final JdbcParameter parameter = entry.getKey(); + final JdbcParameterBinding appliedBinding = entry.getValue(); + // This is a special case where the rendered SQL depends on the presence of the parameter, + // but not specifically on the value. In this case we have to re-generate the SQL if we can't find a binding + // The need for this can be tested with the OracleFollowOnLockingTest#testPessimisticLockWithMaxResultsThenNoFollowOnLocking + // Since the Limit is not part of the query plan cache key, but this has an effect on follow on locking, + // we must treat the absence of Limit parameters, when they were considered for locking, as incompatible + if ( appliedBinding == null ) { + if ( parameter == offsetParameter ) { + if ( queryOptions.getLimit() == null || queryOptions.getLimit().getFirstRowJpa() == 0 ) { + return false; + } + } + else if ( parameter == limitParameter ) { + if ( queryOptions.getLimit() == null || queryOptions.getLimit().getMaxRowsJpa() == Integer.MAX_VALUE ) { + return false; + } + } + else if ( jdbcParameterBindings.getBinding( parameter ) == null ) { + return false; + } + } // We handle limit and offset parameters below if ( parameter != offsetParameter && parameter != limitParameter ) { - final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( entry.getKey() ); - final JdbcParameterBinding appliedBinding = entry.getValue(); + final JdbcParameterBinding binding = jdbcParameterBindings.getBinding( parameter ); if ( binding == null || !appliedBinding.getBindType() .getJavaTypeDescriptor() .areEqual( binding.getBindValue(), appliedBinding.getBindValue() ) ) { @@ -144,15 +132,6 @@ public class JdbcSelect implements JdbcOperation { } } } - final LockOptions lockOptions = queryOptions.getLockOptions(); - if ( appliedLockOptions == null ) { - if ( lockOptions != null && !lockOptions.isEmpty() ) { - return false; - } - } - else if ( !appliedLockOptions.isCompatible( lockOptions ) ) { - return false; - } final Limit limit = queryOptions.getLimit(); if ( offsetParameter == null && limitParameter == null ) { if ( limit != null && !limit.isEmpty() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcUpdate.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcUpdate.java index 2e8d3cedbd..14318edcf4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcUpdate.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcUpdate.java @@ -6,8 +6,25 @@ */ package org.hibernate.sql.exec.spi; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.LockOptions; +import org.hibernate.internal.FilterJdbcParameter; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; + /** * @author Steve Ebersole */ -public interface JdbcUpdate extends JdbcMutation { +public class JdbcUpdate extends AbstractJdbcOperation implements JdbcMutation { + + public JdbcUpdate( + String sql, + List parameterBinders, + Set affectedTableNames, + Set filterJdbcParameters, + Map appliedParameters) { + super( sql, parameterBinders, affectedTableNames, filterJdbcParameters, appliedParameters ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/NativeJdbcMutation.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/NativeJdbcMutation.java index 1faab96913..72608491d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/NativeJdbcMutation.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/NativeJdbcMutation.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Set; import org.hibernate.internal.FilterJdbcParameter; +import org.hibernate.query.spi.QueryOptions; /** * Executable JDBC command @@ -51,4 +52,13 @@ public class NativeJdbcMutation implements JdbcMutation { return Collections.EMPTY_SET; } + @Override + public boolean dependsOnParameterBindings() { + return false; + } + + @Override + public boolean isCompatibleWith(JdbcParameterBindings jdbcParameterBindings, QueryOptions queryOptions) { + return true; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AssemblerCreationState.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AssemblerCreationState.java index 4e129e89d6..6f500f7675 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AssemblerCreationState.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AssemblerCreationState.java @@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph; import java.util.function.Supplier; +import org.hibernate.LockMode; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAstCreationContext; @@ -17,6 +18,8 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext; */ public interface AssemblerCreationState { + LockMode determineEffectiveLockMode(String identificationVariable); + Initializer resolveInitializer( NavigablePath navigablePath, ModelPart fetchedModelPart, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java index d3ebc1523a..34097a1131 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java @@ -76,7 +76,6 @@ public interface FetchParent extends DomainResultGraphNode { NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { return fetchable.generateFetch( @@ -84,7 +83,6 @@ public interface FetchParent extends DomainResultGraphNode { fetchablePath, fetchTiming, selected, - lockMode, resultVariable, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java index 32011e75ca..089d485798 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java @@ -31,7 +31,6 @@ public interface Fetchable extends ModelPart { NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java index e6c1151755..9564efb03f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java @@ -63,7 +63,6 @@ public class CollectionDomainResult implements DomainResult, CollectionResultGra this, true, null, - LockMode.READ, creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java index 4afde5012c..a610617832 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java @@ -8,14 +8,12 @@ package org.hibernate.sql.results.graph.collection.internal; import java.util.List; -import org.hibernate.LockMode; import org.hibernate.collection.spi.CollectionInitializerProducer; import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -29,7 +27,6 @@ import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.FetchableContainer; import org.hibernate.sql.results.graph.collection.CollectionInitializer; -import org.hibernate.sql.results.graph.entity.EntityFetch; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; /** @@ -136,8 +133,6 @@ public class EagerCollectionFetch extends CollectionFetch implements FetchParent fetchParent, true, null, - // todo (6.0) : we need to propagate these lock modes - LockMode.READ, indexFetch, elementFetch, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java index 8ce35e9e8c..5f12b61717 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java @@ -57,8 +57,8 @@ public class EntityCollectionPartTableGroup implements TableGroup { } @Override - public LockMode getLockMode() { - return collectionTableGroup.getLockMode(); + public String getSourceAlias() { + return collectionTableGroup.getSourceAlias(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java index 6d43bbf855..9ff2aba1e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java @@ -6,7 +6,6 @@ */ package org.hibernate.sql.results.graph.embeddable.internal; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; @@ -67,7 +66,6 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab null, nullable ? SqlAstJoinType.LEFT : SqlAstJoinType.INNER, true, - LockMode.NONE, creationState.getSqlAstCreationState() ); return tableGroupJoin.getJoinedGroup(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java index 7198f80f57..6025d66c1b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java @@ -6,7 +6,6 @@ */ package org.hibernate.sql.results.graph.embeddable.internal; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; @@ -59,7 +58,6 @@ public class EmbeddableForeignKeyResultImpl NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { final boolean shouldSelect; @@ -78,7 +76,6 @@ public class EmbeddableForeignKeyResultImpl fetchablePath, fetchTiming, shouldSelect, - lockMode, resultVariable, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java index 93716d34c8..c79ab22a5f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java @@ -8,7 +8,6 @@ package org.hibernate.sql.results.graph.embeddable.internal; import java.util.List; -import org.hibernate.LockMode; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.query.NavigablePath; @@ -53,7 +52,6 @@ public class EmbeddableResultImpl extends AbstractFetchParent implements Embe resultVariable, SqlAstJoinType.INNER, true, - LockMode.NONE, creationState.getSqlAstCreationState() ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java index 4e1b024b7c..265ecdb594 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java @@ -12,7 +12,9 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; +import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.StaleObjectStateException; import org.hibernate.WrongClassException; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.entry.CacheEntry; @@ -48,7 +50,9 @@ import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableAssembler; import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; +import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.type.TypeHelper; +import org.hibernate.type.VersionType; import static org.hibernate.internal.log.LoggingHelper.toLoggableString; @@ -114,10 +118,16 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces this.navigablePath = navigablePath; this.lockMode = lockMode; + assert lockMode != null; if ( identifierResult != null ) { this.identifierAssembler = identifierResult.createResultAssembler( new AssemblerCreationState() { + @Override + public LockMode determineEffectiveLockMode(String identificationVariable) { + return creationState.determineEffectiveLockMode( identificationVariable ); + } + @Override public Initializer resolveInitializer( NavigablePath navigablePath, @@ -468,7 +478,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces entityInstance = existingEntity; } else { - // look to see if another initializer from a parent load context or an earlier // initializer is already loading the entity if ( entityInstance == null ) { @@ -481,12 +490,47 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces } } + + if ( LockMode.NONE != lockMode ) { + final EntityEntry entry = session.getPersistenceContextInternal().getEntry( entityInstance ); + if ( entry != null && entry.getLockMode().lessThan( lockMode ) ) { + //we only check the version when _upgrading_ lock modes + if ( versionAssembler != null ) { + checkVersion( entry, rowProcessingState ); + } + //we need to upgrade the lock mode to the mode requested + entry.setLockMode( lockMode ); + } + } } notifyParentResolutionListeners( entityInstance ); preLoad( rowProcessingState ); } + /** + * Check the version of the object in the RowProcessingState against + * the object version in the session cache, throwing an exception + * if the version numbers are different + */ + private void checkVersion(EntityEntry entry, final RowProcessingState rowProcessingState) throws HibernateException { + final Object version = entry.getVersion(); + + if ( version != null ) { + // null version means the object is in the process of being loaded somewhere else in the ResultSet + final VersionType versionType = concreteDescriptor.getVersionType(); + final Object currentVersion = versionAssembler.assemble( rowProcessingState ); + if ( !versionType.isEqual( version, currentVersion ) ) { + final StatisticsImplementor statistics = rowProcessingState.getSession().getFactory().getStatistics(); + if ( statistics.isStatisticsEnabled() ) { + statistics.optimisticFailure( concreteDescriptor.getEntityName() ); + } + throw new StaleObjectStateException( concreteDescriptor.getEntityName(), entry.getId() ); + } + } + + } + protected Object getProxy(PersistenceContext persistenceContext) { return persistenceContext.getProxy( entityKey ); } @@ -666,6 +710,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces else { rowId = null; } + // from the perspective of Hibernate, an entity is read locked as soon as it is read + // so regardless of the requested lock mode, we upgrade to at least the read level + final LockMode lockModeToAcquire = lockMode == LockMode.NONE + ? LockMode.READ + : lockMode; final EntityEntry entityEntry = persistenceContext.addEntry( toInitialize, @@ -674,7 +723,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces rowId, entityKey.getIdentifier(), version, - lockMode, + lockModeToAcquire, true, concreteDescriptor, false diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java index e018f92955..e74f947705 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java @@ -39,28 +39,24 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent private final DomainResult discriminatorResult; private final DomainResult versionResult; private final DomainResult rowIdResult; - private final LockMode lockMode; private final EntityMappingType targetType; public AbstractEntityResultGraphNode( EntityValuedModelPart referencedModelPart, - LockMode lockMode, NavigablePath navigablePath, DomainResultCreationState creationState) { - this( referencedModelPart, lockMode, navigablePath, null, creationState ); + this( referencedModelPart, navigablePath, null, creationState ); } @SuppressWarnings("WeakerAccess") public AbstractEntityResultGraphNode( EntityValuedModelPart referencedModelPart, - LockMode lockMode, NavigablePath navigablePath, EntityMappingType targetType, DomainResultCreationState creationState) { super( referencedModelPart.getEntityMappingType(), navigablePath ); this.referencedModelPart = referencedModelPart; - this.lockMode = lockMode; this.targetType = targetType; final EntityMappingType entityDescriptor = referencedModelPart.getEntityMappingType(); @@ -205,10 +201,6 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent return getEntityValuedModelPart().getEntityMappingType().getMappedJavaTypeDescriptor(); } - public LockMode getLockMode() { - return lockMode; - } - public DomainResult getIdentifierResult() { return identifierResult; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityValuedFetchable.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityValuedFetchable.java index df8d5d284d..630f4bd9f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityValuedFetchable.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityValuedFetchable.java @@ -6,7 +6,6 @@ */ package org.hibernate.sql.results.graph.entity; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.FetchParent; @@ -24,7 +23,6 @@ public interface EntityValuedFetchable extends Fetchable, EntityValuedModelPart NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java index 1b2395633d..891316887b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java @@ -6,7 +6,6 @@ */ package org.hibernate.sql.results.graph.entity.internal; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -25,19 +24,18 @@ import org.hibernate.sql.results.graph.entity.EntityValuedFetchable; public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch { private final EntityResultImpl entityResult; - private final LockMode lockMode; + private final String sourceAlias; public EntityFetchJoinedImpl( FetchParent fetchParent, EntityValuedFetchable fetchedAttribute, TableGroup tableGroup, - LockMode lockMode, boolean nullable, NavigablePath navigablePath, DomainResultCreationState creationState) { super( fetchParent, fetchedAttribute, navigablePath, nullable ); - this.lockMode = lockMode; - entityResult = new EntityResultImpl( + this.sourceAlias = tableGroup.getSourceAlias(); + this.entityResult = new EntityResultImpl( navigablePath, fetchedAttribute, tableGroup, @@ -57,7 +55,7 @@ public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch { entityResult, getReferencedModePart(), getNavigablePath(), - lockMode, + creationState.determineEffectiveLockMode( sourceAlias ), entityResult.getIdentifierResult(), entityResult.getDiscriminatorResult(), entityResult.getVersionResult(), @@ -80,7 +78,4 @@ public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch { return entityResult; } - public LockMode getLockMode() { - return lockMode; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java index ab232028fe..e86e266c3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java @@ -6,6 +6,7 @@ */ package org.hibernate.sql.results.graph.entity.internal; +import org.hibernate.LockMode; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.query.NavigablePath; @@ -49,7 +50,6 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E DomainResultCreationState creationState) { super( entityValuedModelPart, - creationState.getSqlAstCreationState().determineLockMode( resultVariable ), navigablePath, creationState ); @@ -89,6 +89,10 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E return resultVariable; } + protected LockMode getLockMode(AssemblerCreationState creationState) { + return creationState.determineEffectiveLockMode( tableGroup.getSourceAlias() ); + } + @Override public DomainResultAssembler createResultAssembler(AssemblerCreationState creationState) { final EntityInitializer initializer = (EntityInitializer) creationState.resolveInitializer( @@ -97,7 +101,7 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E () -> new EntityResultInitializer( this, getNavigablePath(), - getLockMode(), + getLockMode( creationState ), getIdentifierResult(), getDiscriminatorResult(), getVersionResult(), diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java index ae89166fbb..86509f3d03 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java @@ -38,7 +38,7 @@ public class EntityResultJoinedSubclassImpl extends EntityResultImpl { () -> new EntityResultInitializer( this, getNavigablePath(), - getLockMode(), + getLockMode( creationState ), getIdentifierResult(), getDiscriminatorResult(), getVersionResult(), diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java index 88c87aec90..fdf9940bcb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResultsHelper.java @@ -12,6 +12,8 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.BatchFetchQueue; import org.hibernate.engine.spi.CollectionEntry; @@ -22,6 +24,7 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.exec.spi.Callback; +import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.results.ResultsLogger; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResultAssembler; @@ -36,15 +39,20 @@ import org.hibernate.stat.spi.StatisticsImplementor; */ public class ResultsHelper { public static RowReader createRowReader( - SessionFactoryImplementor sessionFactory, - Callback callback, + ExecutionContext executionContext, + LockOptions lockOptions, RowTransformer rowTransformer, JdbcValues jdbcValues) { - final Map initializerMap = new LinkedHashMap<>(); + final Map initializerMap = new LinkedHashMap<>(); final List initializers = new ArrayList<>(); + final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory(); final List> assemblers = jdbcValues.getValuesMapping().resolveAssemblers( new AssemblerCreationState() { + @Override + public LockMode determineEffectiveLockMode(String identificationVariable) { + return lockOptions.getEffectiveLockMode( identificationVariable ); + } @Override public Initializer resolveInitializer( @@ -86,8 +94,7 @@ public class ResultsHelper { return new StandardRowReader<>( (List) assemblers, initializers, - rowTransformer, - callback + rowTransformer ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java index b9d4f5d572..c610808813 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java @@ -149,7 +149,7 @@ public class RowProcessingStateStandardImpl implements RowProcessingState { @Override public Callback getCallback() { - return afterLoadAction -> {}; + return executionContext.getCallback(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java index 792a8aa1ef..8e579908b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/StandardRowReader.java @@ -33,21 +33,18 @@ public class StandardRowReader implements RowReader { private final RowTransformer rowTransformer; private final int assemblerCount; - private final Callback callback; @SuppressWarnings("WeakerAccess") public StandardRowReader( List resultAssemblers, List initializers, - RowTransformer rowTransformer, - Callback callback) { + RowTransformer rowTransformer) { this.resultAssemblers = resultAssemblers; this.initializers = initializers; this.rowTransformer = rowTransformer; this.assemblerCount = resultAssemblers.size(); - this.callback = callback; logDebugInfo(); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java index 1f297845cb..f6b466e2c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java @@ -166,7 +166,6 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { throw new UnsupportedOperationException(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java index c9225350fa..c42e7a856d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java @@ -6,7 +6,6 @@ */ package org.hibernate.sql.results.internal.domain; -import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.Association; @@ -195,7 +194,6 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association { NavigablePath fetchablePath, FetchTiming fetchTiming, boolean selected, - LockMode lockMode, String resultVariable, DomainResultCreationState creationState) { throw new UnsupportedOperationException(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java index a5b9810605..23c9812b3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java @@ -9,8 +9,10 @@ package org.hibernate.sql.results.jdbc.internal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Collections; import java.util.function.Function; +import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.dialect.Dialect; @@ -19,26 +21,32 @@ import org.hibernate.dialect.pagination.NoopLimitHandler; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; import org.hibernate.query.Limit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; import org.hibernate.sql.exec.spi.ExecutionContext; +import org.hibernate.sql.exec.spi.JdbcLockStrategy; import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcSelect; -import org.jboss.logging.Logger; - /** * @author Steve Ebersole */ public class DeferredResultSetAccess extends AbstractResultSetAccess { - private static final Logger log = CoreLogging.logger( DeferredResultSetAccess.class ); + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( + DeferredResultSetAccess.class + ); private final JdbcSelect jdbcSelect; private final JdbcParameterBindings jdbcParameterBindings; private final ExecutionContext executionContext; private final Function statementCreator; + private final String finalSql; + private final Limit limit; + private final LimitHandler limitHandler; + private final boolean usesFollowOnLocking; private PreparedStatement preparedStatement; private ResultSet resultSet; @@ -53,6 +61,79 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { this.executionContext = executionContext; this.jdbcSelect = jdbcSelect; this.statementCreator = statementCreator; + final QueryOptions queryOptions = executionContext.getQueryOptions(); + if ( queryOptions == null ) { + finalSql = jdbcSelect.getSql(); + limit = null; + limitHandler = NoopLimitHandler.NO_LIMIT; + usesFollowOnLocking = false; + } + else { + // Note that limit and lock aren't set for SQM as that is applied during SQL rendering + // But for native queries, we have to adapt the SQL string + final Dialect dialect = executionContext.getSession().getJdbcServices().getDialect(); + String sql = jdbcSelect.getSql(); + limit = queryOptions.getLimit(); + if ( limit == null || limit.isEmpty() || jdbcSelect.usesLimitParameters() ) { + sql = jdbcSelect.getSql(); + limitHandler = NoopLimitHandler.NO_LIMIT; + } + else { + limitHandler = dialect.getLimitHandler(); + sql = limitHandler.processSql( + jdbcSelect.getSql(), + limit, + queryOptions + ); + } + + final LockOptions lockOptions = queryOptions.getLockOptions(); + boolean followOnLocking = false; + if ( lockOptions != null && !lockOptions.isEmpty() && jdbcSelect.getLockStrategy() != JdbcLockStrategy.NONE ) { + switch ( jdbcSelect.getLockStrategy() ) { + case FOLLOW_ON: + followOnLocking = true; + break; + case AUTO: + if ( lockOptions.getFollowOnLocking() == null && dialect.useFollowOnLocking( sql, queryOptions ) + || Boolean.TRUE.equals( lockOptions.getFollowOnLocking() ) ) { + followOnLocking = true; + } + break; + } + if ( followOnLocking ) { + final LockMode lockMode = determineFollowOnLockMode( lockOptions ); + if ( lockMode != LockMode.UPGRADE_SKIPLOCKED ) { + // Dialect prefers to perform locking in a separate step + if ( lockOptions.getLockMode() != LockMode.NONE ) { + LOG.usingFollowOnLocking(); + } + + final LockOptions lockOptionsToUse = new LockOptions( lockMode ); + lockOptionsToUse.setTimeOut( lockOptions.getTimeOut() ); + lockOptionsToUse.setScope( lockOptions.getScope() ); + + executionContext.getCallback().registerAfterLoadAction( + (session, entity, persister) -> { + ( (Session) session ).buildLockRequest( lockOptionsToUse ).lock( + persister.getEntityName(), + entity + ); + } + ); + } + } + else { + sql = dialect.applyLocksToSql( sql, lockOptions, Collections.emptyMap() ); + } + } + usesFollowOnLocking = followOnLocking; + finalSql = dialect.addSqlHintOrComment( + sql, + queryOptions, + executionContext.getSession().getFactory().getSessionFactoryOptions().isCommentsEnabled() + ); + } } @Override @@ -68,46 +149,20 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { return executionContext.getSession().getFactory(); } + public String getFinalSql() { + return finalSql; + } + + public boolean usesFollowOnLocking() { + return usesFollowOnLocking; + } + private void executeQuery() { final LogicalConnectionImplementor logicalConnection = getPersistenceContext().getJdbcCoordinator().getLogicalConnection(); - final JdbcServices jdbcServices = getPersistenceContext().getFactory().getServiceRegistry().getService( JdbcServices.class ); final QueryOptions queryOptions = executionContext.getQueryOptions(); - final String finalSql; - final Limit limit; - final LimitHandler limitHandler; - if ( queryOptions == null ) { - finalSql = jdbcSelect.getSql(); - limit = null; - limitHandler = NoopLimitHandler.NO_LIMIT; - } - else { - // Note that limit and lock aren't set for SQM as that is applied during SQL rendering - // But for native queries, we have to adapt the SQL string - final Dialect dialect = executionContext.getSession().getJdbcServices().getDialect(); - final String sql; - limit = queryOptions.getLimit(); - if ( limit == null || limit.isEmpty() || jdbcSelect.usesLimitParameters() ) { - sql = jdbcSelect.getSql(); - limitHandler = NoopLimitHandler.NO_LIMIT; - } - else { - limitHandler = dialect.getLimitHandler(); - sql = limitHandler.processSql( - jdbcSelect.getSql(), - limit, - queryOptions - ); - } - - finalSql = dialect.addSqlHintOrComment( - applyLocks( sql, queryOptions.getLockOptions() ), - queryOptions, - executionContext.getSession().getFactory().getSessionFactoryOptions().isCommentsEnabled() - ); - } try { - log.tracef( "Executing query to retrieve ResultSet : %s", finalSql ); + LOG.tracef( "Executing query to retrieve ResultSet : %s", finalSql ); // prepare the query preparedStatement = statementCreator.apply( finalSql ); @@ -169,7 +224,7 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { } catch (SQLException e) { - throw jdbcServices.getSqlExceptionHelper().convert( + throw executionContext.getSession().getJdbcServices().getSqlExceptionHelper().convert( e, "JDBC exception executing SQL [" + finalSql + "]" ); @@ -179,20 +234,18 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { } } - private String applyLocks(String sql, LockOptions lockOptions) { - if ( lockOptions != null && !lockOptions.isEmpty() ) { - // Locks are applied during SQL rendering, but for native queries, we apply locks separately - final LockOptions originalLockOptions = lockOptions.makeCopy(); - executionContext.getCallback().registerAfterLoadAction( - (session, entity, persister) -> { - ( (Session) session ).buildLockRequest( originalLockOptions ).lock( - persister.getEntityName(), - entity - ); - } - ); + protected LockMode determineFollowOnLockMode(LockOptions lockOptions) { + final LockMode lockModeToUse = lockOptions.findGreatestLockMode(); + + if ( lockOptions.hasAliasSpecificLockModes() ) { + if ( lockOptions.getLockMode() == LockMode.NONE && lockModeToUse == LockMode.NONE ) { + return lockModeToUse; + } + else { + LOG.aliasSpecificLockingWithFollowOnLocking( lockModeToUse ); + } } - return sql; + return lockModeToUse; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java index 92a20692d8..54bd383fa2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java @@ -22,6 +22,7 @@ import org.hibernate.event.spi.EventType; import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PostLoadEventListener; import org.hibernate.event.spi.PreLoadEvent; +import org.hibernate.persister.entity.Loadable; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.collection.internal.ArrayInitializer; import org.hibernate.query.spi.QueryOptions; @@ -199,6 +200,11 @@ public class JdbcValuesSourceProcessingStateStandardImpl implements JdbcValuesSo for ( PostLoadEventListener listener : listenerGroup.listeners() ) { listener.onPostLoad( postLoadEvent ); } + executionContext.invokeAfterLoadActions( + getSession(), + loadingEntityEntry.getEntityInstance(), + (Loadable) loadingEntityEntry.getDescriptor() + ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchingEntityLoaderInitializationWithNoLockModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/BatchingEntityLoaderInitializationWithNoLockModeTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchingEntityLoaderInitializationWithNoLockModeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/BatchingEntityLoaderInitializationWithNoLockModeTest.java index 461e57c7a8..e43de735bd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/batchfetch/BatchingEntityLoaderInitializationWithNoLockModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batchfetch/BatchingEntityLoaderInitializationWithNoLockModeTest.java @@ -1,4 +1,4 @@ -package org.hibernate.test.batchfetch; +package org.hibernate.orm.test.batchfetch; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.junit.Assert.assertNotNull; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/OracleFollowOnLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/OracleFollowOnLockingTest.java similarity index 91% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/functional/OracleFollowOnLockingTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/OracleFollowOnLockingTest.java index f19539eee7..41d1b8eea9 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/OracleFollowOnLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/OracleFollowOnLockingTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.functional; +package org.hibernate.orm.test.dialect.functional; import java.util.List; import javax.persistence.Entity; @@ -23,7 +23,9 @@ import org.hibernate.Session; import org.hibernate.annotations.QueryHints; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.dialect.Oracle8iDialect; +import org.hibernate.dialect.OracleDialect; import org.hibernate.exception.SQLGrammarException; +import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; @@ -33,12 +35,13 @@ import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author Vlad Mihalcea */ -@RequiresDialect(value = { Oracle8iDialect.class }) +@RequiresDialect(value = { OracleDialect.class }) @TestForIssue(jiraKey = "HHH-9486") public class OracleFollowOnLockingTest extends BaseNonConfigCoreFunctionalTestCase { @@ -190,11 +193,16 @@ public class OracleFollowOnLockingTest extends .getResultList(); fail( "Should throw exception since Oracle does not support ORDER BY if follow on locking is disabled" ); } - catch ( PersistenceException expected ) { + catch ( IllegalStateException expected ) { assertEquals( - SQLGrammarException.class, + IllegalQueryOperationException.class, expected.getCause().getClass() ); + assertTrue( + expected.getCause().getMessage().contains( + "Locking with OFFSET is not supported!" + ) + ); } } @@ -265,11 +273,16 @@ public class OracleFollowOnLockingTest extends .getResultList(); fail( "Should throw exception since Oracle does not support ORDER BY if follow on locking is disabled" ); } - catch ( PersistenceException expected ) { + catch ( IllegalStateException expected ) { assertEquals( - SQLGrammarException.class, + IllegalQueryOperationException.class, expected.getCause().getClass() ); + assertTrue( + expected.getCause().getMessage().contains( + "Locking with ORDER BY is not supported!" + ) + ); } } @@ -338,11 +351,16 @@ public class OracleFollowOnLockingTest extends .getResultList(); fail( "Should throw exception since Oracle does not support DISTINCT if follow on locking is disabled" ); } - catch ( PersistenceException expected ) { + catch ( IllegalStateException expected ) { assertEquals( - SQLGrammarException.class, + IllegalQueryOperationException.class, expected.getCause().getClass() ); + assertTrue( + expected.getCause().getMessage().contains( + "Locking with DISTINCT is not supported!" + ) + ); } } @@ -411,11 +429,16 @@ public class OracleFollowOnLockingTest extends .getResultList(); fail( "Should throw exception since Oracle does not support GROUP BY if follow on locking is disabled" ); } - catch ( PersistenceException expected ) { + catch ( IllegalStateException expected ) { assertEquals( - SQLGrammarException.class, + IllegalQueryOperationException.class, expected.getCause().getClass() ); + assertTrue( + expected.getCause().getMessage().contains( + "Locking with GROUP BY is not supported!" + ) + ); } } @@ -477,11 +500,16 @@ public class OracleFollowOnLockingTest extends .getResultList(); fail( "Should throw exception since Oracle does not support UNION if follow on locking is disabled" ); } - catch ( PersistenceException expected ) { + catch ( IllegalStateException expected ) { assertEquals( - SQLGrammarException.class, + IllegalQueryOperationException.class, expected.getCause().getClass() ); + assertTrue( + expected.getCause().getMessage().contains( + "Locking with set operators is not supported!" + ) + ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/AbstractLockHintTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/AbstractLockHintTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/AbstractLockHintTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/AbstractLockHintTest.java index 92617adc85..b262d4f460 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/AbstractLockHintTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/AbstractLockHintTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.lockhint; +package org.hibernate.orm.test.dialect.unit.lockhint; import java.util.Collections; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/MySQLStorageEngineTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/MySQLStorageEngineTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java index 36346e313b..b7e6846a62 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/MySQLStorageEngineTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.lockhint; +package org.hibernate.orm.test.dialect.unit.lockhint; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServer2005LockHintsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SQLServer2005LockHintsTest.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServer2005LockHintsTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SQLServer2005LockHintsTest.java index f874226293..6936c18036 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServer2005LockHintsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SQLServer2005LockHintsTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.lockhint; +package org.hibernate.orm.test.dialect.unit.lockhint; import org.hibernate.LockMode; import org.hibernate.LockOptions; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServerLockHintsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SQLServerLockHintsTest.java similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServerLockHintsTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SQLServerLockHintsTest.java index 5ba1d03089..589c9940af 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SQLServerLockHintsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SQLServerLockHintsTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.lockhint; +package org.hibernate.orm.test.dialect.unit.lockhint; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.SQLServerDialect; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseASE15LockHintsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SybaseASE15LockHintsTest.java similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseASE15LockHintsTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SybaseASE15LockHintsTest.java index 6565443042..f64da376e5 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseASE15LockHintsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SybaseASE15LockHintsTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.lockhint; +package org.hibernate.orm.test.dialect.unit.lockhint; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.SybaseASE15Dialect; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseLockHintsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SybaseLockHintsTest.java similarity index 91% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseLockHintsTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SybaseLockHintsTest.java index a493a87ac7..594e144098 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/lockhint/SybaseLockHintsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/SybaseLockHintsTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.lockhint; +package org.hibernate.orm.test.dialect.unit.lockhint; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.SybaseDialect; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/DB2LockTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/DB2LockTimeoutTest.java similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/DB2LockTimeoutTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/DB2LockTimeoutTest.java index 24b725d0e6..0f9909e09a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/DB2LockTimeoutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/DB2LockTimeoutTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.locktimeout; +package org.hibernate.orm.test.dialect.unit.locktimeout; import org.hibernate.LockMode; import org.hibernate.LockOptions; @@ -20,7 +20,7 @@ import static org.junit.Assert.assertEquals; */ public class DB2LockTimeoutTest extends BaseUnitTestCase { - private final Dialect dialect = new DB2Dialect(); + private final Dialect dialect = new DB2Dialect( 1150 ); @Test public void testLockTimeoutNoAliasNoTimeout() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/HANALockTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/HANALockTimeoutTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/HANALockTimeoutTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/HANALockTimeoutTest.java index 455345ea35..5176390ee3 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/HANALockTimeoutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/HANALockTimeoutTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.locktimeout; +package org.hibernate.orm.test.dialect.unit.locktimeout; import org.hibernate.LockMode; import org.hibernate.LockOptions; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java index b1a3d150d4..36f4b6e21c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.locktimeout; +package org.hibernate.orm.test.dialect.unit.locktimeout; import org.hibernate.LockMode; import org.hibernate.LockOptions; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/PostgreSQLLockTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/PostgreSQLLockTimeoutTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/PostgreSQLLockTimeoutTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/PostgreSQLLockTimeoutTest.java index 07bef09183..8f4de8fd4d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/locktimeout/PostgreSQLLockTimeoutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/PostgreSQLLockTimeoutTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.locktimeout; +package org.hibernate.orm.test.dialect.unit.locktimeout; import org.hibernate.LockMode; import org.hibernate.LockOptions; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/AbstractSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/AbstractSequenceInformationExtractorTest.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/AbstractSequenceInformationExtractorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/AbstractSequenceInformationExtractorTest.java index 8d804fddd8..5c726f280b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/AbstractSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/AbstractSequenceInformationExtractorTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.sequence; +package org.hibernate.orm.test.dialect.unit.sequence; import javax.persistence.Entity; import javax.persistence.Id; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DB2390SequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2390SequenceInformationExtractorTest.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DB2390SequenceInformationExtractorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2390SequenceInformationExtractorTest.java index 1f93aec438..13f0e8a53c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DB2390SequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2390SequenceInformationExtractorTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.sequence; +package org.hibernate.orm.test.dialect.unit.sequence; import org.hibernate.dialect.DB2390Dialect; import org.hibernate.dialect.Dialect; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DB2400SequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2400SequenceInformationExtractorTest.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DB2400SequenceInformationExtractorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2400SequenceInformationExtractorTest.java index a3fc3b45d4..c0bcb93a2a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DB2400SequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2400SequenceInformationExtractorTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.sequence; +package org.hibernate.orm.test.dialect.unit.sequence; import org.hibernate.dialect.DB2400Dialect; import org.hibernate.dialect.Dialect; diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenFiveDialectSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenFiveDialectSequenceInformationExtractorTest.java similarity index 87% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenFiveDialectSequenceInformationExtractorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenFiveDialectSequenceInformationExtractorTest.java index 868a43e7f0..75463a4990 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenFiveDialectSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenFiveDialectSequenceInformationExtractorTest.java @@ -4,9 +4,9 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.sequence; +package org.hibernate.orm.test.dialect.unit.sequence; -import org.hibernate.dialect.DerbyTenFiveDialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; @@ -21,7 +21,7 @@ public class DerbyTenFiveDialectSequenceInformationExtractorTest extends Abstrac @Override public Dialect getDialect() { - return new DerbyTenFiveDialect(); + return new DerbyDialect( 1050 ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenSevenDialectSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenSevenDialectSequenceInformationExtractorTest.java similarity index 89% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenSevenDialectSequenceInformationExtractorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenSevenDialectSequenceInformationExtractorTest.java index 166544245e..b93c6d9a65 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenSevenDialectSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenSevenDialectSequenceInformationExtractorTest.java @@ -4,9 +4,9 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.sequence; +package org.hibernate.orm.test.dialect.unit.sequence; -import org.hibernate.dialect.DerbyTenSevenDialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; @@ -20,7 +20,7 @@ import org.hibernate.testing.TestForIssue; public class DerbyTenSevenDialectSequenceInformationExtractorTest extends AbstractSequenceInformationExtractorTest { @Override public Dialect getDialect() { - return new DerbyTenSevenDialect(); + return new DerbyDialect( 1070 ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenSixDialectSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenSixDialectSequenceInformationExtractorTest.java similarity index 89% rename from hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenSixDialectSequenceInformationExtractorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenSixDialectSequenceInformationExtractorTest.java index a3835bddf5..e2aa3be84e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/unit/sequence/DerbyTenSixDialectSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DerbyTenSixDialectSequenceInformationExtractorTest.java @@ -4,9 +4,9 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.dialect.unit.sequence; +package org.hibernate.orm.test.dialect.unit.sequence; -import org.hibernate.dialect.DerbyTenSixDialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; @@ -20,7 +20,7 @@ import org.hibernate.testing.TestForIssue; public class DerbyTenSixDialectSequenceInformationExtractorTest extends AbstractSequenceInformationExtractorTest { @Override public Dialect getDialect() { - return new DerbyTenSixDialect(); + return new DerbyDialect( 1060 ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/engine/spi/EntityEntryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/engine/spi/EntityEntryTest.java similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/engine/spi/EntityEntryTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/engine/spi/EntityEntryTest.java index 6da291c62b..1bb3a62b0c 100644 --- a/hibernate-core/src/test/java/org/hibernate/engine/spi/EntityEntryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/engine/spi/EntityEntryTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.engine.spi; +package org.hibernate.orm.test.engine.spi; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -14,7 +14,12 @@ import java.io.ObjectOutputStream; import org.hibernate.LockMode; import org.hibernate.engine.internal.MutableEntityEntry; +import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.engine.spi.Status; +import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -33,7 +38,7 @@ public class EntityEntryTest { EntityEntry entityEntry = createEntityEntry(); assertEquals( LockMode.OPTIMISTIC, entityEntry.getLockMode() ); - assertEquals( Status.MANAGED, entityEntry.getStatus() ); + Assert.assertEquals( Status.MANAGED, entityEntry.getStatus() ); assertEquals( true, entityEntry.isExistsInDatabase() ); assertEquals( true, entityEntry.isBeingReplicated() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/EntityManagerClosedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/EntityManagerClosedTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/EntityManagerClosedTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/EntityManagerClosedTest.java index 015723d14a..8cdc72d728 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/EntityManagerClosedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/EntityManagerClosedTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test; +package org.hibernate.orm.test.jpa; import java.util.Date; import java.util.GregorianCalendar; @@ -17,6 +17,8 @@ import javax.persistence.Parameter; import javax.persistence.Query; import javax.persistence.TemporalType; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + import org.hibernate.testing.TestForIssue; import org.junit.Test; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/JPALockTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/JPALockTest.java index d32f896869..6ff351d11d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/JPALockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/JPALockTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.lock; +package org.hibernate.orm.test.jpa.lock; import org.hibernate.LockMode; import org.hibernate.Session; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Lock.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Lock.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Lock.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Lock.java index ee50e511f5..46373bd03d 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Lock.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Lock.java @@ -6,7 +6,7 @@ */ //$Id$ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/LockExceptionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/lock/LockExceptionTests.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java index 6f1a38fb7f..a20d7ad778 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/LockExceptionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java @@ -4,7 +4,7 @@ * 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.test.jpa.lock; +package org.hibernate.orm.test.jpa.lock; import java.util.Collections; import javax.persistence.LockModeType; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java index 79e48d8a5b..a168ffa05f 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import java.util.HashMap; import java.util.List; @@ -28,8 +28,8 @@ import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.HSQLDialect; -import org.hibernate.dialect.Oracle10gDialect; -import org.hibernate.dialect.PostgreSQL81Dialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.QueryHints; @@ -39,7 +39,6 @@ import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; -import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; import org.hibernate.testing.transaction.TransactionUtil; import org.hibernate.testing.util.ExceptionUtil; import org.jboss.logging.Logger; @@ -661,7 +660,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { } @Test - @RequiresDialect( Oracle10gDialect.class ) + @RequiresDialect( OracleDialect.class ) @RequiresDialectFeature( DialectChecks.SupportsLockTimeouts.class ) public void testContendedPessimisticReadLockTimeout() throws Exception { final CountDownLatch latch = new CountDownLatch( 1 ); @@ -743,7 +742,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { } @Test - @RequiresDialect( Oracle10gDialect.class ) + @RequiresDialect( OracleDialect.class ) @RequiresDialectFeature( DialectChecks.SupportsLockTimeouts.class ) public void testContendedPessimisticWriteLockTimeout() throws Exception { @@ -823,7 +822,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { } @Test - @RequiresDialect( { Oracle10gDialect.class, PostgreSQL81Dialect.class }) + @RequiresDialect( { OracleDialect.class, PostgreSQLDialect.class }) @RequiresDialectFeature( DialectChecks.SupportsLockTimeouts.class ) public void testContendedPessimisticWriteLockNoWait() throws Exception { @@ -903,7 +902,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { } @Test - @RequiresDialect( Oracle10gDialect.class ) + @RequiresDialect( OracleDialect.class ) @RequiresDialectFeature( DialectChecks.SupportsLockTimeouts.class ) public void testQueryTimeout() throws Exception { @@ -988,7 +987,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { } @Test - @RequiresDialect( Oracle10gDialect.class ) + @RequiresDialect( OracleDialect.class ) @RequiresDialectFeature( DialectChecks.SupportsLockTimeouts.class ) public void testQueryTimeoutEMProps() throws Exception { final CountDownLatch latch = new CountDownLatch( 1 ); @@ -1074,7 +1073,7 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase { } @Test - @RequiresDialect( Oracle10gDialect.class ) + @RequiresDialect( OracleDialect.class ) @RequiresDialectFeature( DialectChecks.SupportsLockTimeouts.class ) public void testLockTimeoutEMProps() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTimeoutPropertyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTimeoutPropertyTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTimeoutPropertyTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTimeoutPropertyTest.java index cfc7443436..a369dfdd8c 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/LockTimeoutPropertyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTimeoutPropertyTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import java.util.Map; import javax.persistence.EntityManager; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Lockable.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Lockable.java similarity index 95% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Lockable.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Lockable.java index 3302e90f37..67ddb3aaf6 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Lockable.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Lockable.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/NativeSQLQueryTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/NativeSQLQueryTimeoutTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/NativeSQLQueryTimeoutTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/NativeSQLQueryTimeoutTest.java index 4d816387e7..6938803fb3 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/NativeSQLQueryTimeoutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/NativeSQLQueryTimeoutTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.PostgreSQLDialect; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Person.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Person.java similarity index 95% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Person.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Person.java index 558fc68d6e..188f06b5a4 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/Person.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/Person.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import javax.persistence.Entity; import javax.persistence.GeneratedValue; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/QueryLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/QueryLockingTest.java old mode 100755 new mode 100644 similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/QueryLockingTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/QueryLockingTest.java index d683156c79..8b98ae3bbc --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/QueryLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/QueryLockingTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import java.util.List; import java.util.Map; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/RepeatableReadTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/RepeatableReadTest.java index b210e8f53f..5e01cc6eea 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/lock/RepeatableReadTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/RepeatableReadTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.lock; +package org.hibernate.orm.test.jpa.lock; import java.math.BigDecimal; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/StatementIsClosedAfterALockExceptionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/StatementIsClosedAfterALockExceptionTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/StatementIsClosedAfterALockExceptionTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/StatementIsClosedAfterALockExceptionTest.java index bb35f9b4a5..26cdb8e6d1 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/StatementIsClosedAfterALockExceptionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/StatementIsClosedAfterALockExceptionTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/UnversionedLock.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/UnversionedLock.java similarity index 95% rename from hibernate-core/src/test/java/org/hibernate/jpa/test/lock/UnversionedLock.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/UnversionedLock.java index 28d91aed36..68438ef8f7 100644 --- a/hibernate-core/src/test/java/org/hibernate/jpa/test/lock/UnversionedLock.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/UnversionedLock.java @@ -6,7 +6,7 @@ */ //$Id$ -package org.hibernate.jpa.test.lock; +package org.hibernate.orm.test.jpa.lock; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/UpgradeLockTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/UpgradeLockTest.java index 605ffda520..7dc9d2b3f6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/UpgradeLockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/UpgradeLockTest.java @@ -11,8 +11,6 @@ import java.util.concurrent.TimeUnit; import javax.persistence.EntityManager; import javax.persistence.LockModeType; -import org.hibernate.jpa.test.lock.Lock; - import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/JpaSchemaGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/JpaSchemaGeneratorTest.java index 425609e831..02086d19c9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/JpaSchemaGeneratorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/schemagen/JpaSchemaGeneratorTest.java @@ -150,7 +150,7 @@ public class JpaSchemaGeneratorTest extends EntityManagerFactoryBasedFunctionalT // We want a fresh db after emf close // Unfortunately we have to use this dirty hack because the db seems not to be closed otherwise settings.put( "hibernate.connection.url", "jdbc:h2:mem:db-schemagen" + schemagenNumber++ - + ";MVCC=TRUE;LOCK_TIMEOUT=10000" ); + + ";LOCK_TIMEOUT=10000" ); EntityManagerFactoryBuilder emfb = Bootstrap.getEntityManagerFactoryBuilder( buildPersistenceUnitDescriptor(), settings diff --git a/hibernate-core/src/test/java/org/hibernate/test/lob/BlobLocatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/BlobLocatorTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/lob/BlobLocatorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/lob/BlobLocatorTest.java index b7010a9ceb..1e94680b19 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lob/BlobLocatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/BlobLocatorTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.lob; +package org.hibernate.orm.test.lob; import java.sql.Blob; import java.util.Arrays; @@ -35,6 +35,11 @@ import static org.junit.Assert.assertNotNull; public class BlobLocatorTest extends BaseCoreFunctionalTestCase { private static final long BLOB_SIZE = 10000L; + @Override + protected String getBaseForMappings() { + return "org/hibernate/orm/test/"; + } + public String[] getMappings() { return new String[] { "lob/LobMappings.hbm.xml" }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/lob/ClobLocatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/ClobLocatorTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/lob/ClobLocatorTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/lob/ClobLocatorTest.java index 4d41358977..5883b0c21a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lob/ClobLocatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/ClobLocatorTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.lob; +package org.hibernate.orm.test.lob; import java.sql.Clob; @@ -37,6 +37,11 @@ import static org.junit.Assert.assertNotNull; public class ClobLocatorTest extends BaseCoreFunctionalTestCase { private static final int CLOB_SIZE = 10000; + @Override + protected String getBaseForMappings() { + return "org/hibernate/orm/test/"; + } + public String[] getMappings() { return new String[] { "lob/LobMappings.hbm.xml" }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/lob/LobHolder.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobHolder.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/lob/LobHolder.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobHolder.java index 4b10200b7e..3b8d84c694 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lob/LobHolder.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobHolder.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.lob; +package org.hibernate.orm.test.lob; import java.sql.Blob; import java.sql.Clob; diff --git a/hibernate-core/src/test/java/org/hibernate/test/lob/LobMappings.hbm.xml b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMappings.hbm.xml similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/test/lob/LobMappings.hbm.xml rename to hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMappings.hbm.xml index 4ad93cd21a..8f25efb5a3 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lob/LobMappings.hbm.xml +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMappings.hbm.xml @@ -9,7 +9,7 @@ "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> - + diff --git a/hibernate-core/src/test/java/org/hibernate/test/lob/LobMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMergeTest.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/test/lob/LobMergeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMergeTest.java index 36c23c3523..d24cedd7a9 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/lob/LobMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/lob/LobMergeTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.lob; +package org.hibernate.orm.test.lob; import java.util.Arrays; @@ -27,6 +27,11 @@ import static org.junit.Assert.assertTrue; public class LobMergeTest extends BaseCoreFunctionalTestCase { private static final int LOB_SIZE = 10000; + @Override + protected String getBaseForMappings() { + return "org/hibernate/orm/test/"; + } + public String[] getMappings() { return new String[] { "lob/LobMappings.hbm.xml" }; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/A.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/A.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/test/locking/A.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/A.java index 243c4982b4..da734a2d9b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/A.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/A.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import javax.persistence.Column; import javax.persistence.Entity; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/AbstractSkipLockedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/AbstractSkipLockedTest.java similarity index 88% rename from hibernate-core/src/test/java/org/hibernate/test/locking/AbstractSkipLockedTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/AbstractSkipLockedTest.java index 522756e987..11c3bc2f37 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/AbstractSkipLockedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/AbstractSkipLockedTest.java @@ -1,19 +1,23 @@ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import javax.persistence.Entity; import javax.persistence.Id; + import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.Session; -import org.hibernate.dialect.MySQL8Dialect; -import org.hibernate.dialect.Oracle8iDialect; -import org.hibernate.dialect.PostgreSQL95Dialect; -import org.hibernate.dialect.SQLServer2005Dialect; +import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.query.Query; + +import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.junit.Test; @@ -34,7 +38,8 @@ public abstract class AbstractSkipLockedTest @Test - @RequiresDialect({ SQLServer2005Dialect.class }) + @RequiresDialect({ SQLServerDialect.class }) + @RequiresDialectFeature(DialectChecks.SupportsSkipLocked.class) public void testSQLServerSkipLocked() { doInHibernate( this::sessionFactory, session -> { @@ -67,7 +72,8 @@ public abstract class AbstractSkipLockedTest } @Test - @RequiresDialect({ PostgreSQL95Dialect.class }) + @RequiresDialect({ PostgreSQLDialect.class }) + @RequiresDialectFeature(DialectChecks.SupportsSkipLocked.class) public void testPostgreSQLSkipLocked() { doInHibernate( this::sessionFactory, session -> { @@ -106,7 +112,8 @@ public abstract class AbstractSkipLockedTest } @Test - @RequiresDialect({ Oracle8iDialect.class }) + @RequiresDialect({ OracleDialect.class }) + @RequiresDialectFeature(DialectChecks.SupportsSkipLocked.class) public void testOracleSkipLocked() { doInHibernate( this::sessionFactory, session -> { @@ -141,7 +148,8 @@ public abstract class AbstractSkipLockedTest } @Test - @RequiresDialect({ MySQL8Dialect.class }) + @RequiresDialect({ MySQLDialect.class }) + @RequiresDialectFeature(DialectChecks.SupportsSkipLocked.class) public void testMySQLSkipLocked() { doInHibernate( this::sessionFactory, session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/HANAOptimisticLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/HANAOptimisticLockingTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/locking/HANAOptimisticLockingTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/HANAOptimisticLockingTest.java index 93eeaae962..dfc20ae3fa 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/HANAOptimisticLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/HANAOptimisticLockingTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import javax.persistence.Entity; import javax.persistence.GeneratedValue; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/JoinedInheritanceOptimisticForceIncrementTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/JoinedInheritanceOptimisticForceIncrementTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/locking/JoinedInheritanceOptimisticForceIncrementTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/JoinedInheritanceOptimisticForceIncrementTest.java index e626a692fe..98ef4c67da 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/JoinedInheritanceOptimisticForceIncrementTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/JoinedInheritanceOptimisticForceIncrementTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import javax.persistence.Column; import javax.persistence.Entity; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java index 50bf945e2a..128d2ff747 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/LockModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import java.util.Collections; import java.util.concurrent.CountDownLatch; @@ -23,6 +23,9 @@ import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; @@ -115,10 +118,12 @@ public class LockModeTest extends BaseCoreFunctionalTestCase { public void testCriteriaAliasSpecific() { // open a session, begin a transaction and lock row doInHibernate( this::sessionFactory, session -> { - CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); - CriteriaQuery criteria = criteriaBuilder.createQuery( A.class ); - criteria.from( A.class ); - A it = session.createQuery( criteria ).setLockMode("this",LockMode.PESSIMISTIC_WRITE ).uniqueResult(); + HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder(); + JpaCriteriaQuery criteria = criteriaBuilder.createQuery( A.class ); + ( (SqmPath) criteria.from( A.class ) ).setExplicitAlias( "this" ); + A it = session.createQuery( criteria ) + .setLockMode( "this", LockMode.PESSIMISTIC_WRITE ) + .uniqueResult(); // A it = (A) session.createCriteria( A.class ) // .setLockMode( "this", LockMode.PESSIMISTIC_WRITE ) @@ -179,7 +184,7 @@ public class LockModeTest extends BaseCoreFunctionalTestCase { public void testQueryLockModePessimisticWriteWithAlias() { doInHibernate( this::sessionFactory, session -> { // shouldn't throw an exception - session.createQuery( "SELECT MAX(a.id)+1 FROM A a where a.value = :value" ) + session.createQuery( "SELECT a.id+1 FROM A a where a.value = :value" ) .setLockMode( "a", LockMode.PESSIMISTIC_WRITE ) .setParameter( "value", "it" ) .list(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/LockRefreshTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/locking/LockRefreshTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshTest.java index 7d9a13ea71..d5c45bf644 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/LockRefreshTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockRefreshTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import java.util.Arrays; import javax.persistence.Column; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticReadSkipLockedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticReadSkipLockedTest.java similarity index 85% rename from hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticReadSkipLockedTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticReadSkipLockedTest.java index 817fd99b1a..d2d0716cea 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticReadSkipLockedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticReadSkipLockedTest.java @@ -1,4 +1,4 @@ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import org.hibernate.LockMode; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticWriteLockTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteLockTimeoutTest.java similarity index 85% rename from hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticWriteLockTimeoutTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteLockTimeoutTest.java index 7366630183..ebed5a3f44 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticWriteLockTimeoutTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteLockTimeoutTest.java @@ -1,13 +1,13 @@ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.Session; import org.hibernate.boot.SessionFactoryBuilder; -import org.hibernate.dialect.Oracle8iDialect; -import org.hibernate.dialect.PostgreSQL81Dialect; -import org.hibernate.dialect.PostgreSQL95Dialect; -import org.hibernate.dialect.SQLServer2005Dialect; +import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; + import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.jdbc.SQLStatementInterceptor; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; @@ -51,8 +51,7 @@ public class PessimisticWriteLockTimeoutTest } @Test - @RequiresDialect({ Oracle8iDialect.class, PostgreSQL81Dialect.class, - SQLServer2005Dialect.class } ) + @RequiresDialect({ OracleDialect.class, PostgreSQLDialect.class, SQLServerDialect.class } ) public void testNoWait() throws NoSuchFieldException, IllegalAccessException { @@ -77,7 +76,7 @@ public class PessimisticWriteLockTimeoutTest } @Test - @RequiresDialect({ Oracle8iDialect.class, PostgreSQL95Dialect.class }) + @RequiresDialect({ OracleDialect.class, PostgreSQLDialect.class }) public void testSkipLocked() throws NoSuchFieldException, IllegalAccessException { diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticWriteSkipLockedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteSkipLockedTest.java similarity index 85% rename from hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticWriteSkipLockedTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteSkipLockedTest.java index a81e17a0aa..ffac603f7b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/PessimisticWriteSkipLockedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/PessimisticWriteSkipLockedTest.java @@ -1,4 +1,4 @@ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import org.hibernate.LockMode; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/UpgradeSkipLockedTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/UpgradeSkipLockedTest.java similarity index 91% rename from hibernate-core/src/test/java/org/hibernate/test/locking/UpgradeSkipLockedTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/UpgradeSkipLockedTest.java index c8d3879748..5513de0b5c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/UpgradeSkipLockedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/UpgradeSkipLockedTest.java @@ -1,4 +1,4 @@ -package org.hibernate.test.locking; +package org.hibernate.orm.test.locking; import org.hibernate.LockMode; import org.hibernate.LockOptions; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/Door.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/Door.java index 8d7595de25..8f9c313493 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/Door.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/Door.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.locking.paging; +package org.hibernate.orm.test.locking.paging; import javax.persistence.Entity; import javax.persistence.Id; diff --git a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/PagingAndLockingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java similarity index 94% rename from hibernate-core/src/test/java/org/hibernate/test/locking/paging/PagingAndLockingTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java index 97e72358c2..547afc6688 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/locking/paging/PagingAndLockingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/paging/PagingAndLockingTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.locking.paging; +package org.hibernate.orm.test.locking.paging; import java.util.List; import javax.persistence.LockModeType; @@ -51,7 +51,7 @@ public class PagingAndLockingTest extends BaseCoreFunctionalTestCase { @After public void deleteTestData() { inTransaction( - s -> session.createQuery( "delete Door" ).executeUpdate() + s -> s.createQuery( "delete Door" ).executeUpdate() ); } @@ -90,7 +90,7 @@ public class PagingAndLockingTest extends BaseCoreFunctionalTestCase { .list(); assertEquals( 2, results.size() ); for ( Door door : results ) { - assertEquals( LockMode.PESSIMISTIC_WRITE, session.getCurrentLockMode( door ) ); + assertEquals( LockMode.PESSIMISTIC_WRITE, s.getCurrentLockMode( door ) ); } } ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/genericApi/BasicGetLoadAccessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/BasicGetLoadAccessTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/ops/genericApi/BasicGetLoadAccessTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/BasicGetLoadAccessTest.java index 3775fbf186..1813a62217 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/ops/genericApi/BasicGetLoadAccessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/BasicGetLoadAccessTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.ops.genericApi; +package org.hibernate.orm.test.ops.genericApi; import java.util.NoSuchElementException; import java.util.Optional; diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/genericApi/ProxiedGetLoadAccessTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/ProxiedGetLoadAccessTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/ops/genericApi/ProxiedGetLoadAccessTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/ProxiedGetLoadAccessTest.java index 1735096f72..805c8d89a0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/ops/genericApi/ProxiedGetLoadAccessTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/genericApi/ProxiedGetLoadAccessTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.ops.genericApi; +package org.hibernate.orm.test.ops.genericApi; import javax.persistence.Entity; import javax.persistence.GeneratedValue; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationArrayTest.java index 67d2b1832e..103100c0bb 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationArrayTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/QueryParametersValidationArrayTest.java @@ -68,7 +68,7 @@ public class QueryParametersValidationArrayTest { @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - @Column(columnDefinition = "ARRAY(1)") + @Column(columnDefinition = "ARRAY") private String[] readings; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/BaseSqmUnitTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/BaseSqmUnitTest.java index c086fc577d..a2a4496b1f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/BaseSqmUnitTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/BaseSqmUnitTest.java @@ -9,8 +9,10 @@ package org.hibernate.orm.test.query.sqm; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.loader.ast.spi.AfterLoadAction; import org.hibernate.metamodel.spi.MetamodelImplementor; +import org.hibernate.persister.entity.Loadable; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ast.spi.SqlAstCreationContext; @@ -48,6 +50,10 @@ public abstract class BaseSqmUnitTest public void registerAfterLoadAction(AfterLoadAction afterLoadAction) { } + @Override + public void invokeAfterLoadActions(SharedSessionContractImplementor session, Object entity, Loadable persister) { + } + protected SqmSelectStatement interpretSelect(String hql) { return interpretSelect( hql, sessionFactory() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java index 800e5f5add..bd5da0f00b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/mutation/multitable/IdSelectionTests.java @@ -201,8 +201,7 @@ public class IdSelectionTests { @Override public Callback getCallback() { - return afterLoadAction -> { - }; + throw new UnsupportedOperationException(); } } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java index 18d603980b..a2779cc681 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/DialectChecks.java @@ -190,6 +190,12 @@ abstract public class DialectChecks { } } + public static class SupportsSkipLocked implements DialectCheck { + public boolean isMatch(Dialect dialect) { + return dialect.supportsSkipLocked(); + } + } + public static class DoubleQuoteQuoting implements DialectCheck { @Override public boolean isMatch(Dialect dialect) { @@ -255,6 +261,12 @@ abstract public class DialectChecks { } } + public static class SupportWait implements DialectCheck { + public boolean isMatch(Dialect dialect) { + return dialect.supportsWait(); + } + } + public static class SupportDropConstraints implements DialectCheck { public boolean isMatch(Dialect dialect) { return dialect.dropConstraints(); diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java index c1ea6fb791..81baadf6fd 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java @@ -52,6 +52,7 @@ import org.hibernate.testing.OnFailure; import org.hibernate.testing.SkipLog; import org.hibernate.testing.cache.CachingRegionFactory; import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl; +import org.hibernate.testing.orm.junit.DialectContext; import org.hibernate.testing.transaction.TransactionUtil2; import org.junit.After; import org.junit.Before; @@ -68,7 +69,7 @@ import static org.junit.Assert.fail; public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { public static final String VALIDATE_DATA_CLEANUP = "hibernate.test.validateDataCleanup"; - public static final Dialect DIALECT = Dialect.getDialect(); + public static final Dialect DIALECT = DialectContext.getDialect(); private Configuration configuration; private StandardServiceRegistryImpl serviceRegistry; diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index 1390167206..8900dbbc0c 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -210,6 +210,12 @@ abstract public class DialectFeatureChecks { } } + public static class SupportsSkipLocked implements DialectFeatureCheck { + public boolean apply(Dialect dialect) { + return dialect.supportsSkipLocked(); + } + } + public static class DoubleQuoteQuoting implements DialectFeatureCheck { @Override public boolean apply(Dialect dialect) {