diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index a12ff33b07..59d429d62b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -58,9 +58,10 @@ import org.hibernate.loader.BatchFetchStyle; import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode; import org.hibernate.query.criteria.LiteralHandlingMode; -import org.hibernate.query.hql.SemanticQueryProducer; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverterFactory; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; @@ -210,9 +211,10 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { private CurrentTenantIdentifierResolver currentTenantIdentifierResolver; // Queries - private SemanticQueryProducer semanticQueryProducer; + private HqlTranslator hqlTranslator; private SqmMultiTableMutationStrategy sqmMultiTableMutationStrategy; private SqmFunctionRegistry sqmFunctionRegistry; + private SqmToSqlAstConverterFactory sqmTranslatorFactory; private Boolean useOfJdbcNamedParametersEnabled; private Map querySubstitutions; private boolean namedQueryStartupCheckingEnabled; @@ -356,7 +358,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { this.jtaTrackByThread = cfgService.getSetting( JTA_TRACK_BY_THREAD, BOOLEAN, true ); - final String semanticQueryProducerImplName = ConfigurationHelper.extractValue( + final String hqlTranslatorImplFqn = ConfigurationHelper.extractValue( AvailableSettings.SEMANTIC_QUERY_PRODUCER, configurationSettings, () -> ConfigurationHelper.extractPropertyValue( @@ -364,12 +366,23 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { configurationSettings ) ); - this.semanticQueryProducer = resolveSemanticQueryProducer( - semanticQueryProducerImplName, + this.hqlTranslator = resolveHqlTranslator( + hqlTranslatorImplFqn, serviceRegistry, strategySelector ); + final String sqmTranslatorFactoryImplFqn = ConfigurationHelper.extractPropertyValue( + AvailableSettings.SEMANTIC_QUERY_TRANSLATOR, + configurationSettings + ); + this.sqmTranslatorFactory = resolveSqmTranslator( + sqmTranslatorFactoryImplFqn, + serviceRegistry, + strategySelector + ); + + final String sqmMutationStrategyImplName = ConfigurationHelper.extractValue( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, configurationSettings, @@ -587,7 +600,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { ); } - private SemanticQueryProducer resolveSemanticQueryProducer( + private HqlTranslator resolveHqlTranslator( String producerName, StandardServiceRegistry serviceRegistry, StrategySelector strategySelector) { @@ -597,18 +610,32 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { //noinspection Convert2Lambda return strategySelector.resolveDefaultableStrategy( - SemanticQueryProducer.class, + HqlTranslator.class, producerName, - new Callable() { + new Callable() { @Override - public SemanticQueryProducer call() throws Exception { + public HqlTranslator call() throws Exception { final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); - return (SemanticQueryProducer) classLoaderService.classForName( producerName ).newInstance(); + return (HqlTranslator) classLoaderService.classForName( producerName ).newInstance(); } } ); } + private SqmToSqlAstConverterFactory resolveSqmTranslator( + String translatorImplFqn, + StandardServiceRegistry serviceRegistry, + StrategySelector strategySelector) { + if ( StringHelper.isEmpty( translatorImplFqn ) ) { + return null; + } + + return strategySelector.resolveStrategy( + SqmToSqlAstConverterFactory.class, + translatorImplFqn + ); + } + @SuppressWarnings("deprecation") private static Interceptor determineInterceptor(Map configurationSettings, StrategySelector strategySelector) { Object setting = configurationSettings.get( INTERCEPTOR ); @@ -831,8 +858,13 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { } @Override - public SemanticQueryProducer getHqlTranslator() { - return semanticQueryProducer; + public HqlTranslator getHqlTranslator() { + return hqlTranslator; + } + + @Override + public SqmToSqlAstConverterFactory getSqmTranslatorFactory() { + return sqmTranslatorFactory; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java index 16507bb3ff..65f9c3edc7 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java @@ -30,9 +30,10 @@ import org.hibernate.loader.BatchFetchStyle; import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode; import org.hibernate.query.criteria.LiteralHandlingMode; -import org.hibernate.query.hql.SemanticQueryProducer; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverterFactory; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.tuple.entity.EntityTuplizerFactory; @@ -40,6 +41,9 @@ import org.hibernate.tuple.entity.EntityTuplizerFactory; /** * Convenience base class for custom implementors of SessionFactoryOptions, using delegation * + * @implNote non-abstract to ensure that all SessionFactoryOptions methods have at least + * a default implementation + * * @author Steve Ebersole */ @SuppressWarnings("unused") @@ -391,10 +395,15 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp } @Override - public SemanticQueryProducer getHqlTranslator() { + public HqlTranslator getHqlTranslator() { return delegate.getHqlTranslator(); } + @Override + public SqmToSqlAstConverterFactory getSqmTranslatorFactory() { + return delegate.getSqmTranslatorFactory(); + } + @Override public TimeZone getJdbcTimeZone() { return delegate.getJdbcTimeZone(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java index 7f20aae938..cac0e8e8d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java @@ -33,9 +33,10 @@ import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode; import org.hibernate.query.QueryLiteralRendering; import org.hibernate.query.criteria.LiteralHandlingMode; -import org.hibernate.query.hql.SemanticQueryProducer; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverterFactory; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.hibernate.resource.jdbc.spi.StatementInspector; import org.hibernate.stat.Statistics; @@ -143,7 +144,9 @@ public interface SessionFactoryOptions { }; } - SemanticQueryProducer getHqlTranslator(); + HqlTranslator getHqlTranslator(); + + SqmToSqlAstConverterFactory getSqmTranslatorFactory(); SqmMultiTableMutationStrategy getSqmMultiTableMutationStrategy(); @@ -351,5 +354,4 @@ public interface SessionFactoryOptions { boolean isUseOfJdbcNamedParametersEnabled(); boolean isOmitJoinOfSuperclassTablesEnabled(); - } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 7e27c7842d..937841535d 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -17,9 +17,11 @@ import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cache.spi.TimestampsCacheFactory; import org.hibernate.jpa.spi.JpaCompliance; import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.internal.ParameterMetadataImpl; import org.hibernate.query.spi.QueryInterpretationCache; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverterFactory; import org.hibernate.resource.beans.container.spi.ExtendedBeanManager; import org.hibernate.resource.transaction.spi.TransactionCoordinator; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; @@ -924,10 +926,15 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings { String QUERY_TRANSLATOR = "hibernate.query.factory_class"; /** - * Names the {@link org.hibernate.query.hql.SemanticQueryProducer} class to use. + * Names the {@link HqlTranslator} class to use. */ String SEMANTIC_QUERY_PRODUCER = "hibernate.query.hql.translator"; + /** + * Names the {@link SqmToSqlAstConverterFactory} class to use. + */ + String SEMANTIC_QUERY_TRANSLATOR = "hibernate.query.sqm.translator"; + /** * @deprecated Use {@link #QUERY_MULTI_TABLE_MUTATION_STRATEGY} instead */ 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 acabb6afd6..977857c3f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -40,6 +40,7 @@ import org.hibernate.ScrollMode; import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; import org.hibernate.boot.model.relational.Sequence; +import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.CastFunction; @@ -95,9 +96,11 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Lockable; import org.hibernate.procedure.internal.StandardCallableStatementSupport; import org.hibernate.procedure.spi.CallableStatementSupport; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverterFactory; import org.hibernate.service.ServiceRegistry; import org.hibernate.sql.ANSICaseFragment; import org.hibernate.sql.ANSIJoinFragment; @@ -3072,4 +3075,31 @@ public abstract class Dialect implements ConversionContext { protected String prependComment(String sql, String comment) { return "/* " + comment + " */ " + sql; } + + /** + * Return an HqlTranslator specific for the Dialect. Return {@code null} + * to use Hibernate's standard translator. + * + * Note that {@link SessionFactoryOptions#getHqlTranslator()} has higher precedence + * + * @see org.hibernate.query.hql.internal.StandardHqlTranslator + * @see QueryEngine#getHqlTranslator() + */ + public HqlTranslator getHqlTranslator() { + return null; + } + + /** + * Return an SqmToSqlAstConverterFactory specific for the Dialect. Return {@code null} + * to use Hibernate's standard translator. + * + * Note that {@link SessionFactoryOptions#getSqmTranslatorFactory()} has higher + * precedence as it comes directly from the user config + * + * @see org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter + * @see QueryEngine#getSqmTranslator() + */ + public SqmToSqlAstConverterFactory getSqmTranslatorFactory() { + return null; + } } 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 4419f900d3..24d1a6b15d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -615,7 +615,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont final QueryInterpretationCache interpretationCache = queryEngine.getInterpretationCache(); final SqmStatement sqm = interpretationCache.resolveSqmStatement( queryString, - s -> queryEngine.getSemanticQueryProducer().interpret( queryString ) + s -> queryEngine.getHqlTranslator().interpret( queryString ) ); final QuerySqmImpl query = new QuerySqmImpl( diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/SemanticQueryProducer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/HqlTranslator.java similarity index 77% rename from hibernate-core/src/main/java/org/hibernate/query/hql/SemanticQueryProducer.java rename to hibernate-core/src/main/java/org/hibernate/query/hql/HqlTranslator.java index 6ad252db6a..2c2081ebab 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/SemanticQueryProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/HqlTranslator.java @@ -15,21 +15,24 @@ import org.hibernate.query.sqm.tree.SqmStatement; * Main entry point into building semantic queries. * * @see SessionFactoryImplementor#getQueryEngine() - * @see QueryEngine#getSemanticQueryProducer() + * @see QueryEngine#getHqlTranslator() * * @author Steve Ebersole */ @Incubating -public interface SemanticQueryProducer { +public interface HqlTranslator { /** * Performs the interpretation of a HQL/JPQL query string to SQM. * - * @param query The HQL/JPQL query string to interpret + * @param hql The HQL/JPQL query string to interpret * * @return The semantic representation of the incoming query. */ - SqmStatement interpret(String query); + SqmStatement interpret(String hql); + /** + * Give the translator a chance to "shut down" if it needs to + */ default void close() { // nothing to do generally speaking } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java index 1540f29c19..9fdca65945 100755 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/NamedHqlQueryMementoImpl.java @@ -124,7 +124,7 @@ public class NamedHqlQueryMementoImpl extends AbstractNamedQueryMemento implemen @Override public void validate(QueryEngine queryEngine) { - queryEngine.getSemanticQueryProducer().interpret( getHqlString() ); + queryEngine.getHqlTranslator().interpret( getHqlString() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryProducerImpl.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java similarity index 91% rename from hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryProducerImpl.java rename to hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java index 08c92207fd..70d81e5b33 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryProducerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java @@ -8,7 +8,7 @@ package org.hibernate.query.hql.internal; import org.hibernate.QueryException; import org.hibernate.query.sqm.InterpretationException; -import org.hibernate.query.hql.SemanticQueryProducer; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.sqm.internal.SqmTreePrinter; import org.hibernate.query.sqm.spi.SqmCreationContext; import org.hibernate.query.hql.spi.SqmCreationOptions; @@ -19,11 +19,11 @@ import org.hibernate.query.sqm.tree.SqmStatement; * * @author Steve Ebersole */ -public class SemanticQueryProducerImpl implements SemanticQueryProducer { +public class StandardHqlTranslator implements HqlTranslator { private final SqmCreationContext sqmCreationContext; private final SqmCreationOptions sqmCreationOptions; - public SemanticQueryProducerImpl( + public StandardHqlTranslator( SqmCreationContext sqmCreationContext, SqmCreationOptions sqmCreationOptions) { this.sqmCreationContext = sqmCreationContext; diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/package-info.java b/hibernate-core/src/main/java/org/hibernate/query/hql/package-info.java index 8712538ef4..bbf46ed395 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/package-info.java @@ -12,7 +12,7 @@ * This includes transformation via Antlr, {@link org.hibernate.query.Query} * manipulation and execution * - * @see org.hibernate.query.hql.SemanticQueryProducer + * @see org.hibernate.query.hql.HqlTranslator * @see org.hibernate.query.hql.spi.HqlQueryImplementor * @see org.hibernate.query.hql.spi.NamedHqlQueryMemento */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/NamedQueryRepositoryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/NamedQueryRepositoryImpl.java index 9a8c406b73..489327fc16 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/NamedQueryRepositoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/NamedQueryRepositoryImpl.java @@ -12,7 +12,7 @@ import java.util.function.Consumer; import org.hibernate.HibernateException; import org.hibernate.procedure.spi.NamedCallableQueryMemento; -import org.hibernate.query.hql.SemanticQueryProducer; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.hql.spi.NamedHqlQueryMemento; import org.hibernate.query.named.NamedQueryRepository; import org.hibernate.query.named.NamedResultSetMappingMemento; @@ -127,7 +127,7 @@ public class NamedQueryRepositoryImpl implements NamedQueryRepository { public Map checkNamedQueries(QueryEngine queryEngine) { Map errors = new HashMap<>(); - final SemanticQueryProducer sqmProducer = queryEngine.getSemanticQueryProducer(); + final HqlTranslator sqmProducer = queryEngine.getHqlTranslator(); final QueryInterpretationCache interpretationCache = queryEngine.getInterpretationCache(); final boolean cachingEnabled = interpretationCache.isEnabled(); 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 72f976510e..5447bcf16b 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 @@ -16,21 +16,27 @@ 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.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.util.config.ConfigurationHelper; -import org.hibernate.metamodel.spi.MetamodelImplementor; +import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.query.QueryLogger; +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.QueryInterpretationCacheDisabledImpl; import org.hibernate.query.internal.QueryInterpretationCacheStandardImpl; import org.hibernate.query.named.NamedQueryRepository; -import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder; -import org.hibernate.query.hql.SemanticQueryProducer; -import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry; -import org.hibernate.query.hql.internal.SemanticQueryProducerImpl ; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverterFactory; +import org.hibernate.query.sqm.internal.DomainParameterXref; 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.hql.spi.SqmCreationOptions; +import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter; import org.hibernate.service.ServiceRegistry; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; /** * Aggregation and encapsulation of the components Hibernate uses @@ -44,7 +50,7 @@ public class QueryEngine { SessionFactoryImplementor sessionFactory, MetadataImplementor metadata) { return new QueryEngine( - sessionFactory.getMetamodel(), + sessionFactory.getJpaMetamodel(), sessionFactory.getServiceRegistry(), sessionFactory.getSessionFactoryOptions(), sessionFactory, @@ -56,12 +62,13 @@ public class QueryEngine { private final NamedQueryRepository namedQueryRepository; private final SqmCriteriaNodeBuilder criteriaBuilder; - private final SemanticQueryProducer semanticQueryProducer; + private final HqlTranslator hqlTranslator; + private final SqmToSqlAstConverterFactory sqmToSqlAstConverterFactory; private final QueryInterpretationCache interpretationCache; private final SqmFunctionRegistry sqmFunctionRegistry; public QueryEngine( - MetamodelImplementor domainModel, + JpaMetamodel domainModel, ServiceRegistry serviceRegistry, SessionFactoryOptions runtimeOptions, SqmCreationContext sqmCreationContext, @@ -74,17 +81,27 @@ public class QueryEngine { this.namedQueryRepository = namedQueryRepository; - // todo (6.0) : allow SemanticQueryProducer to be pluggable (see legacy `QueryTranslatorFactoryInitiator`) - // - also, allow for Dialect to specify producer to use (or defer to standard producer) - this.semanticQueryProducer = new SemanticQueryProducerImpl( sqmCreationContext, sqmCreationOptions ); + this.hqlTranslator = resolveHqlTranslator( + runtimeOptions, + dialect, + sqmCreationContext, + sqmCreationOptions + ); + + this.sqmToSqlAstConverterFactory = resolveSqmToSqlAstConverterFactory( + runtimeOptions, + dialect, + sqmCreationContext, + sqmCreationOptions + ); this.criteriaBuilder = new SqmCriteriaNodeBuilder( this, - domainModel.getJpaMetamodel(), + domainModel, serviceRegistry ); - this.interpretationCache = buildQueryPlanCache( properties ); + this.interpretationCache = buildInterpretationCache( properties ); this.sqmFunctionRegistry = new SqmFunctionRegistry(); dialect.initializeFunctionRegistry( this ); @@ -93,7 +110,56 @@ public class QueryEngine { } } - private static QueryInterpretationCache buildQueryPlanCache(Map properties) { + private static HqlTranslator resolveHqlTranslator( + SessionFactoryOptions runtimeOptions, + Dialect dialect, + SqmCreationContext sqmCreationContext, + SqmCreationOptions sqmCreationOptions) { + if ( runtimeOptions.getHqlTranslator() != null ) { + return runtimeOptions.getHqlTranslator(); + } + + if ( dialect.getHqlTranslator() != null ) { + return dialect.getHqlTranslator(); + } + + return new StandardHqlTranslator( sqmCreationContext, sqmCreationOptions ); + } + + private SqmToSqlAstConverterFactory resolveSqmToSqlAstConverterFactory( + SessionFactoryOptions runtimeOptions, + Dialect dialect, + SqmCreationContext sqmCreationContext, + SqmCreationOptions sqmCreationOptions) { + if ( runtimeOptions.getSqmTranslatorFactory() != null ) { + return runtimeOptions.getSqmTranslatorFactory(); + } + + if ( dialect.getSqmTranslatorFactory() != null ) { + return dialect.getSqmTranslatorFactory(); + } + + //noinspection Convert2Lambda + return new SqmToSqlAstConverterFactory() { + @Override + public SqmSelectToSqlAstConverter createSelectConverter( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings, + LoadQueryInfluencers influencers, + SqlAstCreationContext creationContext) { + return new StandardSqmSelectToSqlAstConverter( + queryOptions, + domainParameterXref, + domainParameterBindings, + influencers, + creationContext + ); + } + }; + } + + private static QueryInterpretationCache buildInterpretationCache(Map properties) { final boolean explicitUseCache = ConfigurationHelper.getBoolean( AvailableSettings.QUERY_PLAN_CACHE_ENABLED, properties, @@ -144,8 +210,12 @@ public class QueryEngine { return criteriaBuilder; } - public SemanticQueryProducer getSemanticQueryProducer() { - return semanticQueryProducer; + public HqlTranslator getHqlTranslator() { + return hqlTranslator; + } + + public SqmToSqlAstConverterFactory getSqmTranslatorFactory() { + return sqmToSqlAstConverterFactory; } public QueryInterpretationCache getInterpretationCache() { @@ -165,8 +235,8 @@ public class QueryEngine { criteriaBuilder.close(); } - if ( semanticQueryProducer != null ) { - semanticQueryProducer.close(); + if ( hqlTranslator != null ) { + hqlTranslator.close(); } if ( interpretationCache != null ) { 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 1c823bab21..c2a11008c9 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 @@ -14,14 +14,17 @@ import javax.persistence.TupleElement; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.ScrollMode; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.streams.StingArrayCollector; import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.SelectQueryPlan; +import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.SqmToSqlAstConverterFactory; import org.hibernate.query.sqm.sql.internal.SqmSelectInterpretation; -import org.hibernate.query.sqm.sql.internal.SqmSelectToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; @@ -147,20 +150,30 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { @Override public List performList(ExecutionContext executionContext) { + final SharedSessionContractImplementor session = executionContext.getSession(); + if ( jdbcSelect == null ) { // todo (6.0) : for cases where we have no "load query influencers" we could use a cached SQL AST // - this is similar to the plan for loaders - final SqmSelectToSqlAstConverter sqmConverter = new SqmSelectToSqlAstConverter( + + final SessionFactoryImplementor sessionFactory = session.getFactory(); + + final SqmToSqlAstConverterFactory sqmTranslatorFactory = sessionFactory.getQueryEngine().getSqmTranslatorFactory(); + + final SqmSelectToSqlAstConverter sqmConverter = sqmTranslatorFactory.createSelectConverter( executionContext.getQueryOptions(), domainParameterXref, executionContext.getDomainParameterBindingContext().getQueryParameterBindings(), executionContext.getLoadQueryInfluencers(), - executionContext.getSession().getFactory() + sessionFactory ); + final SqmSelectInterpretation interpretation = sqmConverter.interpret( sqm ); + + // todo (6.0) : allow Dialect to specify SQL -> JdbcCall converter to use jdbcSelect = SqlAstSelectToJdbcSelectConverter.interpret( interpretation.getSqlAst(), - executionContext.getSession().getFactory() + sessionFactory ); this.jdbcParamsXref = SqmUtil.generateJdbcParamsXref( @@ -174,11 +187,11 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { executionContext.getDomainParameterBindingContext().getQueryParameterBindings(), domainParameterXref, jdbcParamsXref, - executionContext.getSession() + session ); try { - return executionContext.getSession().getFactory().getJdbcServices().getJdbcSelectExecutor().list( + return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( jdbcSelect, jdbcParameterBindings, executionContext, 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 99042206c2..febce20910 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 @@ -92,7 +92,7 @@ public class QuerySqmImpl final SessionFactoryImplementor factory = producer.getFactory(); - this.sqmStatement = factory.getQueryEngine().getSemanticQueryProducer().interpret( hqlString ); + this.sqmStatement = factory.getQueryEngine().getHqlTranslator().interpret( hqlString ); if ( resultType != null ) { if ( sqmStatement instanceof SqmDmlStatement ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/package-info.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/package-info.java index 0a6b1a66a8..b8325ec23c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/package-info.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/package-info.java @@ -12,7 +12,7 @@ * * This package defines support for producing SQM trees (see {@link org.hibernate.query.sqm.tree}). * The main entry point into producing an SQM tree is - * {@link org.hibernate.query.hql.SemanticQueryProducer}, which + * {@link org.hibernate.query.hql.HqlTranslator}, which * can be obtained via * {@link org.hibernate.query.spi.QueryEngine} which in turn is obtained via * {@link org.hibernate.engine.spi.SessionFactoryImplementor#getQueryEngine()}. @@ -21,7 +21,7 @@ * == From HQL/JPQL * * `SemanticQueryProducer` defines just a single method for producing SQM based on HQL: - * {@link org.hibernate.query.hql.SemanticQueryProducer#interpret}. + * {@link org.hibernate.query.hql.HqlTranslator#interpret}. * See {@link org.hibernate.query.hql.internal} for details * * diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectToSqlAstConverter.java new file mode 100644 index 0000000000..34722e0900 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmSelectToSqlAstConverter.java @@ -0,0 +1,18 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sqm.sql; + +import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess; +import org.hibernate.query.sqm.sql.internal.SqmSelectInterpretation; +import org.hibernate.query.sqm.tree.select.SqmSelectStatement; + +/** + * @author Steve Ebersole + */ +public interface SqmSelectToSqlAstConverter extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess { + SqmSelectInterpretation interpret(SqmSelectStatement statement); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmToSqlAstConverterFactory.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmToSqlAstConverterFactory.java new file mode 100644 index 0000000000..2dcfa29ffc --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/SqmToSqlAstConverterFactory.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.query.sqm.sql; + +import org.hibernate.engine.spi.LoadQueryInfluencers; +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.SqmSelectToSqlAstConverter; +import org.hibernate.sql.ast.spi.SqlAstCreationContext; + +/** + * @author Steve Ebersole + */ +public interface SqmToSqlAstConverterFactory { + SqmSelectToSqlAstConverter createSelectConverter( + QueryOptions queryOptions, + DomainParameterXref domainParameterXref, + QueryParameterBindings domainParameterBindings, + LoadQueryInfluencers influencers, + SqlAstCreationContext creationContext); + + // todo (6.0) : update, delete, etc converters... +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmSelectInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmSelectInterpretation.java index a21d57d340..2ea0deb927 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmSelectInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmSelectInterpretation.java @@ -17,7 +17,7 @@ import org.hibernate.sql.exec.spi.JdbcParameter; /** * Details of the result of interpreting an SQM SELECT AST into a SQL SELECT AST * - * @see SqmSelectToSqlAstConverter#interpret(org.hibernate.query.sqm.tree.select.SqmSelectStatement) + * @see StandardSqmSelectToSqlAstConverter#interpret(org.hibernate.query.sqm.tree.select.SqmSelectStatement) * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmSelectToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java similarity index 98% rename from hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmSelectToSqlAstConverter.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java index de539507b9..0d1fb76827 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqmSelectToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/StandardSqmSelectToSqlAstConverter.java @@ -64,14 +64,14 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; * @author John O'Hara */ @SuppressWarnings("unchecked") -public class SqmSelectToSqlAstConverter +public class StandardSqmSelectToSqlAstConverter extends BaseSqmToSqlAstConverter - implements DomainResultCreationState { + implements DomainResultCreationState, org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter { private final CircularFetchDetector circularFetchDetector = new CircularFetchDetector(); private final List domainResults = new ArrayList<>(); - public SqmSelectToSqlAstConverter( + public StandardSqmSelectToSqlAstConverter( QueryOptions queryOptions, DomainParameterXref domainParameterXref, QueryParameterBindings domainParameterBindings, @@ -82,6 +82,7 @@ public class SqmSelectToSqlAstConverter super( creationContext, queryOptions, domainParameterXref, domainParameterBindings, influencers ); } + @Override public SqmSelectInterpretation interpret(SqmSelectStatement statement) { return new SqmSelectInterpretation( visitSelectStatement( statement ), @@ -292,7 +293,7 @@ public class SqmSelectToSqlAstConverter joined, lockMode, alias, - SqmSelectToSqlAstConverter.this + StandardSqmSelectToSqlAstConverter.this ); } catch (RuntimeException e) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaStandardSqmInferenceTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaStandardSqmInferenceTests.java index 9cca4f4012..8ed649a499 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaStandardSqmInferenceTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/JpaStandardSqmInferenceTests.java @@ -114,7 +114,7 @@ public class JpaStandardSqmInferenceTests extends SessionFactoryBasedFunctionalT } private void checkParameters(String query, Class... expecteds) { - final SqmStatement sqmStatement = sessionFactory().getQueryEngine().getSemanticQueryProducer().interpret( query ); + final SqmStatement sqmStatement = sessionFactory().getQueryEngine().getHqlTranslator().interpret( query ); checkParameterTypes( sqmStatement, expecteds ); } 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 3488fd55aa..760332ac00 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 @@ -56,7 +56,7 @@ public abstract class BaseSqmUnitTest } public static SqmSelectStatement interpretSelect(String hql, SessionFactoryImplementor sessionFactory) { - return (SqmSelectStatement) sessionFactory.getQueryEngine().getSemanticQueryProducer().interpret( hql ); + return (SqmSelectStatement) sessionFactory.getQueryEngine().getHqlTranslator().interpret( hql ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java index bf2e2aef80..3faf6e31ad 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java @@ -12,7 +12,6 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; -import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Component; import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender; import org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity; import org.hibernate.query.NavigablePath; @@ -20,7 +19,7 @@ import org.hibernate.query.hql.spi.HqlQueryImplementor; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sqm.internal.QuerySqmImpl; import org.hibernate.query.sqm.sql.internal.SqmSelectInterpretation; -import org.hibernate.query.sqm.sql.internal.SqmSelectToSqlAstConverter; +import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.sql.ast.spi.SqlAstSelectToJdbcSelectConverter; import org.hibernate.sql.ast.spi.SqlSelection; @@ -78,7 +77,7 @@ public class SmokeTests { //noinspection unchecked final SqmSelectStatement sqmStatement = (SqmSelectStatement) hqlQuery.getSqmStatement(); - final SqmSelectToSqlAstConverter sqmConverter = new SqmSelectToSqlAstConverter( + final StandardSqmSelectToSqlAstConverter sqmConverter = new StandardSqmSelectToSqlAstConverter( hqlQuery.getQueryOptions(), ( (QuerySqmImpl) hqlQuery ).getDomainParameterXref(), query.getParameterBindings(), @@ -134,7 +133,7 @@ public class SmokeTests { //noinspection unchecked final SqmSelectStatement sqmStatement = (SqmSelectStatement) hqlQuery.getSqmStatement(); - final SqmSelectToSqlAstConverter sqmConverter = new SqmSelectToSqlAstConverter( + final StandardSqmSelectToSqlAstConverter sqmConverter = new StandardSqmSelectToSqlAstConverter( hqlQuery.getQueryOptions(), ( (QuerySqmImpl) hqlQuery ).getDomainParameterXref(), query.getParameterBindings(), diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java index a276dae88e..6d1eab8bfd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/ASTParserLoadingOrderByTest.java @@ -17,7 +17,7 @@ import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; -import org.hibernate.query.hql.SemanticQueryProducer; +import org.hibernate.query.hql.HqlTranslator; import org.hibernate.testing.FailureExpected; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -55,7 +55,7 @@ public class ASTParserLoadingOrderByTest extends BaseCoreFunctionalTestCase { super.configure( cfg ); cfg.setProperty( Environment.USE_QUERY_CACHE, "false" ); cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); - cfg.setProperty( Environment.SEMANTIC_QUERY_PRODUCER, SemanticQueryProducer.class.getName() ); + cfg.setProperty( Environment.SEMANTIC_QUERY_PRODUCER, HqlTranslator.class.getName() ); } private void createData() {