From 9adffa4e4f28cfd4ca68c9bc7fa5aa79e87f7d7f Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 13 Jun 2023 21:29:06 +0200 Subject: [PATCH] further work on query parser exceptions I missed a couple of things --- .../java/org/hibernate/QueryException.java | 18 ++++++- .../domain/internal/BasicSqmPathSource.java | 2 +- .../domain/internal/JpaMetamodelImpl.java | 9 ++-- .../query/EntityReferenceException.java | 25 ---------- .../query/NamedQueryValidationException.java | 5 +- .../org/hibernate/query/PathException.java | 13 +++-- .../query/QueryTypeMismatchException.java | 2 +- .../query/UnknownNamedQueryException.java | 6 ++- .../hql/internal/SemanticQueryBuilder.java | 2 +- .../hql/internal/StandardHqlTranslator.java | 16 ++++-- .../internal/NamedObjectRepositoryImpl.java | 26 ++++++---- .../query/sqm/EntityTypeException.java | 35 +++++++++++++ .../query/{ => sqm}/PathElementException.java | 6 +-- .../hibernate/query/sqm/SqmPathSource.java | 1 - .../{ => sqm}/TerminalPathException.java | 6 +-- .../query/sqm/UnknownEntityException.java | 19 +++++-- .../query/sqm/UnknownPathException.java | 32 ++++++++---- .../BasicValuedPathInterpretation.java | 3 +- .../tree/domain/SqmBasicValuedSimplePath.java | 3 +- .../query/sqm/tree/domain/SqmPath.java | 4 +- .../domain/SqmPluralValuedSimplePath.java | 2 +- .../sqm/tree/expression/SqmEnumLiteral.java | 6 +-- .../sqm/tree/expression/SqmFieldLiteral.java | 7 ++- .../hql/ScrollableCollectionFetchingTest.java | 50 +++++++++++++++---- 24 files changed, 198 insertions(+), 100 deletions(-) delete mode 100644 hibernate-core/src/main/java/org/hibernate/query/EntityReferenceException.java create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/EntityTypeException.java rename hibernate-core/src/main/java/org/hibernate/query/{ => sqm}/PathElementException.java (82%) rename hibernate-core/src/main/java/org/hibernate/query/{ => sqm}/TerminalPathException.java (82%) diff --git a/hibernate-core/src/main/java/org/hibernate/QueryException.java b/hibernate-core/src/main/java/org/hibernate/QueryException.java index 5e4afc5b38..25d73b1f4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/QueryException.java +++ b/hibernate-core/src/main/java/org/hibernate/QueryException.java @@ -7,8 +7,22 @@ package org.hibernate; /** - * A problem occurred translating a Hibernate query to SQL - * due to invalid query syntax, or some similar problem. + * A problem occurred translating a Hibernate query to SQL due to illegal query + * syntax, an operation which is not well-typed, an unresolvable reference to + * an entity or attribute, an unknown named query, or any similar problem. This + * exception type is not used to represent failures that occur while executing + * a query or reading the result set of a query. + *

+ * The two most important subtypes are: + *

+ * + * @see org.hibernate.query.SemanticException + * @see org.hibernate.query.SyntaxException */ public class QueryException extends HibernateException { private final String queryString; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java index e92ac0d376..8d2a4e389e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/BasicSqmPathSource.java @@ -8,7 +8,7 @@ package org.hibernate.metamodel.model.domain.internal; import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.query.ReturnableType; -import org.hibernate.query.TerminalPathException; +import org.hibernate.query.sqm.TerminalPathException; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmPath; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index 806a79581d..fa330baa6f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -47,7 +47,7 @@ import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; -import org.hibernate.query.EntityReferenceException; +import org.hibernate.query.sqm.EntityTypeException; import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.descriptor.java.EnumJavaType; @@ -164,7 +164,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable { public EntityDomainType resolveHqlEntityReference(String entityName) { final EntityDomainType hqlEntityReference = getHqlEntityReference( entityName ); if ( hqlEntityReference == null ) { - throw new EntityReferenceException( "Could not resolve entity reference: " + entityName ); + throw new EntityTypeException( "Could not resolve entity name '" + entityName + "'", entityName ); } return hqlEntityReference; } @@ -444,7 +444,8 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable { // otherwise, try to handle it as a polymorphic reference { - final EntityDomainType polymorphicDomainType = (EntityDomainType) polymorphicEntityReferenceMap.get( javaType ); + final EntityDomainType polymorphicDomainType = + (EntityDomainType) polymorphicEntityReferenceMap.get( javaType ); if ( polymorphicDomainType != null ) { return polymorphicDomainType; } @@ -495,7 +496,7 @@ public class JpaMetamodelImpl implements JpaMetamodelImplementor, Serializable { } } - throw new EntityReferenceException( "Could not resolve entity reference : " + javaType.getName() ); + throw new EntityTypeException( "Could not resolve entity class '" + javaType.getName() + "'", javaType.getName() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/EntityReferenceException.java b/hibernate-core/src/main/java/org/hibernate/query/EntityReferenceException.java deleted file mode 100644 index dadacda4a9..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/query/EntityReferenceException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later - * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html - */ -package org.hibernate.query; - -/** - * Indicates that a reference to an entity did not resolve to - * a mapped entity class. - * - * @apiNote extends {@link IllegalArgumentException} to - * satisfy a questionable requirement of the JPA criteria - * query API - * - * @since 6.3 - * - * @author Gavin King - */ -public class EntityReferenceException extends IllegalArgumentException { - public EntityReferenceException(String message) { - super(message); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/query/NamedQueryValidationException.java b/hibernate-core/src/main/java/org/hibernate/query/NamedQueryValidationException.java index 0dc44f249f..71b5d1b6f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/NamedQueryValidationException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/NamedQueryValidationException.java @@ -13,7 +13,10 @@ import java.util.Map; /** * Indicates that validation and translation of one or more named - * queries failed at initialization time. + * queries failed at initialization time. This exception packages + * every {@link org.hibernate.QueryException} that occurred for an + * invalid HQL/JPQL query, together with any exceptions that indicate + * problems with named native SQL queries. * * @author Gavin King */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/PathException.java b/hibernate-core/src/main/java/org/hibernate/query/PathException.java index ad05feb636..d13e0aa711 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/PathException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/PathException.java @@ -7,23 +7,22 @@ package org.hibernate.query; /** - * Indicates an attempt to use a path in an unsupported way + * Indicates a problem with a path expression in HQL/JPQL. * * @author Steve Ebersole * - * @see PathElementException - * @see TerminalPathException + * @see org.hibernate.query.sqm.UnknownPathException */ public class PathException extends SemanticException { public PathException(String message) { super( message ); } - /** - * @deprecated This is currently unused - */ - @Deprecated(forRemoval = true, since = "6.3") public PathException(String message, Exception cause) { super( message, cause ); } + + public PathException(String message, String hql, Exception cause) { + super(message, hql, cause); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/QueryTypeMismatchException.java b/hibernate-core/src/main/java/org/hibernate/query/QueryTypeMismatchException.java index 60be1f40c5..7109c3668a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/QueryTypeMismatchException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/QueryTypeMismatchException.java @@ -9,7 +9,7 @@ package org.hibernate.query; import org.hibernate.HibernateException; /** - * Indicates a mismatch between a Query's expected and actual result types + * Indicates a mismatch between the expected and actual result types of a query. * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/query/UnknownNamedQueryException.java b/hibernate-core/src/main/java/org/hibernate/query/UnknownNamedQueryException.java index 007d77d912..c38a22ae41 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/UnknownNamedQueryException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/UnknownNamedQueryException.java @@ -9,10 +9,12 @@ package org.hibernate.query; import org.hibernate.QueryException; /** - * Indicates a request for a named-query when no query is - * registered under that name + * Occurs when a named query is requested, and there is no known + * HQL or native SQL query registered under the given name. * * @author Steve Ebersole + * + * @see org.hibernate.query.named.NamedObjectRepository */ public class UnknownNamedQueryException extends QueryException { public UnknownNamedQueryException(String queryName) { 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 fa5ebd3f7a..e4db357631 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 @@ -57,7 +57,7 @@ import org.hibernate.query.ParameterLabelException; import org.hibernate.query.PathException; import org.hibernate.query.ReturnableType; import org.hibernate.query.SemanticException; -import org.hibernate.query.TerminalPathException; +import org.hibernate.query.sqm.TerminalPathException; import org.hibernate.query.criteria.JpaCteCriteria; import org.hibernate.query.criteria.JpaCteCriteriaAttribute; import org.hibernate.query.criteria.JpaCteCriteriaType; diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java index bb422f9b7b..3c8b2b6d49 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/StandardHqlTranslator.java @@ -14,15 +14,17 @@ import org.antlr.v4.runtime.NoViableAltException; import org.hibernate.QueryException; import org.hibernate.grammars.hql.HqlLexer; import org.hibernate.grammars.hql.HqlParser; -import org.hibernate.query.EntityReferenceException; -import org.hibernate.query.PathElementException; +import org.hibernate.query.sqm.EntityTypeException; +import org.hibernate.query.sqm.PathElementException; import org.hibernate.query.SyntaxException; -import org.hibernate.query.TerminalPathException; +import org.hibernate.query.sqm.TerminalPathException; import org.hibernate.query.hql.HqlLogging; import org.hibernate.query.hql.HqlTranslator; import org.hibernate.query.hql.spi.SqmCreationOptions; import org.hibernate.query.sqm.InterpretationException; import org.hibernate.query.sqm.ParsingException; +import org.hibernate.query.sqm.UnknownEntityException; +import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.query.sqm.internal.SqmTreePrinter; import org.hibernate.query.sqm.spi.SqmCreationContext; import org.hibernate.query.sqm.tree.SqmStatement; @@ -78,9 +80,15 @@ public class StandardHqlTranslator implements HqlTranslator { return sqmStatement; } - catch (QueryException | PathElementException | TerminalPathException | EntityReferenceException e) { + catch (QueryException e) { throw e; } + catch (PathElementException | TerminalPathException e) { + throw new UnknownPathException( e.getMessage(), query, e ); + } + catch (EntityTypeException e) { + throw new UnknownEntityException( e.getMessage(), e.getReference(), e ); + } catch (Exception e) { // this is some sort of "unexpected" exception, i.e. something buglike throw new InterpretationException( query, e ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java index 5f9104e709..00ab542e4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/NamedObjectRepositoryImpl.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.function.Consumer; import org.hibernate.HibernateException; +import org.hibernate.QueryException; import org.hibernate.boot.Metadata; import org.hibernate.boot.query.NamedHqlQueryDefinition; import org.hibernate.boot.query.NamedNativeQueryDefinition; @@ -18,16 +19,18 @@ import org.hibernate.boot.query.NamedProcedureCallDefinition; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.procedure.spi.NamedCallableQueryMemento; -import org.hibernate.query.EntityReferenceException; +import org.hibernate.query.sqm.EntityTypeException; import org.hibernate.query.NamedQueryValidationException; -import org.hibernate.query.PathElementException; -import org.hibernate.query.TerminalPathException; +import org.hibernate.query.sqm.PathElementException; +import org.hibernate.query.sqm.TerminalPathException; import org.hibernate.query.named.NamedObjectRepository; import org.hibernate.query.named.NamedQueryMemento; import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryInterpretationCache; import org.hibernate.query.sql.spi.NamedNativeQueryMemento; +import org.hibernate.query.sqm.UnknownEntityException; +import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.query.sqm.spi.NamedSqmQueryMemento; import org.jboss.logging.Logger; @@ -239,21 +242,24 @@ public class NamedObjectRepositoryImpl implements NamedObjectRepository { // Check named HQL queries log.debugf( "Checking %s named HQL queries", sqmMementoMap.size() ); for ( NamedSqmQueryMemento hqlMemento : sqmMementoMap.values() ) { + final String queryString = hqlMemento.getHqlString(); + final String registrationName = hqlMemento.getRegistrationName(); try { - log.debugf( "Checking named HQL query: %s", hqlMemento.getRegistrationName() ); - String queryString = hqlMemento.getHqlString(); + log.debugf( "Checking named HQL query: %s", registrationName ); interpretationCache.resolveHqlInterpretation( queryString, null, s -> queryEngine.getHqlTranslator().translate( queryString, null ) ); } - catch ( HibernateException e ) { - errors.put( hqlMemento.getRegistrationName(), e ); + catch ( QueryException e ) { + errors.put( registrationName, e ); } - catch ( PathElementException | TerminalPathException | EntityReferenceException e ) { - // JPA does not let these be HibernateExceptions in general - errors.put( hqlMemento.getRegistrationName(), new HibernateException(e) ); + catch ( PathElementException | TerminalPathException e ) { + errors.put( registrationName, new UnknownPathException( e.getMessage(), queryString, e ) ); + } + catch ( EntityTypeException e ) { + errors.put( registrationName, new UnknownEntityException( e.getMessage(), e.getReference(), e ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/EntityTypeException.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/EntityTypeException.java new file mode 100644 index 0000000000..02e6e16829 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/EntityTypeException.java @@ -0,0 +1,35 @@ +/* + * 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; + +/** + * Indicates that a reference to an entity, that is, a given entity name + * or Java class object, did not resolve to a known mapped entity type. + * + * @apiNote extends {@link IllegalArgumentException} to + * satisfy a questionable requirement of the JPA + * criteria query API + * + * @since 6.3 + * + * @author Gavin King + */ +public class EntityTypeException extends IllegalArgumentException { + private final String reference; + + public EntityTypeException(String message, String reference) { + super(message); + this.reference = reference; + } + + /** + * The entity name or the name of the Java class. + */ + public String getReference() { + return reference; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/PathElementException.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/PathElementException.java similarity index 82% rename from hibernate-core/src/main/java/org/hibernate/query/PathElementException.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/PathElementException.java index 77d75581f6..10371c064c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/PathElementException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/PathElementException.java @@ -4,15 +4,15 @@ * 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; +package org.hibernate.query.sqm; /** * Indicates that an element of a path did not resolve to * a mapped program element. * * @apiNote extends {@link IllegalArgumentException} to - * satisfy a questionable requirement of the JPA criteria - * query API + * satisfy a questionable requirement of the JPA + * criteria query API * * @since 6.3 * diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java index 9de2997524..92019d68ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java @@ -11,7 +11,6 @@ import java.util.Locale; import jakarta.persistence.metamodel.Bindable; import org.hibernate.metamodel.model.domain.DomainType; -import org.hibernate.query.PathElementException; import org.hibernate.spi.NavigablePath; import org.hibernate.query.sqm.tree.SqmExpressibleAccessor; import org.hibernate.query.sqm.tree.domain.SqmPath; diff --git a/hibernate-core/src/main/java/org/hibernate/query/TerminalPathException.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/TerminalPathException.java similarity index 82% rename from hibernate-core/src/main/java/org/hibernate/query/TerminalPathException.java rename to hibernate-core/src/main/java/org/hibernate/query/sqm/TerminalPathException.java index 398beaca77..31ddfe8d23 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/TerminalPathException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/TerminalPathException.java @@ -4,15 +4,15 @@ * 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; +package org.hibernate.query.sqm; /** * Indicates an attempt to dereference a terminal path * (usually a path referring to something of basic type) * * @apiNote extends {@link IllegalStateException} to - * satisfy a questionable requirement of the JPA criteria - * query API + * satisfy a questionable requirement of the JPA + * criteria query API * * @since 6.3 * diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownEntityException.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownEntityException.java index 4934df2cf4..ef159ee9ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownEntityException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownEntityException.java @@ -9,14 +9,18 @@ package org.hibernate.query.sqm; import org.hibernate.query.SemanticException; /** - * Indicates we were not able to resolve a given "path structure" as an entity name. + * Indicates a failure to resolve an entity name in HQL to a known mapped + * entity type. * - * @apiNote JPA generally requires this to be reported as the much less useful - * {@link IllegalArgumentException}. - * - * todo (6.0) : account for this in the "exception conversion" handling + * @apiNote The JPA criteria API requires that this problem be reported + * as an {@link IllegalArgumentException}, and so we usually + * throw {@link EntityTypeException} from the SQM objects, and + * then wrap as an instance of this exception type in the + * {@link org.hibernate.query.hql.HqlTranslator}. * * @author Steve Ebersole + * + * @see EntityTypeException */ public class UnknownEntityException extends SemanticException { private final String entityName; @@ -30,6 +34,11 @@ public class UnknownEntityException extends SemanticException { this.entityName = entityName; } + public UnknownEntityException(String message, String entityName, Exception cause) { + super( message, cause ); + this.entityName = entityName; + } + public String getEntityName() { return entityName; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownPathException.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownPathException.java index d925631f9e..9d14f4229d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownPathException.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/UnknownPathException.java @@ -8,30 +8,44 @@ package org.hibernate.query.sqm; import java.util.Locale; -import org.hibernate.query.SemanticException; +import org.hibernate.query.PathException; import org.hibernate.query.sqm.tree.domain.SqmPath; /** + * Indicates a failure to resolve an element of a path expression in HQL/JPQL. * - * - * todo (6.0) : account for this in the "exception conversion" handling + * @apiNote The JPA criteria API requires that this sort of problem be reported + * as an {@link IllegalArgumentException} or {@link IllegalStateException}, + * and so we usually throw {@link PathElementException} or + * {@link TerminalPathException} from the SQM objects, and then wrap + * as an instance of this exception type in the + * {@link org.hibernate.query.hql.HqlTranslator}. * * @author Steve Ebersole + * + * @see PathElementException + * @see TerminalPathException */ -public class UnknownPathException extends SemanticException { +public class UnknownPathException extends PathException { + + public UnknownPathException(String message) { + super( message ); + } + + public UnknownPathException(String message, String hql, Exception cause) { + super( message, hql, cause ); + } + + public static UnknownPathException unknownSubPath(SqmPath base, String name) { return new UnknownPathException( String.format( Locale.ROOT, - "Could not resolve path `%s` relative to %s (%s)", + "Could not resolve path element '%s' relative to '%s' (%s)", name, base.getReferencedPathSource().getSqmPathType().getTypeName(), base.getNavigablePath() ) ); } - - private UnknownPathException(String message) { - super( message ); - } } 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 170f1416ac..21f9018346 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 @@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.PathException; +import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.from.SqmFrom; @@ -121,7 +122,7 @@ public class BasicValuedPathInterpretation extends AbstractSqmPathInterpretat } } - throw new PathException( "Path '" + sqmPath.getNavigablePath() + "' did not reference a known model part" ); + throw new UnknownPathException( "Path '" + sqmPath.getNavigablePath() + "' did not reference a known model part" ); } final TableReference tableReference = tableGroup.resolveTableReference( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java index 3a782001ef..33df742478 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBasicValuedSimplePath.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.spi.NavigablePath; import org.hibernate.query.PathException; import org.hibernate.query.hql.spi.SqmCreationState; @@ -70,7 +71,7 @@ public class SqmBasicValuedSimplePath String name, boolean isTerminal, SqmCreationState creationState) { - throw new PathException( + throw new UnknownPathException( String.format( "Could not interpret attribute '%s' of basic-valued path '%s'", name, getNavigablePath() diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java index 2e4a87a531..468b499465 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java @@ -16,7 +16,7 @@ import jakarta.persistence.metamodel.PluralAttribute; import jakarta.persistence.metamodel.SingularAttribute; import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.query.PathException; +import org.hibernate.query.SemanticException; import org.hibernate.spi.NavigablePath; import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.hql.spi.SemanticPathPart; @@ -138,7 +138,7 @@ public interface SqmPath extends SqmExpression, SemanticPathPart, JpaPath< SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { - throw new PathException( "Index operator applied to non-plural path '" + getNavigablePath() + "'" ); + throw new SemanticException( "Index operator applied to non-plural path '" + getNavigablePath() + "'" ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Covariant overrides diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java index 3d0561b1f4..660d3548b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPluralValuedSimplePath.java @@ -90,7 +90,7 @@ public class SqmPluralValuedSimplePath extends AbstractSqmSimplePath { String name, boolean isTerminal, SqmCreationState creationState) { - // this is a reference to a collection outside the from-clause... + // this is a reference to a collection outside the from clause final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( name ); if ( nature == null ) { throw new PathException( "Plural path '" + getNavigablePath() diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java index cc5b4bd26f..74b6c068d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java @@ -10,12 +10,12 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Locale; -import org.hibernate.query.PathException; import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.type.descriptor.java.EnumJavaType; @@ -85,7 +85,7 @@ public class SqmEnumLiteral> extends AbstractSqmExpression String name, boolean isTerminal, SqmCreationState creationState) { - throw new PathException( + throw new UnknownPathException( String.format( Locale.ROOT, "Static enum reference [%s#%s] cannot be de-referenced", @@ -100,7 +100,7 @@ public class SqmEnumLiteral> extends AbstractSqmExpression SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { - throw new PathException( + throw new UnknownPathException( String.format( Locale.ROOT, "Static enum reference [%s#%s] cannot be de-referenced", diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java index 8a9e52c6a5..5f26deb0af 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java @@ -14,14 +14,13 @@ import java.util.List; import java.util.Locale; import org.hibernate.QueryException; -import org.hibernate.query.PathException; -import org.hibernate.query.SemanticException; import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.query.sqm.tree.SqmCopyContext; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; @@ -229,7 +228,7 @@ public class SqmFieldLiteral implements SqmExpression, SqmExpressible, String name, boolean isTerminal, SqmCreationState creationState) { - throw new PathException( + throw new UnknownPathException( String.format( Locale.ROOT, "Static field reference [%s#%s] cannot be de-referenced", @@ -244,7 +243,7 @@ public class SqmFieldLiteral implements SqmExpression, SqmExpressible, SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { - throw new PathException( + throw new UnknownPathException( String.format( Locale.ROOT, "Static field reference [%s#%s] cannot be de-referenced", diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ScrollableCollectionFetchingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ScrollableCollectionFetchingTest.java index 9f84ab0cda..62659011ee 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ScrollableCollectionFetchingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ScrollableCollectionFetchingTest.java @@ -6,13 +6,16 @@ */ package org.hibernate.orm.test.hql; -import org.hibernate.HibernateException; +import org.hibernate.Hibernate; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.DB2Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.SybaseASEDialect; -import org.hibernate.query.PathElementException; +import org.hibernate.query.SemanticException; +import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.RequiresDialectFeature; @@ -43,22 +46,51 @@ import static org.junit.jupiter.api.Assertions.fail; public class ScrollableCollectionFetchingTest { @Test - public void testTupleReturnFails(SessionFactoryScope scope) { + @SkipForDialect(dialectClass=DB2Dialect.class, matchSubTypes = true) + @SkipForDialect(dialectClass= DerbyDialect.class) + public void testTupleReturnWithFetch(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery("insert Mammal (description, bodyWeight, pregnant) values ('Human', 80.0, false)").executeUpdate(); + assertEquals( 1L, session.createSelectionQuery("select count(*) from Mammal").getSingleResult() ); + ScrollableResults results = session.createQuery("select a, a.bodyWeight from Animal a left join fetch a.offspring").scroll(); + assertTrue( results.next() ); + Object[] result = (Object[]) results.get(); + assertTrue(Hibernate.isInitialized(((Animal) result[0]).getOffspring())); + session.createMutationQuery("delete Mammal").executeUpdate(); + } + ); + } + + @Test + public void testTupleReturnWithFetchFailure(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + try { + session.createQuery( "select a.description, a.bodyWeight from Animal a inner join fetch a.offspring" ).scroll(); + fail( "scroll allowed with fetch and projection result" ); + } + catch (IllegalArgumentException e) { + assertTyping( SemanticException.class, e.getCause() ); + } + } + ); + } + + + @Test + public void testUknownPathFailure(SessionFactoryScope scope) { scope.inTransaction( session -> { try { session.createQuery( "select a, a.weight from Animal a inner join fetch a.offspring" ).scroll(); - fail( "scroll allowed with collection fetch and reurning tuples" ); + fail( "scroll allowed with unknown path" ); } catch (IllegalArgumentException e) { - assertTyping( PathElementException.class, e ); - } - catch (HibernateException e) { - // expected result... + assertTyping( UnknownPathException.class, e.getCause() ); } } ); - } @Test