diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityVersionMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityVersionMapping.java index 3241838729..ea97d364db 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityVersionMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityVersionMapping.java @@ -9,6 +9,5 @@ package org.hibernate.metamodel.mapping; /** * @author Steve Ebersole */ -public interface EntityVersionMapping extends SingularAttributeMapping, - StateArrayContributorMapping, BasicValuedModelPart { +public interface EntityVersionMapping extends SingularAttributeMapping, StateArrayContributorMapping, BasicValuedModelPart { } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/VirtualModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/VirtualModelPart.java index 124e31ebe3..99d6257ca0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/VirtualModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/VirtualModelPart.java @@ -7,8 +7,7 @@ package org.hibernate.metamodel.mapping; /** - * Marker interface for parts of the application domain model that are virtual - do not - * actually exist in the model classes + * Marker interface for parts of the application domain model that do not actually exist in the model classes * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainMetamodelImpl.java index 7e1c2f46d5..5224a0c15b 100755 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/DomainMetamodelImpl.java @@ -68,6 +68,9 @@ import static org.hibernate.metamodel.internal.JpaStaticMetaModelPopulationSetti /** * Hibernate implementation of the JPA {@link javax.persistence.metamodel.Metamodel} contract. * + * Really more of the mapping model then the domain model, though it does have reference to the `JpaMetamodel` + * + * * @author Steve Ebersole * @author Emmanuel Bernard * @author Andrea Boriero @@ -700,6 +703,10 @@ public class DomainMetamodelImpl implements DomainMetamodel, MetamodelImplemento return (BasicType) sqmExpressable; } + if ( sqmExpressable instanceof EmbeddedSqmPathSource ) { + throw new NotYetImplementedFor6Exception( "Resolution of embedded-valued SqmExpressable nodes not yet implemented" ); + } + throw new UnsupportedOperationException( "Cannot determine proper mapping model expressable for " + sqmExpressable ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/DomainMetamodel.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/DomainMetamodel.java index d1bdfe1fb0..9cd2191535 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/DomainMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/DomainMetamodel.java @@ -10,21 +10,16 @@ import java.util.List; import java.util.function.Consumer; import org.hibernate.Incubating; -import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.graph.RootGraph; import org.hibernate.metamodel.mapping.MappingModelExpressable; -import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.model.domain.AllowableParameterType; -import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.SqmExpressable; -import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.type.BasicType; import org.hibernate.type.spi.TypeConfiguration; diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 2a8e01959f..533c922b3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -84,12 +84,13 @@ import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic; import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; -import org.hibernate.query.sqm.tree.expression.SqmEntityType; +import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteralNull; import org.hibernate.query.sqm.tree.expression.SqmNamedParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.query.sqm.tree.expression.SqmPathEntityType; import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter; import org.hibernate.query.sqm.tree.expression.SqmUnaryOperation; import org.hibernate.query.sqm.tree.from.DowncastLocation; @@ -1266,14 +1267,14 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implements SqmCre // 2) TYPE( :someParam ) if ( ctx.entityTypeReference().parameter() != null ) { // we have form (2) - return new SqmEntityType( + return new SqmParameterizedEntityType( (SqmParameter) ctx.entityTypeReference().parameter().accept( this ), creationContext.getNodeBuilder() ); } else if ( ctx.entityTypeReference().path() != null ) { // we have form (1) - return new SqmEntityType( + return new SqmPathEntityType( (SqmPath) ctx.entityTypeReference().path().accept( this ), creationContext.getNodeBuilder() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java index c4ba874fba..80e64cf3a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryEngine.java @@ -14,10 +14,8 @@ import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; -import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.query.spi.NativeQueryInterpreter; -import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.metamodel.model.domain.JpaMetamodel; @@ -28,22 +26,14 @@ import org.hibernate.query.hql.spi.SqmCreationOptions; import org.hibernate.query.internal.QueryInterpretationCacheDisabledImpl; import org.hibernate.query.internal.QueryInterpretationCacheStandardImpl; import org.hibernate.query.named.NamedQueryRepository; -import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.internal.SqmCreationOptionsStandard; import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry; import org.hibernate.query.sqm.spi.SqmCreationContext; -import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator; -import org.hibernate.query.sqm.sql.SimpleSqmUpdateTranslator; -import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator; -import org.hibernate.query.sqm.sql.SqmSelectTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; -import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteTranslator; -import org.hibernate.query.sqm.sql.internal.StandardSqmInsertSelectTranslator; -import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator; -import org.hibernate.query.sqm.sql.internal.StandardSqmUpdateTranslator; +import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory; import org.hibernate.service.ServiceRegistry; -import org.hibernate.sql.ast.spi.SqlAstCreationContext; /** * Aggregation and encapsulation of the components Hibernate uses @@ -53,20 +43,39 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext; */ @Incubating public class QueryEngine { + public static QueryEngine from( SessionFactoryImplementor sessionFactory, MetadataImplementor metadata) { + final SqmCreationContext sqmCreationContext = sessionFactory; + final SessionFactoryOptions queryEngineOptions = sessionFactory.getSessionFactoryOptions(); + final SqmCreationOptions sqmCreationOptions = new SqmCreationOptionsStandard( sessionFactory ); + + final Dialect dialect = sessionFactory.getJdbcServices().getDialect(); + final HqlTranslator hqlTranslator = resolveHqlTranslator( + queryEngineOptions, + dialect, + sqmCreationContext, + sqmCreationOptions + ); + + final SqmTranslatorFactory sqmTranslatorFactory = resolveSqmTranslatorFactory( queryEngineOptions, dialect ); + return new QueryEngine( sessionFactory.getJpaMetamodel(), - sessionFactory.getServiceRegistry(), - sessionFactory.getSessionFactoryOptions(), - sessionFactory, - new SqmCreationOptionsStandard( sessionFactory ), - sessionFactory.getProperties(), - metadata.buildNamedQueryRepository( sessionFactory ) + metadata.buildNamedQueryRepository( sessionFactory ), + hqlTranslator, + sqmTranslatorFactory, + sessionFactory.getServiceRegistry().getService( NativeQueryInterpreter.class ), + buildInterpretationCache( sessionFactory.getProperties() ), + dialect, + queryEngineOptions.getSqmFunctionRegistry(), + sessionFactory.getServiceRegistry() ); } + private final JpaMetamodel jpaMetamodel; + private final NamedQueryRepository namedQueryRepository; private final SqmCriteriaNodeBuilder criteriaBuilder; private final HqlTranslator hqlTranslator; @@ -76,50 +85,142 @@ public class QueryEngine { private final SqmFunctionRegistry sqmFunctionRegistry; public QueryEngine( - JpaMetamodel domainModel, - ServiceRegistry serviceRegistry, - SessionFactoryOptions runtimeOptions, - SqmCreationContext sqmCreationContext, - SqmCreationOptions sqmCreationOptions, - Map properties, - NamedQueryRepository namedQueryRepository) { - final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class ); - final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); - final Dialect dialect = jdbcEnvironment.getDialect(); - + JpaMetamodel jpaMetamodel, + NamedQueryRepository namedQueryRepository, + HqlTranslator hqlTranslator, + SqmTranslatorFactory sqmTranslatorFactory, + NativeQueryInterpreter nativeQueryInterpreter, + QueryInterpretationCache interpretationCache, + Dialect dialect, + SqmFunctionRegistry userDefinedRegistry, + ServiceRegistry serviceRegistry) { + this.jpaMetamodel = jpaMetamodel; this.namedQueryRepository = namedQueryRepository; - - this.hqlTranslator = resolveHqlTranslator( - runtimeOptions, - dialect, - sqmCreationContext, - sqmCreationOptions - ); - - this.sqmTranslatorFactory = resolveSqmTranslatorFactory( - runtimeOptions, - dialect, - sqmCreationContext, - sqmCreationOptions - ); + this.sqmTranslatorFactory = sqmTranslatorFactory; + this.nativeQueryInterpreter = nativeQueryInterpreter; + this.interpretationCache = interpretationCache; + this.hqlTranslator = hqlTranslator; this.criteriaBuilder = new SqmCriteriaNodeBuilder( this, - domainModel, + jpaMetamodel, serviceRegistry ); - this.nativeQueryInterpreter = serviceRegistry.getService( NativeQueryInterpreter.class ); + this.sqmFunctionRegistry = new SqmFunctionRegistry(); + dialect.initializeFunctionRegistry( this ); + if ( userDefinedRegistry != null ) { + userDefinedRegistry.overlay( sqmFunctionRegistry ); + } + } - this.interpretationCache = buildInterpretationCache( properties ); + /** + * Simplified constructor mainly meant for Quarkus use + */ + public QueryEngine( + JpaMetamodel jpaMetamodel, + boolean useStrictJpaCompliance, + NamedQueryRepository namedQueryRepository, + NativeQueryInterpreter nativeQueryInterpreter, + Dialect dialect, + ServiceRegistry serviceRegistry) { + this.jpaMetamodel = jpaMetamodel; + this.namedQueryRepository = namedQueryRepository; + this.sqmTranslatorFactory = null; + this.nativeQueryInterpreter = nativeQueryInterpreter; this.sqmFunctionRegistry = new SqmFunctionRegistry(); dialect.initializeFunctionRegistry( this ); - if ( runtimeOptions.getSqmFunctionRegistry() != null ) { - runtimeOptions.getSqmFunctionRegistry().overlay( sqmFunctionRegistry ); - } + + this.criteriaBuilder = new SqmCriteriaNodeBuilder( + this, + jpaMetamodel, + serviceRegistry + ); + + final SqmCreationContext sqmCreationContext = new SqmCreationContext() { + @Override + public JpaMetamodel getJpaMetamodel() { + return jpaMetamodel; + } + + @Override + public ServiceRegistry getServiceRegistry() { + return serviceRegistry; + } + + @Override + public QueryEngine getQueryEngine() { + return QueryEngine.this; + } + + @Override + public NodeBuilder getNodeBuilder() { + return criteriaBuilder; + } + }; + + //noinspection Convert2Lambda + this.hqlTranslator = new StandardHqlTranslator( + sqmCreationContext, + new SqmCreationOptions() { + @Override + public boolean useStrictJpaCompliance() { + return useStrictJpaCompliance; + } + } + ); + + this.interpretationCache = buildInterpretationCache( + serviceRegistry.getService( ConfigurationService.class ).getSettings() + ); } +// public QueryEngine( +// JpaMetamodel domainModel, +// ServiceRegistry serviceRegistry, +// SessionFactoryOptions runtimeOptions, +// SqmCreationContext sqmCreationContext, +// SqmCreationOptions sqmCreationOptions, +// Map properties, +// NamedQueryRepository namedQueryRepository) { +// final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class ); +// final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); +// final Dialect dialect = jdbcEnvironment.getDialect(); +// +// this.namedQueryRepository = namedQueryRepository; +// +// this.hqlTranslator = resolveHqlTranslator( +// runtimeOptions, +// dialect, +// sqmCreationContext, +// sqmCreationOptions +// ); +// +// this.sqmTranslatorFactory = resolveSqmTranslatorFactory( +// runtimeOptions, +// dialect, +// sqmCreationContext, +// sqmCreationOptions +// ); +// +// this.criteriaBuilder = new SqmCriteriaNodeBuilder( +// this, +// domainModel, +// serviceRegistry +// ); +// +// this.nativeQueryInterpreter = serviceRegistry.getService( NativeQueryInterpreter.class ); +// +// this.interpretationCache = buildInterpretationCache( properties ); +// +// this.sqmFunctionRegistry = new SqmFunctionRegistry(); +// dialect.initializeFunctionRegistry( this ); +// if ( runtimeOptions.getSqmFunctionRegistry() != null ) { +// runtimeOptions.getSqmFunctionRegistry().overlay( sqmFunctionRegistry ); +// } +// } + private static HqlTranslator resolveHqlTranslator( SessionFactoryOptions runtimeOptions, Dialect dialect, @@ -136,11 +237,9 @@ public class QueryEngine { return new StandardHqlTranslator( sqmCreationContext, sqmCreationOptions ); } - private SqmTranslatorFactory resolveSqmTranslatorFactory( + private static SqmTranslatorFactory resolveSqmTranslatorFactory( SessionFactoryOptions runtimeOptions, - Dialect dialect, - SqmCreationContext sqmCreationContext, - SqmCreationOptions sqmCreationOptions) { + Dialect dialect) { if ( runtimeOptions.getSqmTranslatorFactory() != null ) { return runtimeOptions.getSqmTranslatorFactory(); } @@ -149,68 +248,7 @@ public class QueryEngine { return dialect.getSqmTranslatorFactory(); } - return new SqmTranslatorFactory() { - @Override - public SqmSelectTranslator createSelectTranslator( - QueryOptions queryOptions, - DomainParameterXref domainParameterXref, - QueryParameterBindings domainParameterBindings, - LoadQueryInfluencers influencers, - SqlAstCreationContext creationContext) { - return new StandardSqmSelectTranslator( - queryOptions, - domainParameterXref, - domainParameterBindings, - influencers, - creationContext - ); - } - - @Override - public SimpleSqmDeleteTranslator createSimpleDeleteTranslator( - QueryOptions queryOptions, - DomainParameterXref domainParameterXref, - QueryParameterBindings domainParameterBindings, - LoadQueryInfluencers influencers, - SqlAstCreationContext creationContext) { - return new StandardSqmDeleteTranslator( - creationContext, - queryOptions, - domainParameterXref, - domainParameterBindings - ); - } - - @Override - public SqmInsertSelectTranslator createInsertSelectTranslator( - QueryOptions queryOptions, - DomainParameterXref domainParameterXref, - QueryParameterBindings domainParameterBindings, - LoadQueryInfluencers influencers, - SqlAstCreationContext creationContext) { - return new StandardSqmInsertSelectTranslator( - creationContext, - queryOptions, - domainParameterXref, - domainParameterBindings - ); - } - - @Override - public SimpleSqmUpdateTranslator createSimpleUpdateTranslator( - QueryOptions queryOptions, - DomainParameterXref domainParameterXref, - QueryParameterBindings queryParameterBindings, - LoadQueryInfluencers loadQueryInfluencers, - SessionFactoryImplementor factory) { - return new StandardSqmUpdateTranslator( - factory, - queryOptions, - domainParameterXref, - queryParameterBindings - ); - } - }; + return new StandardSqmTranslatorFactory(); } private static QueryInterpretationCache buildInterpretationCache(Map properties) { 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 2edacf1367..e2365032de 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 @@ -27,14 +27,14 @@ import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; -import org.hibernate.query.sqm.tree.expression.SqmEntityType; +import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType; import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral; -import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; import org.hibernate.query.sqm.tree.expression.SqmNamedParameter; +import org.hibernate.query.sqm.tree.expression.SqmPathEntityType; import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter; import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression; import org.hibernate.query.sqm.tree.expression.SqmTuple; @@ -190,7 +190,9 @@ public interface SemanticQueryWalker { T visitEntityTypeLiteralExpression(SqmLiteralEntityType expression); - T visitParameterizedEntityTypeExpression(SqmEntityType expression); + T visitSqmPathEntityTypeExpression(SqmPathEntityType expression); + + T visitParameterizedEntityTypeExpression(SqmParameterizedEntityType expression); T visitUnaryOperationExpression(SqmUnaryOperation expression); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java index 162c302bfe..f6763942a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmTreePrinter.java @@ -32,13 +32,14 @@ import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter; -import org.hibernate.query.sqm.tree.expression.SqmEntityType; +import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType; import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; import org.hibernate.query.sqm.tree.expression.SqmNamedParameter; +import org.hibernate.query.sqm.tree.expression.SqmPathEntityType; import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter; import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression; import org.hibernate.query.sqm.tree.expression.SqmTuple; @@ -594,7 +595,7 @@ public class SqmTreePrinter implements SemanticQueryWalker { } @Override - public Object visitParameterizedEntityTypeExpression(SqmEntityType expression) { + public Object visitParameterizedEntityTypeExpression(SqmParameterizedEntityType expression) { return null; } @@ -892,6 +893,11 @@ public class SqmTreePrinter implements SemanticQueryWalker { throw new NotYetImplementedFor6Exception( getClass() ); } + @Override + public Object visitSqmPathEntityTypeExpression(SqmPathEntityType expression) { + return null; + } + @Override public Object visitFullyQualifiedClass(Class namedClass) { return null; 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 5e12a0f827..90b2c39b5c 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 @@ -35,13 +35,14 @@ import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic; import org.hibernate.query.sqm.tree.expression.SqmCaseSearched; import org.hibernate.query.sqm.tree.expression.SqmCaseSimple; import org.hibernate.query.sqm.tree.expression.SqmCollectionSize; -import org.hibernate.query.sqm.tree.expression.SqmEntityType; +import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType; import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral; import org.hibernate.query.sqm.tree.expression.SqmExpression; import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteral; import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; import org.hibernate.query.sqm.tree.expression.SqmNamedParameter; +import org.hibernate.query.sqm.tree.expression.SqmPathEntityType; import org.hibernate.query.sqm.tree.expression.SqmPositionalParameter; import org.hibernate.query.sqm.tree.expression.SqmRestrictedSubQueryExpression; import org.hibernate.query.sqm.tree.expression.SqmTuple; @@ -80,6 +81,7 @@ 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.expression.Expression; /** * Base support for an SQM walker @@ -457,7 +459,12 @@ public class BaseSemanticQueryWalker implements SemanticQueryWalker { } @Override - public Object visitParameterizedEntityTypeExpression(SqmEntityType expression) { + public Object visitSqmPathEntityTypeExpression(SqmPathEntityType expression) { + return expression; + } + + @Override + public Object visitParameterizedEntityTypeExpression(SqmParameterizedEntityType expression) { return expression; } 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 adbb2474d6..3aba25bc32 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,6 +25,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.BinaryArithmeticOperator; import org.hibernate.query.NavigablePath; @@ -794,12 +795,15 @@ public abstract class BaseSqmToSqlAstConverter this.jdbcParameters.addParameters( jdbcParametersForSqm ); this.jdbcParamsBySqmParam.put( sqmParameter, jdbcParametersForSqm ); + final QueryParameterImplementor queryParameter = domainParameterXref.getQueryParameter( sqmParameter ); + final QueryParameterBinding binding = domainParameterBindings.getBinding( queryParameter ); + return new SqmParameterInterpretation( sqmParameter, - domainParameterXref.getQueryParameter( sqmParameter ), + queryParameter, jdbcParametersForSqm, valueMapping, - domainParameterBindings::getBinding + qp -> binding ); } @@ -871,6 +875,10 @@ public abstract class BaseSqmToSqlAstConverter return (BasicValuedMapping) parameterSqmType; } + if ( parameterSqmType instanceof EmbeddedSqmPathSource ) { + throw new NotYetImplementedFor6Exception( "Support for embedded-valued parameters not yet implemented" ); + } + throw new ConversionException( "Could not determine ValueMapping for SqmParameter: " + sqmParameter ); } @@ -1391,10 +1399,8 @@ public abstract class BaseSqmToSqlAstConverter } @Override - public Object visitSubQueryExpression(SqmSubQuery sqmSubQuery) { - throw new NotYetImplementedFor6Exception( getClass() ); - -// final QuerySpec subQuerySpec = visitQuerySpec( sqmSubQuery.getQuerySpec() ); + public QuerySpec visitSubQueryExpression(SqmSubQuery sqmSubQuery) { + return visitQuerySpec( sqmSubQuery.getQuerySpec() ); // // final ExpressableType expressableType = determineExpressableType( sqmSubQuery ); // diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/StandardSqmTranslatorFactory.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/StandardSqmTranslatorFactory.java new file mode 100644 index 0000000000..c62db9e83b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/StandardSqmTranslatorFactory.java @@ -0,0 +1,86 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import org.hibernate.engine.spi.LoadQueryInfluencers; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.spi.QueryOptions; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.query.sqm.internal.DomainParameterXref; +import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteTranslator; +import org.hibernate.query.sqm.sql.internal.StandardSqmInsertSelectTranslator; +import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator; +import org.hibernate.query.sqm.sql.internal.StandardSqmUpdateTranslator; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; + +/** + * Standard implementation of the SqmTranslatorFactory + * + * @author Steve Ebersole + */ +public class StandardSqmTranslatorFactory implements SqmTranslatorFactory { + @Override + public SqmSelectTranslator createSelectTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings, + LoadQueryInfluencers influencers, + SqlAstCreationContext creationContext) { + return new StandardSqmSelectTranslator( + queryOptions, + domainParameterXref, + domainParameterBindings, + influencers, + creationContext + ); + } + + @Override + public SimpleSqmDeleteTranslator createSimpleDeleteTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings, + LoadQueryInfluencers influencers, + SqlAstCreationContext creationContext) { + return new StandardSqmDeleteTranslator( + creationContext, + queryOptions, + domainParameterXref, + domainParameterBindings + ); + } + + @Override + public SqmInsertSelectTranslator createInsertSelectTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings, + LoadQueryInfluencers influencers, + SqlAstCreationContext creationContext) { + return new StandardSqmInsertSelectTranslator( + creationContext, + queryOptions, + domainParameterXref, + domainParameterBindings + ); + } + + @Override + public SimpleSqmUpdateTranslator createSimpleUpdateTranslator( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings queryParameterBindings, + LoadQueryInfluencers loadQueryInfluencers, + SessionFactoryImplementor factory) { + return new StandardSqmUpdateTranslator( + factory, + queryOptions, + domainParameterXref, + queryParameterBindings + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java index cbba740f94..d0cea37775 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java @@ -22,6 +22,7 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstWalker; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.update.Assignment; @@ -39,7 +40,7 @@ public class BasicValuedPathInterpretation implements AssignableSqmPathInterp SqmBasicValuedSimplePath sqmPath, SqlAstCreationState sqlAstCreationState, SemanticQueryWalker sqmWalker) { - final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().findTableGroup( sqmPath.getLhs().getNavigablePath() ); + final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( sqmPath.getLhs().getNavigablePath() ); final BasicValuedModelPart mapping = (BasicValuedModelPart) tableGroup.getModelPart().findSubPart( sqmPath.getReferencedPathSource().getPathName(), @@ -48,7 +49,7 @@ public class BasicValuedPathInterpretation implements AssignableSqmPathInterp final TableReference tableReference = tableGroup.resolveTableReference( mapping.getContainingTableExpression() ); - final ColumnReference columnReference = (ColumnReference) sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression( + final Expression expression = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression( SqlExpressionResolver.createColumnReferenceKey( tableReference, mapping.getMappedColumnExpression() @@ -61,6 +62,19 @@ public class BasicValuedPathInterpretation implements AssignableSqmPathInterp ) ); + final ColumnReference columnReference; + if ( expression instanceof ColumnReference ) { + columnReference = ( (ColumnReference) expression ); + } + else if ( expression instanceof SqlSelectionExpression ) { + final Expression selectedExpression = ( (SqlSelectionExpression) expression ).getExpression(); + assert selectedExpression instanceof ColumnReference; + columnReference = (ColumnReference) selectedExpression; + } + else { + throw new UnsupportedOperationException( "Unsupported basic-valued path expression : " + expression ); + } + return new BasicValuedPathInterpretation<>( columnReference, sqmPath, mapping, tableGroup ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java index f1593c0459..d33db3b39a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectTranslator.java @@ -35,6 +35,7 @@ import org.hibernate.query.sqm.sql.SqmSelectTranslation; import org.hibernate.query.sqm.sql.SqmSelectTranslator; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType; +import org.hibernate.query.sqm.tree.expression.SqmPathEntityType; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument; @@ -415,6 +416,15 @@ public class StandardSqmSelectTranslator return new EntityTypeLiteral( mappingDescriptor ); } + @Override + public Expression visitSqmPathEntityTypeExpression(SqmPathEntityType sqmExpression) { + return BasicValuedPathInterpretation.from( + sqmExpression, + this, + this + ); + } + @Override public Object visitFullyQualifiedClass(Class namedClass) { throw new NotYetImplementedFor6Exception(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEntityType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java similarity index 73% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEntityType.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java index ed9e30ae74..a5ec0b49c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmParameterizedEntityType.java @@ -18,18 +18,16 @@ import org.hibernate.query.sqm.tree.select.SqmSelectableNode; * * @author Steve Ebersole */ -public class SqmEntityType extends AbstractSqmExpression implements SqmSelectableNode { +public class SqmParameterizedEntityType extends AbstractSqmExpression implements SqmSelectableNode { private final SqmExpression discriminatorSource; - public SqmEntityType(SqmParameter parameterExpression, NodeBuilder nodeBuilder) { - super( parameterExpression.getAnticipatedType(), nodeBuilder ); - this.discriminatorSource = parameterExpression; + public SqmExpression getDiscriminatorSource() { + return discriminatorSource; } - public SqmEntityType(SqmPath entityValuedPath, NodeBuilder nodeBuilder) { - //noinspection unchecked - super( entityValuedPath.getReferencedPathSource().sqmAs( EntityDomainType.class ), nodeBuilder ); - this.discriminatorSource = entityValuedPath; + public SqmParameterizedEntityType(SqmParameter parameterExpression, NodeBuilder nodeBuilder) { + super( parameterExpression.getAnticipatedType(), nodeBuilder ); + this.discriminatorSource = parameterExpression; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPathEntityType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPathEntityType.java new file mode 100644 index 0000000000..1b1474b4d6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmPathEntityType.java @@ -0,0 +1,44 @@ +/* + * 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.tree.expression; + +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; +import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.SemanticQueryWalker; +import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; +import org.hibernate.query.sqm.tree.domain.SqmPath; + +/** + * @author Steve Ebersole + */ +public class SqmPathEntityType extends SqmBasicValuedSimplePath { + public SqmPathEntityType(SqmPath entityValuedPath, NodeBuilder nodeBuilder) { + super( + entityValuedPath.getNavigablePath().append( EntityDiscriminatorMapping.ROLE_NAME ), + // todo (6.0) : need a BasicValueSqmPathSource representing the discriminator for this to work. + null, + entityValuedPath, + nodeBuilder + ); + + throw new NotYetImplementedFor6Exception( getClass() ); + +// //noinspection unchecked +// super( +// , +// ( (EntityDomainType) entityValuedPath.getNodeType() ).getDiscriminatorDescriptor(), +// entityValuedPath, +// nodeBuilder +// ); + } + + @Override + public X accept(SemanticQueryWalker walker) { + return walker.visitSqmPathEntityTypeExpression( this ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java index 4ac006f7f3..e87278d9d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/select/QuerySpec.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.sql.ast.spi.SqlAstTreeHelper; import org.hibernate.sql.ast.spi.SqlAstWalker; import org.hibernate.sql.ast.tree.SqlAstNode; @@ -22,7 +23,7 @@ import org.hibernate.sql.ast.tree.predicate.PredicateContainer; /** * @author Steve Ebersole */ -public class QuerySpec implements SqlAstNode, PredicateContainer, CteConsumer { +public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, CteConsumer { private final boolean isRoot; private final FromClause fromClause; @@ -105,4 +106,12 @@ public class QuerySpec implements SqlAstNode, PredicateContainer, CteConsumer { public void accept(SqlAstWalker sqlTreeWalker) { sqlTreeWalker.visitQuerySpec( this ); } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Expression + + @Override + public MappingModelExpressable getExpressionType() { + return null; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultAssembler.java index d6a6b60d93..6abb0cb67b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultAssembler.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/basic/BasicResultAssembler.java @@ -42,11 +42,18 @@ public class BasicResultAssembler implements DomainResultAssembler { this.valueConverter = valueConverter; } + /** + * Access to the raw value (unconverted, if a converter applied) + */ + public Object extractRawValue(RowProcessingState rowProcessingState) { + return rowProcessingState.getJdbcValue( valuesArrayPosition ); + } + @Override public J assemble( RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { - Object jdbcValue = rowProcessingState.getJdbcValue( valuesArrayPosition ); + Object jdbcValue = extractRawValue( rowProcessingState ); SqlResultsLogger.INSTANCE.debugf( "Extracted JDBC value [%d] - [%s]", valuesArrayPosition, jdbcValue ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java index 93e9a1649f..2a2764dfa4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/composite/CompositeFetch.java @@ -8,10 +8,13 @@ package org.hibernate.sql.results.internal.domain.composite; import java.util.function.Consumer; +import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.JoinType; +import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.results.internal.domain.AbstractFetchParent; import org.hibernate.sql.results.spi.AssemblerCreationState; import org.hibernate.sql.results.spi.CompositeResultMappingNode; @@ -44,9 +47,25 @@ public class CompositeFetch extends AbstractFetchParent implements CompositeResu this.fetchTiming = fetchTiming; this.nullable = nullable; - creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup( + creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( getNavigablePath(), - creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup( fetchParent.getNavigablePath() ) + np -> { + final TableGroupJoin tableGroupJoin = getFetchContainer().createTableGroupJoin( + getNavigablePath(), + creationState.getSqlAstCreationState() + .getFromClauseAccess() + .findTableGroup( fetchParent.getNavigablePath() ), + null, + nullable ? JoinType.LEFT : JoinType.INNER, + LockMode.NONE, + stem -> creationState.getSqlAliasBaseManager().createSqlAliasBase( stem ), + creationState.getSqlAstCreationState().getSqlExpressionResolver(), + creationState.getSqlAstCreationState().getCreationContext() + ); + + return tableGroupJoin.getJoinedGroup(); + } + ); afterInitialize( creationState ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/intg/package-info.java b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/package-info.java new file mode 100644 index 0000000000..0855da9e75 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/package-info.java @@ -0,0 +1,15 @@ +/* + * 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 + */ + +/** + * Tests verifying that expectations of various integrations continue to work. + * + * E.g. make sure that SQM trees can + * continue to be built without access to SessionFactory; Quarkus and others use this to translate HQL statements + * into SQM at boot time or even build time + */ +package org.hibernate.orm.test.intg; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/intg/sqm/HqlTranslationNoFactoryTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/sqm/HqlTranslationNoFactoryTests.java new file mode 100644 index 0000000000..2d316f701d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/sqm/HqlTranslationNoFactoryTests.java @@ -0,0 +1,145 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.intg.sqm; + +import java.util.Collections; +import java.util.Optional; + +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.internal.JpaStaticMetaModelPopulationSetting; +import org.hibernate.metamodel.model.domain.JpaMetamodel; +import org.hibernate.metamodel.model.domain.internal.JpaMetamodelImpl; +import org.hibernate.metamodel.spi.DomainMetamodel; +import org.hibernate.metamodel.spi.RuntimeModelCreationContext; +import org.hibernate.query.hql.HqlTranslator; +import org.hibernate.query.hql.internal.StandardHqlTranslator; +import org.hibernate.query.hql.spi.SqmCreationOptions; +import org.hibernate.query.internal.NamedQueryRepositoryImpl; +import org.hibernate.query.spi.QueryEngine; +import org.hibernate.query.sqm.spi.SqmCreationContext; +import org.hibernate.query.sqm.tree.SqmStatement; +import org.hibernate.type.spi.TypeConfiguration; + +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.ServiceRegistryScope; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * Tests making sure that HQL queries can fully be translated without a SessionFactory. + * + * todo (6.0) : have a way to directly load these translations into the {@link org.hibernate.query.named.NamedQueryRepository} + * directly. For example Quarkus could store its build-time translations and directly here during boot-strap of the SF + * + * @author Steve Ebersole + */ +@ServiceRegistry +@DomainModel( standardModels = StandardDomainModel.RETAIL ) +public class HqlTranslationNoFactoryTests { + @Test + @FailureExpected( reason = "Building the JpaDomain") + public void testHqlTranslationNoSessionFactory(DomainModelScope modelScope, ServiceRegistryScope registryScope) { + final String hql = "select a from SalesAssociate a"; + + final HqlTranslator hqlTranslator = buildHqlTranslator( modelScope, registryScope ); + final SqmStatement sqmStatement = hqlTranslator.translate( hql ); + assert sqmStatement != null; + } + + private HqlTranslator buildHqlTranslator(DomainModelScope modelScope, ServiceRegistryScope registryScope) { + final MetadataImplementor bootModel = modelScope.getDomainModel(); + final TypeConfiguration typeConfiguration = bootModel.getTypeConfiguration(); + + final JpaMetamodelImpl jpaMetamodel = new JpaMetamodelImpl( typeConfiguration ); + + // todo (6.0) (quarkus) : we should limit the type of the last argument here from `RuntimeModelCreationContext` + // which assumes access to SessionFactory + jpaMetamodel.processJpa( + bootModel, + Collections.emptyMap(), + JpaStaticMetaModelPopulationSetting.ENABLED, + Collections.emptyList(), + new RuntimeModelCreationContext() { + @Override + public SessionFactoryImplementor getSessionFactory() { + throw new UnsupportedOperationException( "SessionFactory not available" ); + } + + @Override + public BootstrapContext getBootstrapContext() { + return typeConfiguration.getMetadataBuildingContext().getBootstrapContext(); + } + + @Override + public MetadataImplementor getBootModel() { + return modelScope.getDomainModel(); + } + + @Override + public DomainMetamodel getDomainModel() { + throw new UnsupportedOperationException( "DomainMetamodel not available" ); + } + } + ); + + Optional queryEngineAccess = Optional.empty(); + + // todo (6.0) (quarkus) : this circularity is problematic as well + // between `SqmCreationContext#getQueryEngine` and the `SqmCreationContext` passed to `#QueryEngine` + final SqmCreationContext sqmCreationContext = new SqmCreationContext() { + @Override + public JpaMetamodel getJpaMetamodel() { + return jpaMetamodel; + } + + @Override + public QueryEngine getQueryEngine() { + return queryEngineAccess.orElseThrow( () -> new RuntimeException( "Unexpected access to `SqmCreationContext#getQueryEngine`" ) ); + } + }; + + // we don't want strict JPA query compliance + final SqmCreationOptions sqmCreationOptions = () -> false; + + final QueryEngine queryEngine = new QueryEngine( + jpaMetamodel, + // we don't want strict JPA query compliance + false, + new NamedQueryRepositoryImpl( Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap() ), + // NativeQueryInterpreter + null, + // this is exclusively to build the SqmFunctionRegistry, maybe Quarkus should just build it directly and pass + registryScope.getRegistry().getService( JdbcServices.class ).getDialect(), + registryScope.getRegistry() + ); + + return new StandardHqlTranslator( + new SqmCreationContext() { + @Override + public JpaMetamodel getJpaMetamodel() { + return jpaMetamodel; + } + + @Override + public QueryEngine getQueryEngine() { + return queryEngine; + } + }, + sqmCreationOptions + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/DateQueryParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/DateQueryParameterTest.java new file mode 100644 index 0000000000..123e348840 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/DateQueryParameterTest.java @@ -0,0 +1,123 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.hql; + +import java.util.Date; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Chris Cranford + */ +public class DateQueryParameterTest extends SessionFactoryBasedFunctionalTest { + @Entity(name = "DateEntity") + public static class DateEntity { + @Id + @GeneratedValue + private Integer id; + private Date timestamp; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Date getTimestamp() { + return timestamp; + } + + public void setTimestamp(Date timestamp) { + this.timestamp = timestamp; + } + } + + private long timestamp1; + private long timestamp2; + private long timestamp3; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { DateEntity.class }; + } + + @Test + public void testDateEntityQuery() throws Exception { + try { + timestamp1 = System.currentTimeMillis(); + + inTransaction( + session -> { + final DateEntity entity = new DateEntity(); + entity.setTimestamp( new Date() ); + session.save( entity ); + } + ); + + timestamp2 = System.currentTimeMillis(); + Thread.sleep( 1100 ); + + inTransaction( + session -> { + final DateEntity entity = new DateEntity(); + entity.setTimestamp( new Date() ); + session.save( entity ); + } + ); + + timestamp3 = System.currentTimeMillis(); + + inTransaction( + session -> { + // Test nothing before timestamp1 + List results = session + .createQuery( "SELECT e FROM DateEntity e WHERE e.timestamp < :value", DateEntity.class ) + .setParameter( "value", new Date( timestamp1 ) ) + .getResultList(); + assertEquals( results.size(), 0 ); + + // Test only one entry before timestamp2 + results = session + .createQuery( "SELECT e FROM DateEntity e WHERE e.timestamp < : value", DateEntity.class ) + .setParameter( "value", new Date( timestamp2 ) ) + .getResultList(); + assertEquals( results.size(), 1 ); + + // Test two entries before timestamp3 + results = session + .createQuery( "SELECT e FROM DateEntity e WHERE e.timestamp < : value", DateEntity.class ) + .setParameter( "value", new Date( timestamp3 ) ) + .getResultList(); + assertEquals( results.size(), 2 ); + } + ); + } + catch ( InterruptedException e ) { + throw new RuntimeException( "Failed to thread sleep properly", e ); + } + } + + @AfterEach + public void cleanUpData() { + inTransaction( + session -> { + session.createQuery( "delete DateEntity" ).executeUpdate(); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/EmbeddableAsParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/EmbeddableAsParameterTest.java new file mode 100644 index 0000000000..74ef60d7c8 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/EmbeddableAsParameterTest.java @@ -0,0 +1,159 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.hql; + +import java.util.List; +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Andrea Boriero + */ +@FailureExpected( reason = "Support for embedded-valued parameters not yet implemented" ) +public class EmbeddableAsParameterTest extends SessionFactoryBasedFunctionalTest { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class, + }; + } + + @Test + public void testAsParameterInWhereClause() { + inTransaction( + session -> { + List results = session.createQuery( "select p from Person p where p.name = :name" ). + setParameter( "name", new Name( "Fab", "Fab" ) ).list(); + assertThat( results.size(), is( 1 ) ); + } + ); + } + + @Test + public void testAsParameterReuseInWhereClause() { + inTransaction( + session -> { + List results = session.createQuery( "select p from Person p where p.name = :name or p.name = :name " ). + setParameter( "name", new Name( "Fab", "Fab" ) ).list(); + assertThat( results.size(), is( 1 ) ); + } + ); + } + + @BeforeEach + public void setUp() { + inTransaction( + session -> { + Person person = new Person( + 1, + new Name( "Fab", "Fab" ), + 33 + + ); + session.save( person ); + } ); + } + + @AfterEach + public void tearDown() { + inTransaction( + session -> { + session.createQuery( "delete Person" ).executeUpdate(); + } + ); + } + + @Entity(name = "Person") + public static class Person { + @Id + private Integer id; + + private Name name; + + private Integer age; + + public Person() { + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + public Person(Integer id, Name name, Integer age) { + this.id = id; + this.name = name; + this.age = age; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Name getName() { + return name; + } + + public void setName(Name name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + } + + + @Embeddable + public static class Name { + private String firstName; + private String secondName; + + public Name() { + } + + public Name(String firstName, String secondName) { + this.firstName = firstName; + this.secondName = secondName; + } + + public String getFirstName() { + return firstName; + } + + public String getSecondName() { + return secondName; + } + + public void setSecondName(String secondName) { + this.secondName = secondName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java deleted file mode 100644 index 4dfb10d5ea..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ /dev/null @@ -1,22 +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.orm.test.query.hql; - -import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest; -import org.hibernate.orm.test.query.sqm.domain.Person; - -/** - * @author Steve Ebersole - */ -public class FunctionTests extends BaseSqmUnitTest { - @Override - protected Class[] getAnnotatedClasses() { - return new Class[] { Person.class }; - } - - -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/NamedHqlQueriesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/NamedHqlQueriesTest.java new file mode 100644 index 0000000000..de85bb76dd --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/NamedHqlQueriesTest.java @@ -0,0 +1,150 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.hql; + +import java.time.LocalDate; +import java.time.Month; +import java.util.List; +import java.util.Objects; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.annotations.NamedQueries; +import org.hibernate.annotations.NamedQuery; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; + +public class NamedHqlQueriesTest extends SessionFactoryBasedFunctionalTest { + + final VideoGame GOD_OF_WAR = new VideoGame( "GOW_2018", "God of war", LocalDate.of( 2018, Month.APRIL, 20 ) ); + final VideoGame THE_LAST_OF_US = new VideoGame( + "TLOU_2013", "The last of us", LocalDate.of( 2013, Month.JUNE, 14 ) ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + VideoGame.class, + }; + } + + @Test + public void testQueryWithoutParameters() { + inTransaction( + session -> { + List results = session.createNamedQuery( "videogames" ).list(); + assertThat( results, containsInAnyOrder( GOD_OF_WAR, THE_LAST_OF_US ) ); + } ); + } + + @Test + public void testQueryWithSingleParameters() { + inTransaction( + session -> { + List results = session.createNamedQuery( "title" ) + .setParameter("title", GOD_OF_WAR.getTitle() ) + .list(); + assertThat( results, contains( GOD_OF_WAR ) ); + } ); + } + + @BeforeEach + public void setUp() { + inTransaction( + session -> { + session.save( GOD_OF_WAR ); + session.save( THE_LAST_OF_US ); + } ); + } + + @AfterEach + public void tearDown() { + inTransaction( + session -> { + session.createQuery( "from VideoGame vg" ) + .list() + .forEach( vg -> session.delete( vg ) ); + } ); + } + + @Entity(name = "VideoGame") + @NamedQueries({ + @NamedQuery(name = "videogames", query = "select vg from VideoGame vg"), + @NamedQuery(name = "title", query = "select vg from VideoGame vg where vg.title=:title") + }) + static class VideoGame { + + public VideoGame() { + } + + public VideoGame(String id, String title, LocalDate releaseDate) { + this.id = id; + this.title = title; + this.releaseDate = releaseDate; + } + + @Id + private String id; + private String title; + private LocalDate releaseDate; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public LocalDate getReleaseDate() { + return releaseDate; + } + + public void setReleaseDate(LocalDate releaseDate) { + this.releaseDate = releaseDate; + } + + + @Override + public String toString() { + return title; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + VideoGame videoGame = (VideoGame) o; + return Objects.equals( id, videoGame.id ) && + Objects.equals( title, videoGame.title ) && + Objects.equals( releaseDate, videoGame.releaseDate ); + } + + @Override + public int hashCode() { + return Objects.hash( id, title, releaseDate ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/SubqueryLimitOffsetTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/SubqueryLimitOffsetTest.java new file mode 100644 index 0000000000..2ef0d67dea --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/SubqueryLimitOffsetTest.java @@ -0,0 +1,80 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.hql; + +import java.util.Calendar; +import java.util.List; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.domain.gambit.SimpleEntity; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Andrea Boriero + */ +public class SubqueryLimitOffsetTest extends SessionFactoryBasedFunctionalTest { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + SimpleEntity.class, + }; + } + + @Test + public void testSubqueryLimitOffset() { + inTransaction( + session -> { + List results = session.createQuery( + "select o from SimpleEntity o where o.someString = ( select oSub.someString from SimpleEntity oSub order by oSub.someString limit 1 )" ) + .list(); + assertThat( results.size(), is( 2 ) ); + } ); + } + + @BeforeEach + public void setUp() { + inTransaction( + session -> { + SimpleEntity entity = new SimpleEntity( + 1, + Calendar.getInstance().getTime(), + null, + Integer.MAX_VALUE, + Long.MAX_VALUE, + "some" + ); + session.save( entity ); + + SimpleEntity second_entity = new SimpleEntity( + 2, + Calendar.getInstance().getTime(), + null, + Integer.MIN_VALUE, + Long.MAX_VALUE, + "some" + ); + session.save( second_entity ); + + } ); + } + + @AfterEach + public void tearDown() { + inTransaction( + session -> { + session.createQuery( "from SimpleEntity e" ) + .list() + .forEach( simpleEntity -> session.delete( simpleEntity ) ); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/CrossJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/CrossJoinTest.java new file mode 100644 index 0000000000..03599f83dc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/CrossJoinTest.java @@ -0,0 +1,36 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm.exec; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.domain.gambit.SimpleEntity; +import org.junit.jupiter.api.Test; + +/** + * @author Steve Ebersole + */ +public class CrossJoinTest extends SessionFactoryBasedFunctionalTest { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + SimpleEntity.class, + }; + } + + @Test + public void testSimpleCrossJoin() { + inTransaction( + session -> { + session.createQuery( + "from SimpleEntity e1, SimpleEntity e2 where e1.id = e2.id and e1.someDate = {d '2018-01-01'}" ) + .list(); + + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/DiscriminatorTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/DiscriminatorTests.java new file mode 100644 index 0000000000..5a719991c4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/DiscriminatorTests.java @@ -0,0 +1,81 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm.exec; + +import java.util.List; + +import org.hibernate.boot.MetadataSources; +import org.hibernate.query.spi.QueryImplementor; + +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.hibernate.testing.orm.domain.retail.DomesticVendor; +import org.hibernate.testing.orm.domain.retail.ForeignVendor; +import org.hibernate.testing.orm.domain.retail.Vendor; +import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.hamcrest.Matcher; + +import static org.hamcrest.CoreMatchers.either; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("WeakerAccess") +public class DiscriminatorTests extends BaseSessionFactoryFunctionalTest { + + @Override + protected void applyMetadataSources(MetadataSources metadataSources) { + StandardDomainModel.RETAIL.getDescriptor().applyDomainModel( metadataSources ); + } + + @BeforeEach + public void setUpTestData() { + inTransaction( + session -> { + session.save( new ForeignVendor( 1, "ForeignVendor", "Vendor, Inc." ) ); + session.save( new DomesticVendor( 2, "DomesticVendor", "Vendor, Inc." ) ); + } + ); + } + + @AfterEach + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete Vendor" ).executeUpdate(); + } + ); + } + + @Test + @FailureExpected( reason = "selection of discriminator not yet implemented" ) + public void testSelection() { + inTransaction( + session -> { + final QueryImplementor query = session.createQuery( "select type( v ) from Vendor v" ); + final List list = query.list(); + assertThat( list.size(), is( 2 ) ); + for ( Object o : list ) { + assertThat( + o, + (Matcher) either( is( DomesticVendor.class.getName() ) ) + .or( is( ForeignVendor.class.getName() ) ) + ); + } + + assert list.contains( DomesticVendor.class.getName() ); + assert list.contains( ForeignVendor.class.getName() ); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/FunctionTests.java new file mode 100644 index 0000000000..bd7df3633a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/FunctionTests.java @@ -0,0 +1,87 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm.exec; + +import java.util.Calendar; +import java.util.List; + +import org.hibernate.orm.test.query.sqm.BaseSqmUnitTest; + +import org.hibernate.testing.orm.domain.gambit.SimpleEntity; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Steve Ebersole + */ +public class FunctionTests extends BaseSqmUnitTest { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { SimpleEntity.class }; + } + + @Override + protected boolean exportSchema() { + return true; + } + + @Test + @FailureExpected( reason = "Function support not yet implemented" ) + public void testSubstrInsideConcat() { + inTransaction( + session -> { + List results = session.createQuery( + "select concat('111', concat('222222', '1')) from SimpleEntity s where s.id = :id" ) + .setParameter( "id", 1 ) + .list(); + assertThat( results.size(), is( 1 ) ); + assertThat( results.get( 0 ), is( "1112222221" ) ); + } ); + } + + @BeforeEach + public void setUp() { + inTransaction( + session -> { + SimpleEntity entity = new SimpleEntity( + 1, + Calendar.getInstance().getTime(), + null, + Integer.MAX_VALUE, + Long.MAX_VALUE, + null + ); + session.save( entity ); + + SimpleEntity second_entity = new SimpleEntity( + 2, + Calendar.getInstance().getTime(), + null, + Integer.MIN_VALUE, + Long.MAX_VALUE, + null + ); + session.save( second_entity ); + + } ); + } + + @AfterEach + public void tearDown() { + inTransaction( + session -> { + session.createQuery( "delete SimpleEntity" ).executeUpdate(); + } + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/LiteralTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/LiteralTests.java new file mode 100644 index 0000000000..ddc88f5ba3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/LiteralTests.java @@ -0,0 +1,52 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm.exec; + +import org.hibernate.boot.MetadataSources; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.junit.jupiter.api.Test; + +/** + * @author Steve Ebersole + */ +public class LiteralTests extends SessionFactoryBasedFunctionalTest { + + @Override + protected void applyMetadataSources(MetadataSources metadataSources) { + StandardDomainModel.GAMBIT.getDescriptor().applyDomainModel( metadataSources ); + } + + @Test + public void testTimestampLiteral() { + inTransaction( + session -> { + session.createQuery( "from EntityOfBasics e1 where e1.theTimestamp = {ts '2018-01-01T12:30:00'}" ) + .list(); + } + ); + } + + @Test + public void testDateLiteral() { + inTransaction( + session -> { + session.createQuery( "from EntityOfBasics e1 where e1.theDate = {d '2018-01-01'}" ).list(); + } + ); + } + + @Test + public void testTimeLiteral() { + inTransaction( + session -> { + session.createQuery( "from EntityOfBasics e1 where e1.theTime = {t '12:30:00'}" ).list(); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/OrderingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/OrderingTests.java new file mode 100644 index 0000000000..21d5d824f7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/OrderingTests.java @@ -0,0 +1,71 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm.exec; + +import org.hibernate.boot.MetadataSources; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.junit.jupiter.api.Test; + +/** + * Tests for order-by clauses + * @author Steve Ebersole + */ +public class OrderingTests extends SessionFactoryBasedFunctionalTest { + + @Override + protected void applyMetadataSources(MetadataSources metadataSources) { + StandardDomainModel.RETAIL.getDescriptor().applyDomainModel( metadataSources ); + } + + @Test + public void testBasicOrdering() { + inTransaction( + session -> { + session.createQuery( "from SalesAssociate p order by p.name.familiarName" ) + .list(); + } + ); + } + + @Test + @TestForIssue( jiraKey = "HHH-1356" ) + @FailureExpected( reason = "functions not yet implemented" ) + public void testFunctionBasedOrdering() { + inTransaction( + session -> { + session.createQuery( "from SalesAssociate p order by upper( p.name.familiarName )" ) + .list(); + } + ); + } + + @Test + @TestForIssue( jiraKey = "HHH-11688" ) + public void testSelectAliasOrdering() { + inTransaction( + session -> { + session.createQuery( "select v.name as n from Vendor v order by n" ) + .list(); + } + ); + } + + @Test + @TestForIssue( jiraKey = "HHH-11688" ) + public void testSelectPositionOrdering() { + inTransaction( + session -> { + session.createQuery( "select v.name as n from Vendor v order by 1" ) + .list(); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/ParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/ParameterTest.java new file mode 100644 index 0000000000..7a5b37145f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/ParameterTest.java @@ -0,0 +1,46 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm.exec; + +import org.hibernate.boot.MetadataSources; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.domain.StandardDomainModel; +import org.junit.jupiter.api.Test; + +/** + * @author Steve Ebersole + */ +public class ParameterTest extends SessionFactoryBasedFunctionalTest { + + @Override + protected void applyMetadataSources(MetadataSources metadataSources) { + StandardDomainModel.RETAIL.getDescriptor().applyDomainModel( metadataSources ); + } + + @Test + public void testReusedNamedParam() { + inTransaction( + session -> { + session.createQuery( "from SalesAssociate p where p.name.familiarName = :name or p.name.familyName = :name" ) + .setParameter( "name", "a name" ) + .list(); + } + ); + } + + @Test + public void testReusedOrdinalParam() { + inTransaction( + session -> { + session.createQuery( "from SalesAssociate p where p.name.familiarName = ?1 or p.name.familyName = ?1" ) + .setParameter( 1, "a name" ) + .list(); + } + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/SubQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/SubQueryTest.java new file mode 100644 index 0000000000..814092d2d1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/sqm/exec/SubQueryTest.java @@ -0,0 +1,83 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.sqm.exec; + +import java.util.List; + +import org.hibernate.testing.junit5.SessionFactoryBasedFunctionalTest; +import org.hibernate.testing.orm.domain.gambit.BasicEntity; +import org.hibernate.testing.orm.junit.FailureExpected; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +/** + * @author Chris Cranford + */ +public class SubQueryTest extends SessionFactoryBasedFunctionalTest { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + BasicEntity.class + }; + } + + @BeforeAll + public void prepareData() { + inTransaction( + session -> { + BasicEntity entity1 = new BasicEntity( 1, "e1" ); + BasicEntity entity2 = new BasicEntity( 2, "e2" ); + BasicEntity entity3 = new BasicEntity( 3, "e1" ); + session.save( entity1 ); + session.save( entity2 ); + session.save( entity3 ); + } + ); + } + + @Test + @FailureExpected( reason = "function support not yet implemented" ) + public void testSubQueryWithMaxFunction() { + inTransaction( + session -> { + final String hql = "SELECT e FROM BasicEntity e WHERE e.id = " + + "(SELECT max(e1.id) FROM BasicEntity e1 WHERE e1.data = :data)"; + + List results = session + .createQuery( hql, BasicEntity.class ) + .setParameter( "data", "e1" ) + .getResultList(); + assertThat( results.size(), is( 1 ) ); + assertThat( results.get( 0 ).getId(), is( 3 ) ); + assertThat( results.get( 0 ).getData(), is( "e1" ) ); + } + ); + } + + @Test + @FailureExpected( reason = "function support not yet implemented" ) + public void testSubQueryWithMinFunction() { + inTransaction( + session -> { + final String hql = "SELECT e FROM BasicEntity e WHERE e.id = " + + "(SELECT min(e1.id) FROM BasicEntity e1 WHERE e1.data = :data)"; + + List results = session + .createQuery( hql, BasicEntity.class ) + .setParameter( "data", "e1" ) + .getResultList(); + assertThat( results.size(), is( 1 ) ); + assertThat( results.get( 0 ).getId(), is( 1 ) ); + assertThat( results.get( 0 ).getData(), is( "e1" ) ); + } + ); + } +} diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryScope.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryScope.java index 8c09acdffa..c893fe3c11 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryScope.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/ServiceRegistryScope.java @@ -7,6 +7,7 @@ package org.hibernate.testing.orm.junit; import java.util.function.Consumer; +import java.util.function.Function; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.service.Service; @@ -28,4 +29,16 @@ public interface ServiceRegistryScope { action.accept( service ); } + + default R fromService(Class role, Function action) { + assert role != null; + + final S service = getRegistry().getService( role ); + + if ( service == null ) { + throw new IllegalArgumentException( "Could not locate requested service - " + role.getName() ); + } + + return action.apply( service ); + } }