From 24f6e7e6ef4932eac7422e34b1196359cde271f6 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 20 Feb 2024 22:17:38 +0100 Subject: [PATCH] HHH-17764 query result types and single-item selection lists - allow single-item auto-instantiation - check the type of the selection item against the given result type --- .../hibernate/query/spi/AbstractQuery.java | 4 - .../query/spi/AbstractSelectionQuery.java | 32 ++- .../internal/ConcreteSqmSelectQueryPlan.java | 83 ++++--- .../query/sqm/internal/QuerySqmImpl.java | 4 - .../sqm/internal/SqmSelectionQueryImpl.java | 19 +- .../hibernate/query/sqm/internal/SqmUtil.java | 23 ++ .../internal/RowTransformerCheckingImpl.java | 41 ++++ .../AnyImplicitDiscriminatorTest.java | 8 +- .../orm/test/any/annotations/AnyTest.java | 8 +- .../EagerAnyDiscriminatorQueryTest.java | 5 +- ...InheritanceDiscriminatorSelectionTest.java | 4 +- .../CriteriaWrongResultClassTest.java | 1 + .../ConvertedAttributesTypecheckTest.java | 18 +- .../test/proxy/ProxyAsQueryParameterTest.java | 4 +- .../orm/test/query/hql/FunctionTests.java | 4 +- .../query/hql/ImplicitInstantiationTest2.java | 208 ++++++++++++++++++ .../query/hql/MultiValuedParameterTest.java | 4 +- .../hql/treat/HqlTreatJoinFetchTest.java | 4 +- .../where/annotations/EagerManyToOneTest.java | 3 +- 19 files changed, 366 insertions(+), 111 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerCheckingImpl.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ImplicitInstantiationTest2.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java index 310cbc6cdd71..99f6318cbd2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java @@ -29,7 +29,6 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.LockOptions; -import org.hibernate.TypeMismatchException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.graph.GraphSemantic; import org.hibernate.internal.EntityManagerMessageLogger; @@ -654,9 +653,6 @@ public int executeUpdate() throws HibernateException { catch (IllegalQueryOperationException e) { throw new IllegalStateException( e ); } - catch (TypeMismatchException e) { - throw new IllegalArgumentException( e ); - } catch (HibernateException e) { throw getSession().getExceptionConverter().convert( e ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java index 9953a29bc339..da668401eb4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java @@ -39,7 +39,6 @@ import org.hibernate.LockOptions; import org.hibernate.NonUniqueResultException; import org.hibernate.ScrollMode; -import org.hibernate.TypeMismatchException; import org.hibernate.UnknownProfileException; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -89,6 +88,8 @@ import static org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE; import static org.hibernate.jpa.QueryHints.HINT_FOLLOW_ON_LOCKING; import static org.hibernate.jpa.QueryHints.HINT_READONLY; +import static org.hibernate.query.sqm.internal.SqmUtil.isHqlTuple; +import static org.hibernate.query.sqm.internal.SqmUtil.isSelectionAssignableToResultType; /** * @author Steve Ebersole @@ -108,26 +109,26 @@ public AbstractSelectionQuery(SharedSessionContractImplementor session) { } protected TupleMetadata buildTupleMetadata(SqmStatement statement, Class resultType) { - if ( isInstantiableWithoutMetadata( resultType ) ) { - // no need to build metadata for instantiating tuples - return null; - } - else { + if ( statement instanceof SqmSelectStatement ) { final SqmSelectStatement select = (SqmSelectStatement) statement; final List> selections = select.getQueryPart().getFirstQuerySpec().getSelectClause() .getSelections(); - if ( Tuple.class.equals( resultType ) || selections.size() > 1 ) { - return getTupleMetadata( selections ); - } - else { - // only one element in select list, - // we don't support instantiation - return null; - } + return isTupleMetadataRequired( resultType, selections.get(0) ) + ? getTupleMetadata( selections ) + : null; + } + else { + return null; } } + private static boolean isTupleMetadataRequired(Class resultType, SqmSelection selection) { + return isHqlTuple( selection ) + || !isInstantiableWithoutMetadata( resultType ) + && !isSelectionAssignableToResultType( selection, resultType ); + } + private TupleMetadata getTupleMetadata(List> selections) { if ( getQueryOptions().getTupleTransformer() == null ) { return new TupleMetadata( buildTupleElementArray( selections ), buildTupleAliasArray( selections ) ); @@ -431,9 +432,6 @@ public List list() { catch (IllegalQueryOperationException e) { throw new IllegalStateException( e ); } - catch (TypeMismatchException e) { - throw new IllegalArgumentException( e ); - } catch (HibernateException he) { throw getSession().getExceptionConverter().convert( he, getQueryOptions().getLockOptions() ); } 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 fc252ccb5ef3..35517a9bfd95 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 @@ -12,7 +12,6 @@ import java.util.Map; import jakarta.persistence.Tuple; -import org.hibernate.AssertionFailure; import org.hibernate.InstantiationException; import org.hibernate.ScrollMode; import org.hibernate.engine.spi.EntityHolder; @@ -20,7 +19,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.internal.EmptyScrollableResults; -import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.query.Query; import org.hibernate.query.TupleTransformer; @@ -43,6 +41,7 @@ import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.exec.spi.JdbcSelectExecutor; import org.hibernate.sql.results.internal.RowTransformerArrayImpl; +import org.hibernate.sql.results.internal.RowTransformerCheckingImpl; import org.hibernate.sql.results.internal.RowTransformerConstructorImpl; import org.hibernate.sql.results.internal.RowTransformerJpaTupleImpl; import org.hibernate.sql.results.internal.RowTransformerListImpl; @@ -56,7 +55,9 @@ import org.hibernate.sql.results.spi.RowTransformer; import static org.hibernate.internal.util.ReflectHelper.isClass; +import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray; import static org.hibernate.query.sqm.internal.QuerySqmImpl.CRITERIA_HQL_STRING; +import static org.hibernate.query.sqm.internal.SqmUtil.isSelectionAssignableToResultType; /** * Standard Hibernate implementation of SelectQueryPlan for SQM-backed @@ -203,60 +204,60 @@ private static SqmSelection singleSelection(SqmSelectStatement sqm) { return selections.size() == 1 ? selections.get( 0 ) : null; } - private static Class selectionType(SqmSelection selection) { - return selection != null && !selection.getSelectableNode().isCompoundSelection() ? - selection.getNodeJavaType().getJavaTypeClass() - : null; - } + private static final Map,Class> WRAPPERS + = Map.of( + boolean.class, Boolean.class, + int.class, Integer.class, + long.class, Long.class, + short.class, Short.class, + byte.class, Byte.class, + float.class, Float.class, + double.class, Double.class, + char.class, Character.class + ); @SuppressWarnings("unchecked") protected static RowTransformer determineRowTransformer( SqmSelectStatement sqm, - Class resultType, + Class resultClass, TupleMetadata tupleMetadata, QueryOptions queryOptions) { if ( queryOptions.getTupleTransformer() != null ) { return makeRowTransformerTupleTransformerAdapter( sqm, queryOptions ); } - else if ( resultType == null ) { + else if ( resultClass == null ) { return RowTransformerStandardImpl.instance(); } else { + final Class resultType = (Class) + WRAPPERS.getOrDefault( resultClass, resultClass ); final SqmSelection selection = singleSelection( sqm ); - if ( resultType.isArray() && resultType != selectionType( selection ) ) { + if ( isSelectionAssignableToResultType( selection, resultType ) ) { + return RowTransformerSingularReturnImpl.instance(); + } + else if ( resultType.isArray() ) { return (RowTransformer) RowTransformerArrayImpl.instance(); } - else if ( resultType == List.class && resultType != selectionType( selection ) ) { + else if ( List.class.equals( resultType ) ) { return (RowTransformer) RowTransformerListImpl.instance(); } - else { - // NOTE : if we get here : - // 1) there is no TupleTransformer specified - // 2) an explicit result-type, other than an array or List, was specified - - if ( tupleMetadata == null ) { - if ( selection != null ) { - return RowTransformerSingularReturnImpl.instance(); - } - else { - throw new AssertionFailure( "Query defined multiple selections, should have had TupleMetadata" ); - } + else if ( Tuple.class.equals( resultType ) ) { + return (RowTransformer) new RowTransformerJpaTupleImpl( tupleMetadata ); + } + else if ( Map.class.equals( resultType ) ) { + return (RowTransformer) new RowTransformerMapImpl( tupleMetadata ); + } + else if ( isClass( resultType ) ) { + try { + return new RowTransformerConstructorImpl<>( resultType, tupleMetadata ); } - else { - if ( Tuple.class.equals( resultType ) ) { - return (RowTransformer) new RowTransformerJpaTupleImpl( tupleMetadata ); - } - else if ( Map.class.equals( resultType ) ) { - return (RowTransformer) new RowTransformerMapImpl( tupleMetadata ); - } - else if ( isClass( resultType ) ) { - return new RowTransformerConstructorImpl<>( resultType, tupleMetadata ); - } - else { - throw new InstantiationException( "Query result type is not instantiable", resultType ); - } + catch (InstantiationException ie) { + return new RowTransformerCheckingImpl<>( resultType ); } } + else { + return new RowTransformerCheckingImpl<>( resultType ); + } } } @@ -279,10 +280,7 @@ private static RowTransformer makeRowTransformerTupleTransformerAdapter( @SuppressWarnings("unchecked") TupleTransformer tupleTransformer = (TupleTransformer) queryOptions.getTupleTransformer(); - return new RowTransformerTupleTransformerAdapter( - ArrayHelper.toStringArray( aliases ), - tupleTransformer - ); + return new RowTransformerTupleTransformerAdapter<>( toStringArray( aliases ), tupleTransformer ); } @Override @@ -424,10 +422,7 @@ private static CacheableSqmInterpretation buildCacheableSqmInterpretation( final SqlAstTranslator selectTranslator = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory() - .buildSelectTranslator( - sessionFactory, - sqmInterpretation.getSqlAst() - ); + .buildSelectTranslator( sessionFactory, sqmInterpretation.getSqlAst() ); final Map, Map, List>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref( domainParameterXref, sqmInterpretation::getJdbcParamsBySqmParam ); 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 436524062a70..a7939f2a065d 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 @@ -26,7 +26,6 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.ScrollMode; -import org.hibernate.TypeMismatchException; import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.query.spi.EntityGraphQueryHint; import org.hibernate.engine.spi.LoadQueryInfluencers; @@ -692,9 +691,6 @@ public int executeUpdate() { catch (IllegalQueryOperationException e) { throw new IllegalStateException( e ); } - catch (TypeMismatchException e) { - throw new IllegalArgumentException( e ); - } catch (HibernateException e) { throw getSession().getExceptionConverter().convert( e ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java index bfc002340967..9c35fdfaeec2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java @@ -67,7 +67,6 @@ import org.hibernate.sql.results.internal.TupleMetadata; import org.hibernate.sql.results.spi.ResultsConsumer; import org.hibernate.sql.results.spi.SingleResultConsumer; -import org.hibernate.type.descriptor.java.JavaType; import static java.util.stream.Collectors.toList; import static org.hibernate.jpa.HibernateHints.HINT_CACHEABLE; @@ -82,6 +81,7 @@ import static org.hibernate.jpa.SpecHints.HINT_SPEC_CACHE_STORE_MODE; import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOptions; import static org.hibernate.query.sqm.internal.SqmInterpretationsKey.createInterpretationsKey; +import static org.hibernate.query.sqm.internal.SqmUtil.isSelectionAssignableToResultType; import static org.hibernate.query.sqm.internal.SqmUtil.sortSpecification; /** @@ -131,17 +131,14 @@ private Class determineResultType(SqmSelectStatement sqm) { } else { final SqmSelection selection = selections.get(0); - if ( selection!=null ) { - final JavaType javaType = selection.getNodeJavaType(); - if ( javaType != null) { - return javaType.getJavaTypeClass(); - } + if ( isSelectionAssignableToResultType( selection, expectedResultType ) ) { + return selection.getNodeJavaType().getJavaTypeClass(); + } + else { + // let's assume there's some + // way to instantiate it + return expectedResultType; } - // due to some error in the query, - // we don't have any information, - // so just let it through so the - // user sees the real error - return expectedResultType; } } else if ( expectedResultType != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index b293e1936203..7a5078ed11c1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -52,12 +52,14 @@ import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef; import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper; import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.query.sqm.tree.expression.SqmTuple; import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmJoin; import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectableNode; +import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.query.sqm.tree.select.SqmSortSpecification; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlTreeCreationException; @@ -69,6 +71,7 @@ import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.type.JavaObjectType; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; +import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.internal.ConvertedBasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; @@ -601,6 +604,26 @@ static JpaOrder sortSpecification(SqmSelectStatement sqm, Order selection, Class expectedResultType) { + if ( expectedResultType == null + || selection != null && selection.getSelectableNode() instanceof SqmParameter ) { + return true; + } + else if ( selection == null + || !isHqlTuple( selection ) && selection.getSelectableNode().isCompoundSelection() ) { + return false; + } + else { + final JavaType nodeJavaType = selection.getNodeJavaType(); + return nodeJavaType != null + && expectedResultType.isAssignableFrom( nodeJavaType.getJavaTypeClass() ); + } + } + + public static boolean isHqlTuple(SqmSelection selection) { + return selection != null && selection.getSelectableNode() instanceof SqmTuple; + } + private static class CriteriaParameterCollector { private Set> sqmParameters; private Map, List>> jpaCriteriaParamResolutions; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerCheckingImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerCheckingImpl.java new file mode 100644 index 000000000000..e4d116e48ca3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerCheckingImpl.java @@ -0,0 +1,41 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ + +package org.hibernate.sql.results.internal; + +import org.hibernate.TypeMismatchException; +import org.hibernate.sql.results.spi.RowTransformer; + +/** + * @author Gavin King + */ +public class RowTransformerCheckingImpl implements RowTransformer { + + private final Class type; + + public RowTransformerCheckingImpl(Class type) { + this.type = type; + } + + @Override + @SuppressWarnings("unchecked") + public R transformRow(Object[] row) { + final Object result = row[0]; + if ( result == null || type.isInstance( result ) ) { + return (R) result; + } + else { + throw new TypeMismatchException( "Result type is '" + type.getSimpleName() + + "' but the query returned a '" + result.getClass().getSimpleName() + "'" ); + } + } + + @Override + public int determineNumberOfResultElements(int rawElementCount) { + return 1; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyImplicitDiscriminatorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyImplicitDiscriminatorTest.java index 2b767c0efa18..ae0d852605f9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyImplicitDiscriminatorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyImplicitDiscriminatorTest.java @@ -123,13 +123,13 @@ public void dropTestData(SessionFactoryScope scope) { public void testHqlAnyIdQuery(SessionFactoryScope scope) { scope.inTransaction( session -> { - List list1 = session.createQuery( + List list1 = session.createQuery( "select p from ImplicitPropertyHolder p where id(p.property) = 666", - PropertySet.class ).list(); + ImplicitPropertyHolder.class ).list(); assertEquals( 0, list1.size() ); - List list2 = session.createQuery( + List list2 = session.createQuery( "select p from ImplicitPropertyHolder p where type(p.property) = IntegerProperty", - PropertySet.class ).list(); + ImplicitPropertyHolder.class ).list(); assertEquals( 1, list2.size() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyTest.java index 8f98b50b9ef9..52929c5f7785 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/AnyTest.java @@ -136,13 +136,13 @@ public void dropTestData(SessionFactoryScope scope) { public void testHqlAnyIdQuery(SessionFactoryScope scope) { scope.inTransaction( session -> { - List list1 = session.createQuery( + List list1 = session.createQuery( "select p from PropertyHolder p where id(p.property) = 666", - PropertySet.class ).list(); + PropertyHolder.class ).list(); assertEquals( 0, list1.size() ); - List list2 = session.createQuery( + List list2 = session.createQuery( "select p from PropertyHolder p where type(p.property) = IntegerProperty", - PropertySet.class ).list(); + PropertyHolder.class ).list(); assertEquals( 1, list2.size() ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/EagerAnyDiscriminatorQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/EagerAnyDiscriminatorQueryTest.java index 6395e995834f..b37268a31fd6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/EagerAnyDiscriminatorQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/any/annotations/EagerAnyDiscriminatorQueryTest.java @@ -2,6 +2,7 @@ import java.util.List; +import jakarta.persistence.TypedQuery; import org.hibernate.Hibernate; import org.hibernate.annotations.Any; import org.hibernate.annotations.AnyDiscriminator; @@ -82,9 +83,9 @@ public void testNativeQuery(EntityManagerFactoryScope scope) { public void testHQLQuery(EntityManagerFactoryScope scope) { scope.inTransaction( entityManager -> { - Query q = entityManager.createQuery( + TypedQuery q = entityManager.createQuery( "select p from PropertyHolder p", - LazyAnyDiscriminatorQueryTest.PropertyHolder.class + PropertyHolder.class ); List results = q.getResultList(); assertThat( results.size() ).isEqualTo( 1 ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceDiscriminatorSelectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceDiscriminatorSelectionTest.java index cc1a360ce311..458006f1bd21 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceDiscriminatorSelectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/discriminator/JoinedInheritanceDiscriminatorSelectionTest.java @@ -88,14 +88,14 @@ public void testSelectDiscriminator(SessionFactoryScope scope) { scope.inTransaction( session -> { assertThat( session.createQuery( "select p.class from ParentEntity p", - String.class + Class.class ).getResultList() ).hasSize( 4 ); inspector.assertNumberOfJoins( 0, 0 ); inspector.clear(); assertThat( session.createQuery( "select type(p) from ParentEntity p", - String.class + Class.class ).getResultList() ).hasSize( 4 ); inspector.assertNumberOfJoins( 0, 0 ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaWrongResultClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaWrongResultClassTest.java index 384404ad3cfc..039cc7b643a5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaWrongResultClassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaWrongResultClassTest.java @@ -13,6 +13,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.FailureExpected; import org.hibernate.testing.orm.junit.Jpa; import org.hibernate.testing.orm.junit.Setting; import org.junit.jupiter.api.Test; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java index 5b36def8cd5a..b034182814f8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/converted/converter/ConvertedAttributesTypecheckTest.java @@ -61,22 +61,22 @@ public void testLikeOnConvertedString(SessionFactoryScope scope) { @Test public void testUnaryExpressionOnConvertedNumber(SessionFactoryScope scope) { scope.inTransaction( session -> { - final String result = session.createQuery( - "select -convertedNumber from TestEntity", - String.class + session.createQuery( + "from TestEntity where -convertedNumber = -123", + TestEntity.class ).getSingleResult(); - assertThat( result ).isEqualTo( "-123" ); } ); } @Test public void testFromDurationExpressionOnConvertedDuration(SessionFactoryScope scope) { scope.inTransaction( session -> { - final String result = session.createQuery( - "select convertedDuration by day from TestEntity", - String.class - ).getSingleResult(); - assertThat( Long.parseLong( result ) ).isEqualTo( Duration.ofDays( 3 ).toMillis() ); + session.createQuery( + "from TestEntity where convertedDuration by day = ?1", + TestEntity.class + ) + .setParameter( 1, Duration.ofDays( 3 ).toNanos() ) + .getSingleResult(); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java index 51776c7ccf92..a6350fcce0e5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java @@ -118,9 +118,9 @@ public void testSubSubclassProxyParam(SessionFactoryScope scope) { .setParameter( "productId", LUXURY_PRODUCT_ID ) .getSingleResult(); assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse(); - final LuxuryCarVendor result = session.createQuery( + final CarVendor result = session.createQuery( "from CarVendor v where v = :vendor", - LuxuryCarVendor.class + CarVendor.class ).setParameter( "vendor", product.getVendor() ).getSingleResult(); assertThat( result.getId() ).isEqualTo( product.getVendor().getId() ); } ); 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 index 7dc1bcb22be4..597def2cb53a 100644 --- 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 @@ -2002,7 +2002,7 @@ public void testFormat(SessionFactoryScope scope) { session -> { session.createQuery("select format(e.theDate as 'dd/MM/yy'), format(e.theDate as 'EEEE, MMMM dd, yyyy') from EntityOfBasics e", Object[].class) .list(); - session.createQuery("select format(e.theTimestamp as 'dd/MM/yyyy ''at'' HH:mm:ss') from EntityOfBasics e", Date.class) + session.createQuery("select format(e.theTimestamp as 'dd/MM/yyyy ''at'' HH:mm:ss') from EntityOfBasics e", String.class) .list(); assertThat( @@ -2019,7 +2019,7 @@ public void testFormat(SessionFactoryScope scope) { public void testFormatTime(SessionFactoryScope scope) { scope.inTransaction( session -> { - session.createQuery("select format(e.theTime as 'hh:mm:ss a') from EntityOfBasics e", Date.class) + session.createQuery("select format(e.theTime as 'hh:mm:ss a') from EntityOfBasics e", String.class) .list(); assertThat( session.createQuery("select format(theTime as '''Hello'', hh:mm:ss a') from EntityOfBasics where id=123", String.class).getResultList().get(0), diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ImplicitInstantiationTest2.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ImplicitInstantiationTest2.java new file mode 100644 index 000000000000..1e818fa7c749 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/ImplicitInstantiationTest2.java @@ -0,0 +1,208 @@ +package org.hibernate.orm.test.query.hql; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Tuple; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@DomainModel( + annotatedClasses = { + ImplicitInstantiationTest2.Thing.class + } +) +@SessionFactory +public class ImplicitInstantiationTest2 { + + static class Record { + String name; + public Record(String name) { + this.name = name; + } + String name() { + return name; + } + } + + @Test + public void testRecordInstantiationWithoutAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Record result = session.createSelectionQuery("select upper(name) from Thing", Record.class).getSingleResult(); + assertEquals( result.name(), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testSqlRecordInstantiationWithoutAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Record result = session.createNativeQuery("select upper(name) as name from thingy_table", Record.class) + .addSynchronizedEntityClass(Thing.class) + .getSingleResult(); + assertEquals( result.name(), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testTupleInstantiationWithAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Tuple result = session.createQuery("select upper(name) as name from Thing", Tuple.class).getSingleResult(); + assertEquals( result.get("name"), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testTupleInstantiationWithoutAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Tuple result = session.createSelectionQuery("select upper(name) from Thing", Tuple.class).getSingleResult(); + assertEquals( result.get(0), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testMapInstantiationWithoutAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Map result = session.createSelectionQuery("select upper(name) from Thing", Map.class).getSingleResult(); + assertEquals( result.get("0"), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testMapInstantiationWithAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Map result = session.createQuery("select upper(name) as name from Thing", Map.class).getSingleResult(); + assertEquals( result.get("name"), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testListInstantiationWithoutAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + List result = session.createSelectionQuery("select upper(name) from Thing", List.class).getSingleResult(); + assertEquals( result.get(0), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testListInstantiationWithAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + List result = session.createQuery("select upper(name) as name from Thing", List.class).getSingleResult(); + assertEquals( result.get(0), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testSqlTupleInstantiationWithAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Tuple result = session.createNativeQuery("select upper(name) as name from thingy_table", Tuple.class) + .addSynchronizedEntityClass(Thing.class) + .getSingleResult(); + assertEquals( result.get("name"), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testSqlMapInstantiationWithAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + Map result = session.createNativeQuery("select upper(name) as name from thingy_table", Map.class) + .addSynchronizedEntityClass(Thing.class) + .getSingleResult(); + assertEquals( result.get("name"), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + @Test + public void testSqlListInstantiationWithoutAlias(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.persist(new Thing(1L, "thing")); + List result = session.createNativeQuery("select upper(name) as name from thingy_table", List.class) + .addSynchronizedEntityClass(Thing.class) + .getSingleResult(); + assertEquals( result.get(0), "THING" ); + session.getTransaction().setRollbackOnly(); + } + ); + } + + + @Entity(name = "Thing") + @Table(name = "thingy_table") + public class Thing { + private Long id; + + private String name; + + public Thing(Long id, String name) { + this.id = id; + this.name = name; + } + + Thing() { + } + + @Id + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java index a1b1abf05112..a083fbcd3a9c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/MultiValuedParameterTest.java @@ -93,9 +93,9 @@ public void testParameterListIn() { public void test() { inTransaction( session -> { final List ids = List.of( BigInteger.ZERO, BigInteger.ONE, BigInteger.TWO ); - final List resultList = session.createQuery( + final List resultList = session.createQuery( "select id from EntityWithNumericId e WHERE e.id in (:ids)", - EntityWithNumericId.class + BigInteger.class ).setParameter( "ids", ids ).getResultList(); assertThat( resultList.size(), is( 3 ) ); assertThat( resultList, is( ids ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java index 81b904ee0602..c10633411ce0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/treat/HqlTreatJoinFetchTest.java @@ -64,9 +64,9 @@ public void testTreatJoinFetch(SessionFactoryScope scope) { public void testJoinFetchRootTreat(SessionFactoryScope scope) { scope.inTransaction( session -> { - QueryImplementor query = session.createQuery( + QueryImplementor query = session.createQuery( "select t from BaseEntity t join fetch treat(t as JoinedEntity).testEntity j left join fetch j.joined2 e", - TestEntity.class + BaseEntity.class ); query.list(); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/EagerManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/EagerManyToOneTest.java index 64df0306b037..92e7a266ae34 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/EagerManyToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/EagerManyToOneTest.java @@ -78,8 +78,7 @@ public void testFindParent(EntityManagerFactoryScope scope) { ); scope.inTransaction( entityManager -> { - - List children = entityManager.createQuery( "select c from Child c", EagerManyToOne2Test.Child.class ) + List children = entityManager.createQuery( "select c from Child c", Child.class ) .getResultList(); assertThat( children.size() ).isEqualTo( 0 ); }