HHH-16710 implicit instantiation of Lists, Maps
This commit is contained in:
parent
4bdfb2da25
commit
87a2b967c7
|
@ -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" );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue