HHH-16710 implicit instantiation of Lists, Maps

This commit is contained in:
Gavin 2023-05-28 12:14:44 +02:00 committed by Gavin King
parent 4bdfb2da25
commit 87a2b967c7
12 changed files with 361 additions and 135 deletions

View File

@ -23,7 +23,7 @@ public class NonUniqueResultException extends HibernateException {
* @param resultCount The number of actual results.
*/
public NonUniqueResultException(int resultCount) {
super( "query did not return a unique result: " + resultCount );
super( "Query did not return a unique result: " + resultCount + " results were returned" );
}
}

View File

@ -742,7 +742,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
}
}
protected static <R> void checkResultType(Class<R> expectedResultType, SqmSelectionQueryImpl<?> query) {
protected static <R> void checkResultType(Class<R> expectedResultType, SqmSelectionQueryImpl<R> query) {
final Class<?> resultType = query.getResultType();
if ( !expectedResultType.isAssignableFrom( resultType ) ) {
throw new QueryTypeMismatchException(
@ -849,6 +849,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
if ( Tuple.class.equals( resultClass ) ) {
query.setTupleTransformer( new NativeQueryTupleTransformer() );
}
// TODO: handle Map, List as well
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
}

View File

@ -104,40 +104,56 @@ public abstract class AbstractSelectionQuery<R>
super( session );
}
protected TupleMetadata buildTupleMetadata(SqmStatement<?> statement, Class<R> resultType) {
if ( resultType != null && Tuple.class.isAssignableFrom( resultType ) ) {
final List<SqmSelection<?>> selections = ( (SqmSelectStatement<?>) statement ).getQueryPart()
.getFirstQuerySpec()
.getSelectClause()
.getSelections();
// resultType is Tuple..
if ( getQueryOptions().getTupleTransformer() == null ) {
final Map<TupleElement<?>, Integer> tupleElementMap;
if ( selections.size() == 1 && selections.get( 0 ).getSelectableNode() instanceof CompoundSelection<?> ) {
final List<? extends JpaSelection<?>> selectionItems = selections.get( 0 )
.getSelectableNode()
.getSelectionItems();
tupleElementMap = new IdentityHashMap<>( selectionItems.size() );
for ( int i = 0; i < selectionItems.size(); i++ ) {
tupleElementMap.put( selectionItems.get( i ), i );
}
}
else {
tupleElementMap = new IdentityHashMap<>( selections.size() );
for ( int i = 0; i < selections.size(); i++ ) {
final SqmSelection<?> selection = selections.get( i );
tupleElementMap.put( selection.getSelectableNode(), i );
}
}
return new TupleMetadata( tupleElementMap );
}
public static boolean isTupleResultClass(Class<?> resultType) {
return Tuple.class.isAssignableFrom( resultType )
|| Map.class.isAssignableFrom( resultType );
}
throw new IllegalArgumentException(
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer : " +
getQueryOptions().getTupleTransformer()
);
protected TupleMetadata buildTupleMetadata(SqmStatement<?> statement, Class<R> resultType) {
if ( resultType == null ) {
return null;
}
return null;
else if ( isTupleResultClass( resultType ) ) {
final List<SqmSelection<?>> selections =
( (SqmSelectStatement<?>) statement ).getQueryPart()
.getFirstQuerySpec()
.getSelectClause()
.getSelections();
if ( getQueryOptions().getTupleTransformer() == null ) {
return new TupleMetadata( buildTupleElementMap( selections ) );
}
else {
throw new IllegalArgumentException(
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer : " +
getQueryOptions().getTupleTransformer()
);
}
}
else {
return null;
}
}
private static Map<TupleElement<?>, Integer> buildTupleElementMap(List<SqmSelection<?>> selections) {
final Map<TupleElement<?>, Integer> tupleElementMap;
if ( selections.size() == 1
&& selections.get( 0 ).getSelectableNode() instanceof CompoundSelection<?> ) {
final List<? extends JpaSelection<?>> selectionItems =
selections.get( 0 ).getSelectableNode()
.getSelectionItems();
tupleElementMap = new IdentityHashMap<>( selectionItems.size() );
for ( int i = 0; i < selectionItems.size(); i++ ) {
tupleElementMap.put( selectionItems.get( i ), i );
}
}
else {
tupleElementMap = new IdentityHashMap<>( selections.size() );
for (int i = 0; i < selections.size(); i++ ) {
final SqmSelection<?> selection = selections.get( i );
tupleElementMap.put( selection.getSelectableNode(), i );
}
}
return tupleElementMap;
}
protected void applyOptions(NamedSqmQueryMemento memento) {
@ -207,7 +223,7 @@ public abstract class AbstractSelectionQuery<R>
*/
protected void visitQueryReturnType(
SqmQueryPart<R> queryPart,
Class<R> resultType,
Class<R> expectedResultType,
SessionFactoryImplementor factory) {
assert getQueryString().equals( CRITERIA_HQL_STRING );
@ -231,33 +247,35 @@ public abstract class AbstractSelectionQuery<R>
}
}
if ( resultType != null ) {
checkQueryReturnType( sqmQuerySpec, resultType, factory );
if ( expectedResultType != null ) {
checkQueryReturnType( sqmQuerySpec, expectedResultType, factory );
}
}
else {
final SqmQueryGroup<R> queryGroup = (SqmQueryGroup<R>) queryPart;
for ( SqmQueryPart<R> sqmQueryPart : queryGroup.getQueryParts() ) {
visitQueryReturnType( sqmQueryPart, resultType, factory );
visitQueryReturnType( sqmQueryPart, expectedResultType, factory );
}
}
}
protected static <T> void checkQueryReturnType(
SqmQuerySpec<T> querySpec,
Class<T> resultClass,
Class<T> expectedResultClass,
SessionFactoryImplementor sessionFactory) {
if ( resultClass == null || resultClass == Object.class ) {
if ( expectedResultClass == null || expectedResultClass == Object.class ) {
// nothing to check
return;
}
final List<SqmSelection<?>> selections = querySpec.getSelectClause().getSelections();
if ( resultClass.isArray() ) {
if ( expectedResultClass.isArray() ) {
// todo (6.0) : implement
}
else if ( Tuple.class.isAssignableFrom( resultClass ) ) {
else if ( Tuple.class.isAssignableFrom( expectedResultClass )
|| Map.class.isAssignableFrom( expectedResultClass )
|| List.class.isAssignableFrom( expectedResultClass ) ) {
// todo (6.0) : implement
}
else {
@ -290,21 +308,18 @@ public abstract class AbstractSelectionQuery<R>
if ( jpaQueryComplianceEnabled ) {
return;
}
verifyResultType( resultClass, sqmSelection.getNodeType(), sessionFactory );
verifyResultType( expectedResultClass, sqmSelection.getNodeType() );
}
}
protected static <T> void verifyResultType(
Class<T> resultClass,
SqmExpressible<?> sqmExpressible,
SessionFactoryImplementor sessionFactory) {
protected static <T> void verifyResultType(Class<T> resultClass, SqmExpressible<?> sqmExpressible) {
assert sqmExpressible != null;
final JavaType<?> expressibleJavaType = sqmExpressible.getExpressibleJavaType();
assert expressibleJavaType != null;
final Class<?> javaTypeClass = expressibleJavaType.getJavaTypeClass();
if ( !resultClass.isAssignableFrom( javaTypeClass ) ) {
if ( expressibleJavaType instanceof PrimitiveJavaType ) {
if ( ( (PrimitiveJavaType) expressibleJavaType ).getPrimitiveClass() == resultClass ) {
if ( ( (PrimitiveJavaType<?>) expressibleJavaType ).getPrimitiveClass() == resultClass ) {
return;
}
throwQueryTypeMismatchException( resultClass, sqmExpressible );
@ -887,7 +902,7 @@ public abstract class AbstractSelectionQuery<R>
}
@Override
public SelectionQuery<R> setParameterList(String name, Collection values) {
public SelectionQuery<R> setParameterList(String name, @SuppressWarnings("rawtypes") Collection values) {
super.setParameterList( name, values );
return this;
}
@ -995,7 +1010,7 @@ public abstract class AbstractSelectionQuery<R>
}
@Override
public SelectionQuery<R> setProperties(Map map) {
public SelectionQuery<R> setProperties(@SuppressWarnings("rawtypes") Map map) {
super.setProperties( map );
return this;
}

View File

@ -149,7 +149,7 @@ public interface SqmQuery extends CommonQueryContract {
SqmQuery setProperties(Object bean);
@Override
SqmQuery setProperties(Map bean);
SqmQuery setProperties(@SuppressWarnings("rawtypes") Map bean);
@Override
SqmQuery setHibernateFlushMode(FlushMode flushMode);

View File

@ -82,7 +82,7 @@ public interface SqmSelectionQuery<R> extends SqmQuery, SelectionQuery<R> {
SqmSelectionQuery<R> setParameter(Parameter<Date> param, Date value, TemporalType temporalType);
@Override
SqmSelectionQuery<R> setParameterList(String name, Collection values);
SqmSelectionQuery<R> setParameterList(String name, @SuppressWarnings("rawtypes") Collection values);
@Override
<P> SqmSelectionQuery<R> setParameterList(String name, Collection<? extends P> values, Class<P> javaType);
@ -100,7 +100,7 @@ public interface SqmSelectionQuery<R> extends SqmQuery, SelectionQuery<R> {
<P> SqmSelectionQuery<R> setParameterList(String name, P[] values, BindableType<P> type);
@Override
SqmSelectionQuery<R> setParameterList(int position, Collection values);
SqmSelectionQuery<R> setParameterList(int position, @SuppressWarnings("rawtypes") Collection values);
@Override
<P> SqmSelectionQuery<R> setParameterList(int position, Collection<? extends P> values, Class<P> javaType);
@ -139,7 +139,7 @@ public interface SqmSelectionQuery<R> extends SqmQuery, SelectionQuery<R> {
SqmSelectionQuery<R> setProperties(Object bean);
@Override
SqmSelectionQuery<R> setProperties(Map bean);
SqmSelectionQuery<R> setProperties(@SuppressWarnings("rawtypes") Map bean);
@Override
SqmSelectionQuery<R> setHibernateFlushMode(FlushMode flushMode);

View File

@ -11,6 +11,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.persistence.Tuple;
import org.hibernate.AssertionFailure;
import org.hibernate.ScrollMode;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
@ -41,7 +43,6 @@ import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.FromClauseAccess;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
@ -50,6 +51,8 @@ import org.hibernate.sql.exec.spi.JdbcSelectExecutor;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
import org.hibernate.sql.results.internal.RowTransformerJpaTupleImpl;
import org.hibernate.sql.results.internal.RowTransformerListImpl;
import org.hibernate.sql.results.internal.RowTransformerMapImpl;
import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl;
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter;
@ -183,22 +186,26 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
if ( resultType == Object[].class ) {
return (RowTransformer<T>) RowTransformerArrayImpl.instance();
}
else if ( List.class.equals( resultType ) ) {
return (RowTransformer<T>) RowTransformerListImpl.instance();
}
// NOTE : if we get here :
// 1) there is no TupleTransformer specified
// 2) an explicit result-type, other than an array, was specified
final List<SqmSelection<?>> selections = sqm.getQueryPart().getFirstQuerySpec().getSelectClause().getSelections();
final List<SqmSelection<?>> selections =
sqm.getQueryPart().getFirstQuerySpec().getSelectClause().getSelections();
if ( tupleMetadata != null ) {
// resultType is Tuple..
if ( queryOptions.getTupleTransformer() == null ) {
if ( Tuple.class.equals( resultType ) ) {
return (RowTransformer<T>) new RowTransformerJpaTupleImpl( tupleMetadata );
}
throw new IllegalArgumentException(
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer : " +
queryOptions.getTupleTransformer()
);
else if ( Map.class.equals( resultType ) ) {
return (RowTransformer<T>) new RowTransformerMapImpl( tupleMetadata );
}
else {
throw new AssertionFailure( "Wrong result type for tuple handling: " + resultType );
}
}
// NOTE : if we get here we have a resultType of some kind

View File

@ -218,7 +218,7 @@ public class QuerySqmImpl<R>
*/
public QuerySqmImpl(
SqmStatement<R> criteria,
Class<R> resultType,
Class<R> expectedResultType,
SharedSessionContractImplementor producer) {
super( producer );
this.hql = CRITERIA_HQL_STRING;
@ -262,12 +262,12 @@ public class QuerySqmImpl<R>
queryPart.validateQueryStructureAndFetchOwners();
visitQueryReturnType(
queryPart,
resultType,
expectedResultType,
producer.getFactory()
);
}
else {
if ( resultType != null ) {
if ( expectedResultType != null ) {
throw new IllegalQueryOperationException(
"Result type given for a non-SELECT Query",
hql,
@ -288,8 +288,8 @@ public class QuerySqmImpl<R>
}
}
this.resultType = resultType;
this.tupleMetadata = buildTupleMetadata( criteria, resultType );
this.resultType = expectedResultType;
this.tupleMetadata = buildTupleMetadata( criteria, expectedResultType );
}
private void validateStatement(SqmStatement<R> sqmStatement, Class<R> resultType) {

View File

@ -21,7 +21,6 @@ import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.Parameter;
import jakarta.persistence.TemporalType;
import jakarta.persistence.Tuple;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
@ -55,7 +54,6 @@ import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sqm.SqmSelectionQuery;
import org.hibernate.query.sqm.internal.SqmInterpretationsKey.InterpretationsKeySource;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
@ -74,6 +72,8 @@ import static org.hibernate.jpa.LegacySpecHints.HINT_JAVAEE_CACHE_STORE_MODE;
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.tree.SqmCopyContext.simpleContext;
/**
* @author Steve Ebersole
@ -86,7 +86,8 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
private final DomainParameterXref domainParameterXref;
private final QueryParameterBindingsImpl parameterBindings;
private final Class<R> resultType;
private final Class<R> expectedResultType;
private final Class<?> resultType;
private final TupleMetadata tupleMetadata;
public SqmSelectionQueryImpl(
@ -104,30 +105,35 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
this.expectedResultType = expectedResultType;
// visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
this.resultType = determineResultType( sqm, expectedResultType );
this.resultType = determineResultType( sqm );
setComment( hql );
this.tupleMetadata = buildTupleMetadata( sqm, expectedResultType );
}
private static <T> Class<T> determineResultType(SqmSelectStatement<?> sqm, Class<?> expectedResultType) {
if ( expectedResultType != null && expectedResultType.equals( Tuple.class ) ) {
//noinspection unchecked
return (Class<T>) Tuple.class;
}
if ( expectedResultType == null || ! expectedResultType.isArray() ) {
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
if ( selections.size() == 1 ) {
final SqmSelection<?> sqmSelection = selections.get( 0 );
//noinspection unchecked
return (Class<T>) sqmSelection.getNodeJavaType().getJavaTypeClass();
private Class<?> determineResultType(SqmSelectStatement<?> sqm) {
if ( expectedResultType != null ) {
if ( expectedResultType.isArray() ) {
return Object[].class;
}
else if ( List.class.isAssignableFrom( expectedResultType ) ) {
return expectedResultType;
}
else if ( isTupleResultClass( expectedResultType ) ) {
return expectedResultType;
}
else {
return Object[].class;
}
}
//noinspection unchecked
return (Class<T>) Object[].class;
else {
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
return selections.size() == 1
? selections.get(0).getNodeJavaType().getJavaTypeClass()
: Object[].class;
}
}
public SqmSelectionQueryImpl(
@ -136,6 +142,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
SharedSessionContractImplementor session) {
super( session );
this.hql = memento.getHqlString();
this.expectedResultType = resultType;
this.resultType = resultType;
final SessionFactoryImplementor factory = session.getFactory();
@ -164,10 +171,10 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
public SqmSelectionQueryImpl(
NamedCriteriaQueryMementoImpl memento,
Class<R> resultType,
Class<R> expectedResultType,
SharedSessionContractImplementor session) {
//noinspection unchecked
this( (SqmSelectStatement<R>) memento.getSqmStatement(), resultType, session);
this( (SqmSelectStatement<R>) memento.getSqmStatement(), expectedResultType, session );
applyOptions( memento );
}
@ -177,27 +184,20 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
SharedSessionContractImplementor session) {
super( session );
this.hql = CRITERIA_HQL_STRING;
if ( session.isCriteriaCopyTreeEnabled() ) {
this.sqm = criteria.copy( SqmCopyContext.simpleContext() );
}
else {
this.sqm = criteria;
}
this.sqm = session.isCriteriaCopyTreeEnabled() ? criteria.copy( simpleContext() ) : criteria;
this.domainParameterXref = DomainParameterXref.from( sqm );
if ( ! domainParameterXref.hasParameters() ) {
this.parameterMetadata = ParameterMetadataImpl.EMPTY;
}
else {
this.parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
}
this.parameterMetadata = domainParameterXref.hasParameters()
? new ParameterMetadataImpl( domainParameterXref.getQueryParameters() )
: ParameterMetadataImpl.EMPTY;
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
for ( SqmParameter<?> sqmParameter : this.domainParameterXref.getParameterResolutions().getSqmParameters() ) {
for ( SqmParameter<?> sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) {
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<?> ) {
final JpaCriteriaParameter<Object> jpaCriteriaParameter = ( (SqmJpaCriteriaParameterWrapper<Object>) sqmParameter ).getJpaCriteriaParameter();
final JpaCriteriaParameter<Object> jpaCriteriaParameter =
( (SqmJpaCriteriaParameterWrapper<Object>) sqmParameter ).getJpaCriteriaParameter();
final Object value = jpaCriteriaParameter.getValue();
// We don't set a null value, unless the type is also null which is the case when using HibernateCriteriaBuilder.value
if ( value != null || jpaCriteriaParameter.getNodeType() == null ) {
@ -209,7 +209,8 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
}
}
this.resultType = determineResultType( sqm, expectedResultType );
this.expectedResultType = expectedResultType;
this.resultType = determineResultType( sqm );
visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
setComment( hql );
@ -336,12 +337,10 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
// Query plan
private SelectQueryPlan<R> resolveQueryPlan() {
final QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.createInterpretationsKey( this );
final QueryInterpretationCache.Key cacheKey = createInterpretationsKey( this );
if ( cacheKey != null ) {
return getSession().getFactory().getQueryEngine().getInterpretationCache().resolveSelectQueryPlan(
cacheKey,
this::buildQueryPlan
);
return getSession().getFactory().getQueryEngine().getInterpretationCache()
.resolveSelectQueryPlan( cacheKey, this::buildQueryPlan );
}
else {
return buildQueryPlan();
@ -354,40 +353,29 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
getSession().getFactory()
);
if ( concreteSqmStatements.length > 1 ) {
return buildAggregatedQueryPlan( concreteSqmStatements );
}
else {
return buildConcreteQueryPlan( concreteSqmStatements[0], getResultType(), getQueryOptions() );
}
return concreteSqmStatements.length > 1
? buildAggregatedQueryPlan( concreteSqmStatements )
: buildConcreteQueryPlan( concreteSqmStatements[0], getQueryOptions() );
}
private SelectQueryPlan<R> buildAggregatedQueryPlan(SqmSelectStatement<?>[] concreteSqmStatements) {
//noinspection unchecked
final SelectQueryPlan<R>[] aggregatedQueryPlans = new SelectQueryPlan[ concreteSqmStatements.length ];
// todo (6.0) : we want to make sure that certain thing (ResultListTransformer, etc) only get applied at the aggregator-level
for ( int i = 0, x = concreteSqmStatements.length; i < x; i++ ) {
aggregatedQueryPlans[i] = buildConcreteQueryPlan(
concreteSqmStatements[i],
getResultType(),
getQueryOptions()
);
aggregatedQueryPlans[i] = buildConcreteQueryPlan( concreteSqmStatements[i], getQueryOptions() );
}
return new AggregatedSelectQueryPlanImpl<>( aggregatedQueryPlans );
}
private <T> SelectQueryPlan<T> buildConcreteQueryPlan(
private SelectQueryPlan<R> buildConcreteQueryPlan(
SqmSelectStatement<?> concreteSqmStatement,
Class<T> resultType,
QueryOptions queryOptions) {
return new ConcreteSqmSelectQueryPlan<>(
concreteSqmStatement,
getQueryString(),
getDomainParameterXref(),
resultType,
expectedResultType,
tupleMetadata,
queryOptions
);
@ -399,7 +387,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
// InterpretationsKeySource
@Override
public Class<R> getResultType() {
public Class<?> getResultType() {
return resultType;
}
@ -684,7 +672,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
}
@Override
public SqmSelectionQuery<R> setParameterList(String name, Collection values) {
public SqmSelectionQuery<R> setParameterList(String name, @SuppressWarnings("rawtypes") Collection values) {
super.setParameterList( name, values );
return this;
}
@ -720,7 +708,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
}
@Override
public SqmSelectionQuery<R> setParameterList(int position, Collection values) {
public SqmSelectionQuery<R> setParameterList(int position, @SuppressWarnings("rawtypes") Collection values) {
super.setParameterList( position, values );
return this;
}
@ -792,7 +780,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
}
@Override
public SqmSelectionQuery<R> setProperties(Map map) {
public SqmSelectionQuery<R> setProperties(@SuppressWarnings("rawtypes") Map map) {
super.setProperties( map );
return this;
}

View File

@ -0,0 +1,32 @@
/*
* 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.sql.results.spi.RowTransformer;
import java.util.List;
/**
* RowTransformer used when an array is explicitly specified as the return type
*
* @author Steve Ebersole
*/
public class RowTransformerListImpl<T> implements RowTransformer<List<Object>> {
/**
* Singleton access
*/
public static final RowTransformerListImpl INSTANCE = new RowTransformerListImpl();
public static RowTransformerListImpl instance() {
return INSTANCE;
}
@Override
public List<Object> transformRow(Object[] row) {
return List.of( row );
}
}

View File

@ -0,0 +1,48 @@
/*
* 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 jakarta.persistence.Tuple;
import jakarta.persistence.TupleElement;
import org.hibernate.sql.results.spi.RowTransformer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* RowTransformer generating a JPA {@link Tuple}
*
* @author Steve Ebersole
*/
public class RowTransformerMapImpl implements RowTransformer<Map<String,Object>> {
private final TupleMetadata tupleMetadata;
public RowTransformerMapImpl(TupleMetadata tupleMetadata) {
this.tupleMetadata = tupleMetadata;
}
@Override
public Map<String,Object> transformRow(Object[] row) {
Map<String,Object> map = new HashMap<>( row.length );
List<TupleElement<?>> list = tupleMetadata.getList();
for ( int i = 0; i < row.length; i++ ) {
String alias = list.get(i).getAlias();
if ( alias == null ) {
alias = Integer.toString(i);
}
map.put( alias, row[i] );
}
return map;
}
@Override
public int determineNumberOfResultElements(int rawElementCount) {
return 1;
}
}

View File

@ -10,9 +10,10 @@ import java.util.Arrays;
import java.util.List;
import jakarta.persistence.TupleElement;
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
import org.hibernate.query.JpaTuple;
import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.getDescriptorByPrimitiveType;
/**
* Implementation of the JPA Tuple contract
*
@ -45,7 +46,7 @@ public class TupleImpl implements JpaTuple {
public <X> X get(String alias, Class<X> type) {
final Object untyped = get( alias );
if ( untyped != null ) {
if (!elementTypeMatches(type, untyped)) {
if ( !elementTypeMatches( type, untyped ) ) {
throw new IllegalArgumentException(
String.format(
"Requested tuple value [alias=%s, value=%s] cannot be assigned to requested type [%s]",
@ -99,9 +100,8 @@ public class TupleImpl implements JpaTuple {
}
private <X> boolean elementTypeMatches(Class<X> type, Object untyped) {
return type.isInstance(untyped)
|| type.isPrimitive()
&& PrimitiveWrapperHelper.getDescriptorByPrimitiveType( type).getWrapperClass().isInstance( untyped);
return type.isInstance( untyped )
|| type.isPrimitive() && getDescriptorByPrimitiveType( type ).getWrapperClass().isInstance( untyped );
}
@Override

View File

@ -0,0 +1,135 @@
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 = {
ImplicitInstantiationTest.Thing.class
}
)
@SessionFactory
public class ImplicitInstantiationTest {
@Test
public void testTupleInstantiationWithAlias(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist(new Thing(1L, "thing"));
Tuple result = session.createQuery("select id as id, upper(name) as name from Thing", Tuple.class).getSingleResult();
assertEquals( result.get("id"), 1L );
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 id, upper(name) from Thing", Tuple.class).getSingleResult();
assertEquals( result.get(0), 1L );
assertEquals( result.get(1), "THING" );
session.getTransaction().setRollbackOnly();
}
);
}
@Test
public void testMapInstantiationWithoutAlias(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist(new Thing(1L, "thing"));
Map result = session.createSelectionQuery("select id, upper(name) from Thing", Map.class).getSingleResult();
assertEquals( result.get("0"), 1L );
assertEquals( result.get("1"), "THING" );
session.getTransaction().setRollbackOnly();
}
);
}
@Test
public void testMapInstantiationWithAlias(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist(new Thing(1L, "thing"));
Map result = session.createQuery("select id as id, upper(name) as name from Thing", Map.class).getSingleResult();
assertEquals( result.get("id"), 1L );
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 id, upper(name) from Thing", List.class).getSingleResult();
assertEquals( result.get(0), 1L );
assertEquals( result.get(1), "THING" );
session.getTransaction().setRollbackOnly();
}
);
}
@Test
public void testListInstantiationWithAlias(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist(new Thing(1L, "thing"));
List result = session.createQuery("select id as id, upper(name) as name from Thing", List.class).getSingleResult();
assertEquals( result.get(0), 1L );
assertEquals( result.get(1), "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;
}
}
}