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
This commit is contained in:
parent
bb477c180a
commit
c45f047443
|
@ -29,7 +29,6 @@ import org.hibernate.FlushMode;
|
|||
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 abstract class AbstractQuery<R>
|
|||
catch (IllegalQueryOperationException e) {
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
catch (TypeMismatchException e) {
|
||||
throw new IllegalArgumentException( e );
|
||||
}
|
||||
catch (HibernateException e) {
|
||||
throw getSession().getExceptionConverter().convert( e );
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ import org.hibernate.LockMode;
|
|||
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_CACHE_REGION;
|
|||
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,24 +109,24 @@ public abstract class AbstractSelectionQuery<R>
|
|||
}
|
||||
|
||||
protected TupleMetadata buildTupleMetadata(SqmStatement<?> statement, Class<R> 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<SqmSelection<?>> 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 <R> boolean isTupleMetadataRequired(Class<R> resultType, SqmSelection<?> selection) {
|
||||
return isHqlTuple( selection )
|
||||
|| !isInstantiableWithoutMetadata( resultType )
|
||||
&& !isSelectionAssignableToResultType( selection, resultType );
|
||||
}
|
||||
|
||||
private TupleMetadata getTupleMetadata(List<SqmSelection<?>> selections) {
|
||||
|
@ -431,9 +432,6 @@ public abstract class AbstractSelectionQuery<R>
|
|||
catch (IllegalQueryOperationException e) {
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
catch (TypeMismatchException e) {
|
||||
throw new IllegalArgumentException( e );
|
||||
}
|
||||
catch (HibernateException he) {
|
||||
throw getSession().getExceptionConverter().convert( he, getQueryOptions().getLockOptions() );
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.List;
|
|||
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.SessionFactoryImplementor;
|
|||
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.JdbcParameterBindings;
|
|||
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.ResultsConsumer;
|
|||
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,59 +204,59 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
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<?>,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 <T> RowTransformer<T> determineRowTransformer(
|
||||
SqmSelectStatement<?> sqm,
|
||||
Class<T> resultType,
|
||||
Class<T> 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<T> resultType = (Class<T>)
|
||||
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<T>) RowTransformerArrayImpl.instance();
|
||||
}
|
||||
else if ( resultType == List.class && resultType != selectionType( selection ) ) {
|
||||
else if ( List.class.equals( resultType ) ) {
|
||||
return (RowTransformer<T>) RowTransformerListImpl.instance();
|
||||
}
|
||||
else if ( Tuple.class.equals( resultType ) ) {
|
||||
return (RowTransformer<T>) new RowTransformerJpaTupleImpl( tupleMetadata );
|
||||
}
|
||||
else if ( Map.class.equals( resultType ) ) {
|
||||
return (RowTransformer<T>) new RowTransformerMapImpl( tupleMetadata );
|
||||
}
|
||||
else if ( isClass( resultType ) ) {
|
||||
try {
|
||||
return new RowTransformerConstructorImpl<>( resultType, tupleMetadata );
|
||||
}
|
||||
catch (InstantiationException ie) {
|
||||
return new RowTransformerCheckingImpl<>( resultType );
|
||||
}
|
||||
}
|
||||
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<T>) new RowTransformerJpaTupleImpl( tupleMetadata );
|
||||
}
|
||||
else if ( Map.class.equals( resultType ) ) {
|
||||
return (RowTransformer<T>) new RowTransformerMapImpl( tupleMetadata );
|
||||
}
|
||||
else if ( isClass( resultType ) ) {
|
||||
return new RowTransformerConstructorImpl<>( resultType, tupleMetadata );
|
||||
}
|
||||
else {
|
||||
throw new InstantiationException( "Query result type is not instantiable", resultType );
|
||||
}
|
||||
}
|
||||
return new RowTransformerCheckingImpl<>( resultType );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,10 +280,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
TupleTransformer<T> tupleTransformer = (TupleTransformer<T>) queryOptions.getTupleTransformer();
|
||||
return new RowTransformerTupleTransformerAdapter<T>(
|
||||
ArrayHelper.toStringArray( aliases ),
|
||||
tupleTransformer
|
||||
);
|
||||
return new RowTransformerTupleTransformerAdapter<>( toStringArray( aliases ), tupleTransformer );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -424,10 +422,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
|
||||
final SqlAstTranslator<JdbcOperationQuerySelect> selectTranslator =
|
||||
sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory()
|
||||
.buildSelectTranslator(
|
||||
sessionFactory,
|
||||
sqmInterpretation.getSqlAst()
|
||||
);
|
||||
.buildSelectTranslator( sessionFactory, sqmInterpretation.getSqlAst() );
|
||||
|
||||
final Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> jdbcParamsXref
|
||||
= SqmUtil.generateJdbcParamsXref( domainParameterXref, sqmInterpretation::getJdbcParamsBySqmParam );
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.hibernate.HibernateException;
|
|||
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 class QuerySqmImpl<R>
|
|||
catch (IllegalQueryOperationException e) {
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
catch (TypeMismatchException e) {
|
||||
throw new IllegalArgumentException( e );
|
||||
}
|
||||
catch (HibernateException e) {
|
||||
throw getSession().getExceptionConverter().convert( e );
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ import org.hibernate.query.sqm.tree.select.SqmSelection;
|
|||
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_RETRIEVE_MODE;
|
|||
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 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
|
|||
}
|
||||
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 ) {
|
||||
|
|
|
@ -52,12 +52,14 @@ import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
|
|||
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.JdbcParameterBindings;
|
|||
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 @@ public class SqmUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean isSelectionAssignableToResultType(SqmSelection<?> 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<SqmParameter<?>> sqmParameters;
|
||||
private Map<JpaCriteriaParameter<?>, List<SqmJpaCriteriaParameterWrapper<?>>> jpaCriteriaParamResolutions;
|
||||
|
|
|
@ -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<R> implements RowTransformer<R> {
|
||||
|
||||
private final Class<R> type;
|
||||
|
||||
public RowTransformerCheckingImpl(Class<R> 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;
|
||||
}
|
||||
}
|
|
@ -123,13 +123,13 @@ public class AnyImplicitDiscriminatorTest {
|
|||
public void testHqlAnyIdQuery(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
List<PropertySet> list1 = session.createQuery(
|
||||
List<ImplicitPropertyHolder> list1 = session.createQuery(
|
||||
"select p from ImplicitPropertyHolder p where id(p.property) = 666",
|
||||
PropertySet.class ).list();
|
||||
ImplicitPropertyHolder.class ).list();
|
||||
assertEquals( 0, list1.size() );
|
||||
List<PropertySet> list2 = session.createQuery(
|
||||
List<ImplicitPropertyHolder> list2 = session.createQuery(
|
||||
"select p from ImplicitPropertyHolder p where type(p.property) = IntegerProperty",
|
||||
PropertySet.class ).list();
|
||||
ImplicitPropertyHolder.class ).list();
|
||||
assertEquals( 1, list2.size() );
|
||||
|
||||
}
|
||||
|
|
|
@ -136,13 +136,13 @@ public class AnyTest {
|
|||
public void testHqlAnyIdQuery(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
List<PropertySet> list1 = session.createQuery(
|
||||
List<PropertyHolder> list1 = session.createQuery(
|
||||
"select p from PropertyHolder p where id(p.property) = 666",
|
||||
PropertySet.class ).list();
|
||||
PropertyHolder.class ).list();
|
||||
assertEquals( 0, list1.size() );
|
||||
List<PropertySet> list2 = session.createQuery(
|
||||
List<PropertyHolder> list2 = session.createQuery(
|
||||
"select p from PropertyHolder p where type(p.property) = IntegerProperty",
|
||||
PropertySet.class ).list();
|
||||
PropertyHolder.class ).list();
|
||||
assertEquals( 1, list2.size() );
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.hibernate.orm.test.any.annotations;
|
|||
|
||||
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 class EagerAnyDiscriminatorQueryTest {
|
|||
public void testHQLQuery(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
Query q = entityManager.createQuery(
|
||||
TypedQuery<PropertyHolder> q = entityManager.createQuery(
|
||||
"select p from PropertyHolder p",
|
||||
LazyAnyDiscriminatorQueryTest.PropertyHolder.class
|
||||
PropertyHolder.class
|
||||
);
|
||||
List<PropertyHolder> results = q.getResultList();
|
||||
assertThat( results.size() ).isEqualTo( 1 );
|
||||
|
|
|
@ -88,14 +88,14 @@ public class JoinedInheritanceDiscriminatorSelectionTest {
|
|||
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 );
|
||||
} );
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Map;
|
|||
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;
|
||||
|
|
|
@ -61,22 +61,22 @@ public class ConvertedAttributesTypecheckTest {
|
|||
@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();
|
||||
} );
|
||||
}
|
||||
|
||||
|
|
|
@ -118,9 +118,9 @@ public class ProxyAsQueryParameterTest {
|
|||
.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() );
|
||||
} );
|
||||
|
|
|
@ -2002,7 +2002,7 @@ public class FunctionTests {
|
|||
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 class FunctionTests {
|
|||
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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -93,9 +93,9 @@ public class MultiValuedParameterTest extends BaseSessionFactoryFunctionalTest {
|
|||
public void test() {
|
||||
inTransaction( session -> {
|
||||
final List<BigInteger> ids = List.of( BigInteger.ZERO, BigInteger.ONE, BigInteger.TWO );
|
||||
final List<EntityWithNumericId> resultList = session.createQuery(
|
||||
final List<BigInteger> 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 ) );
|
||||
|
|
|
@ -64,9 +64,9 @@ public class HqlTreatJoinFetchTest {
|
|||
public void testJoinFetchRootTreat(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
QueryImplementor<TestEntity> query = session.createQuery(
|
||||
QueryImplementor<BaseEntity> 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();
|
||||
}
|
||||
|
|
|
@ -78,8 +78,7 @@ public class EagerManyToOneTest {
|
|||
);
|
||||
scope.inTransaction(
|
||||
entityManager -> {
|
||||
|
||||
List<EagerManyToOne2Test.Child> children = entityManager.createQuery( "select c from Child c", EagerManyToOne2Test.Child.class )
|
||||
List<Child> children = entityManager.createQuery( "select c from Child c", Child.class )
|
||||
.getResultList();
|
||||
assertThat( children.size() ).isEqualTo( 0 );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue