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.
|
* @param resultCount The number of actual results.
|
||||||
*/
|
*/
|
||||||
public NonUniqueResultException(int resultCount) {
|
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();
|
final Class<?> resultType = query.getResultType();
|
||||||
if ( !expectedResultType.isAssignableFrom( resultType ) ) {
|
if ( !expectedResultType.isAssignableFrom( resultType ) ) {
|
||||||
throw new QueryTypeMismatchException(
|
throw new QueryTypeMismatchException(
|
||||||
|
@ -849,6 +849,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
if ( Tuple.class.equals( resultClass ) ) {
|
if ( Tuple.class.equals( resultClass ) ) {
|
||||||
query.setTupleTransformer( new NativeQueryTupleTransformer() );
|
query.setTupleTransformer( new NativeQueryTupleTransformer() );
|
||||||
}
|
}
|
||||||
|
// TODO: handle Map, List as well
|
||||||
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
|
else if ( getFactory().getMappingMetamodel().isEntityClass( resultClass ) ) {
|
||||||
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,40 +104,56 @@ public abstract class AbstractSelectionQuery<R>
|
||||||
super( session );
|
super( session );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TupleMetadata buildTupleMetadata(SqmStatement<?> statement, Class<R> resultType) {
|
public static boolean isTupleResultClass(Class<?> resultType) {
|
||||||
if ( resultType != null && Tuple.class.isAssignableFrom( resultType ) ) {
|
return Tuple.class.isAssignableFrom( resultType )
|
||||||
final List<SqmSelection<?>> selections = ( (SqmSelectStatement<?>) statement ).getQueryPart()
|
|| Map.class.isAssignableFrom( resultType );
|
||||||
.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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException(
|
protected TupleMetadata buildTupleMetadata(SqmStatement<?> statement, Class<R> resultType) {
|
||||||
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer : " +
|
if ( resultType == null ) {
|
||||||
getQueryOptions().getTupleTransformer()
|
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) {
|
protected void applyOptions(NamedSqmQueryMemento memento) {
|
||||||
|
@ -207,7 +223,7 @@ public abstract class AbstractSelectionQuery<R>
|
||||||
*/
|
*/
|
||||||
protected void visitQueryReturnType(
|
protected void visitQueryReturnType(
|
||||||
SqmQueryPart<R> queryPart,
|
SqmQueryPart<R> queryPart,
|
||||||
Class<R> resultType,
|
Class<R> expectedResultType,
|
||||||
SessionFactoryImplementor factory) {
|
SessionFactoryImplementor factory) {
|
||||||
assert getQueryString().equals( CRITERIA_HQL_STRING );
|
assert getQueryString().equals( CRITERIA_HQL_STRING );
|
||||||
|
|
||||||
|
@ -231,33 +247,35 @@ public abstract class AbstractSelectionQuery<R>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( resultType != null ) {
|
if ( expectedResultType != null ) {
|
||||||
checkQueryReturnType( sqmQuerySpec, resultType, factory );
|
checkQueryReturnType( sqmQuerySpec, expectedResultType, factory );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final SqmQueryGroup<R> queryGroup = (SqmQueryGroup<R>) queryPart;
|
final SqmQueryGroup<R> queryGroup = (SqmQueryGroup<R>) queryPart;
|
||||||
for ( SqmQueryPart<R> sqmQueryPart : queryGroup.getQueryParts() ) {
|
for ( SqmQueryPart<R> sqmQueryPart : queryGroup.getQueryParts() ) {
|
||||||
visitQueryReturnType( sqmQueryPart, resultType, factory );
|
visitQueryReturnType( sqmQueryPart, expectedResultType, factory );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static <T> void checkQueryReturnType(
|
protected static <T> void checkQueryReturnType(
|
||||||
SqmQuerySpec<T> querySpec,
|
SqmQuerySpec<T> querySpec,
|
||||||
Class<T> resultClass,
|
Class<T> expectedResultClass,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
if ( resultClass == null || resultClass == Object.class ) {
|
if ( expectedResultClass == null || expectedResultClass == Object.class ) {
|
||||||
// nothing to check
|
// nothing to check
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<SqmSelection<?>> selections = querySpec.getSelectClause().getSelections();
|
final List<SqmSelection<?>> selections = querySpec.getSelectClause().getSelections();
|
||||||
|
|
||||||
if ( resultClass.isArray() ) {
|
if ( expectedResultClass.isArray() ) {
|
||||||
// todo (6.0) : implement
|
// 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
|
// todo (6.0) : implement
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -290,21 +308,18 @@ public abstract class AbstractSelectionQuery<R>
|
||||||
if ( jpaQueryComplianceEnabled ) {
|
if ( jpaQueryComplianceEnabled ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
verifyResultType( resultClass, sqmSelection.getNodeType(), sessionFactory );
|
verifyResultType( expectedResultClass, sqmSelection.getNodeType() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static <T> void verifyResultType(
|
protected static <T> void verifyResultType(Class<T> resultClass, SqmExpressible<?> sqmExpressible) {
|
||||||
Class<T> resultClass,
|
|
||||||
SqmExpressible<?> sqmExpressible,
|
|
||||||
SessionFactoryImplementor sessionFactory) {
|
|
||||||
assert sqmExpressible != null;
|
assert sqmExpressible != null;
|
||||||
final JavaType<?> expressibleJavaType = sqmExpressible.getExpressibleJavaType();
|
final JavaType<?> expressibleJavaType = sqmExpressible.getExpressibleJavaType();
|
||||||
assert expressibleJavaType != null;
|
assert expressibleJavaType != null;
|
||||||
final Class<?> javaTypeClass = expressibleJavaType.getJavaTypeClass();
|
final Class<?> javaTypeClass = expressibleJavaType.getJavaTypeClass();
|
||||||
if ( !resultClass.isAssignableFrom( javaTypeClass ) ) {
|
if ( !resultClass.isAssignableFrom( javaTypeClass ) ) {
|
||||||
if ( expressibleJavaType instanceof PrimitiveJavaType ) {
|
if ( expressibleJavaType instanceof PrimitiveJavaType ) {
|
||||||
if ( ( (PrimitiveJavaType) expressibleJavaType ).getPrimitiveClass() == resultClass ) {
|
if ( ( (PrimitiveJavaType<?>) expressibleJavaType ).getPrimitiveClass() == resultClass ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throwQueryTypeMismatchException( resultClass, sqmExpressible );
|
throwQueryTypeMismatchException( resultClass, sqmExpressible );
|
||||||
|
@ -887,7 +902,7 @@ public abstract class AbstractSelectionQuery<R>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SelectionQuery<R> setParameterList(String name, Collection values) {
|
public SelectionQuery<R> setParameterList(String name, @SuppressWarnings("rawtypes") Collection values) {
|
||||||
super.setParameterList( name, values );
|
super.setParameterList( name, values );
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -995,7 +1010,7 @@ public abstract class AbstractSelectionQuery<R>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SelectionQuery<R> setProperties(Map map) {
|
public SelectionQuery<R> setProperties(@SuppressWarnings("rawtypes") Map map) {
|
||||||
super.setProperties( map );
|
super.setProperties( map );
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,7 @@ public interface SqmQuery extends CommonQueryContract {
|
||||||
SqmQuery setProperties(Object bean);
|
SqmQuery setProperties(Object bean);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
SqmQuery setProperties(Map bean);
|
SqmQuery setProperties(@SuppressWarnings("rawtypes") Map bean);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
SqmQuery setHibernateFlushMode(FlushMode flushMode);
|
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);
|
SqmSelectionQuery<R> setParameter(Parameter<Date> param, Date value, TemporalType temporalType);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
SqmSelectionQuery<R> setParameterList(String name, Collection values);
|
SqmSelectionQuery<R> setParameterList(String name, @SuppressWarnings("rawtypes") Collection values);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
<P> SqmSelectionQuery<R> setParameterList(String name, Collection<? extends P> values, Class<P> javaType);
|
<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);
|
<P> SqmSelectionQuery<R> setParameterList(String name, P[] values, BindableType<P> type);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
SqmSelectionQuery<R> setParameterList(int position, Collection values);
|
SqmSelectionQuery<R> setParameterList(int position, @SuppressWarnings("rawtypes") Collection values);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
<P> SqmSelectionQuery<R> setParameterList(int position, Collection<? extends P> values, Class<P> javaType);
|
<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);
|
SqmSelectionQuery<R> setProperties(Object bean);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
SqmSelectionQuery<R> setProperties(Map bean);
|
SqmSelectionQuery<R> setProperties(@SuppressWarnings("rawtypes") Map bean);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
SqmSelectionQuery<R> setHibernateFlushMode(FlushMode flushMode);
|
SqmSelectionQuery<R> setHibernateFlushMode(FlushMode flushMode);
|
||||||
|
|
|
@ -11,6 +11,8 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jakarta.persistence.Tuple;
|
||||||
|
import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.ScrollMode;
|
import org.hibernate.ScrollMode;
|
||||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
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.SqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.spi.FromClauseAccess;
|
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.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
|
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
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.graph.entity.LoadingEntityEntry;
|
||||||
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
|
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
|
||||||
import org.hibernate.sql.results.internal.RowTransformerJpaTupleImpl;
|
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.RowTransformerSingularReturnImpl;
|
||||||
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
|
import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
|
||||||
import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter;
|
import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter;
|
||||||
|
@ -183,22 +186,26 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
||||||
if ( resultType == Object[].class ) {
|
if ( resultType == Object[].class ) {
|
||||||
return (RowTransformer<T>) RowTransformerArrayImpl.instance();
|
return (RowTransformer<T>) RowTransformerArrayImpl.instance();
|
||||||
}
|
}
|
||||||
|
else if ( List.class.equals( resultType ) ) {
|
||||||
|
return (RowTransformer<T>) RowTransformerListImpl.instance();
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE : if we get here :
|
// NOTE : if we get here :
|
||||||
// 1) there is no TupleTransformer specified
|
// 1) there is no TupleTransformer specified
|
||||||
// 2) an explicit result-type, other than an array, was 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 ) {
|
if ( tupleMetadata != null ) {
|
||||||
// resultType is Tuple..
|
if ( Tuple.class.equals( resultType ) ) {
|
||||||
if ( queryOptions.getTupleTransformer() == null ) {
|
|
||||||
return (RowTransformer<T>) new RowTransformerJpaTupleImpl( tupleMetadata );
|
return (RowTransformer<T>) new RowTransformerJpaTupleImpl( tupleMetadata );
|
||||||
}
|
}
|
||||||
|
else if ( Map.class.equals( resultType ) ) {
|
||||||
throw new IllegalArgumentException(
|
return (RowTransformer<T>) new RowTransformerMapImpl( tupleMetadata );
|
||||||
"Illegal combination of Tuple resultType and (non-JpaTupleBuilder) TupleTransformer : " +
|
}
|
||||||
queryOptions.getTupleTransformer()
|
else {
|
||||||
);
|
throw new AssertionFailure( "Wrong result type for tuple handling: " + resultType );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE : if we get here we have a resultType of some kind
|
// NOTE : if we get here we have a resultType of some kind
|
||||||
|
|
|
@ -218,7 +218,7 @@ public class QuerySqmImpl<R>
|
||||||
*/
|
*/
|
||||||
public QuerySqmImpl(
|
public QuerySqmImpl(
|
||||||
SqmStatement<R> criteria,
|
SqmStatement<R> criteria,
|
||||||
Class<R> resultType,
|
Class<R> expectedResultType,
|
||||||
SharedSessionContractImplementor producer) {
|
SharedSessionContractImplementor producer) {
|
||||||
super( producer );
|
super( producer );
|
||||||
this.hql = CRITERIA_HQL_STRING;
|
this.hql = CRITERIA_HQL_STRING;
|
||||||
|
@ -262,12 +262,12 @@ public class QuerySqmImpl<R>
|
||||||
queryPart.validateQueryStructureAndFetchOwners();
|
queryPart.validateQueryStructureAndFetchOwners();
|
||||||
visitQueryReturnType(
|
visitQueryReturnType(
|
||||||
queryPart,
|
queryPart,
|
||||||
resultType,
|
expectedResultType,
|
||||||
producer.getFactory()
|
producer.getFactory()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( resultType != null ) {
|
if ( expectedResultType != null ) {
|
||||||
throw new IllegalQueryOperationException(
|
throw new IllegalQueryOperationException(
|
||||||
"Result type given for a non-SELECT Query",
|
"Result type given for a non-SELECT Query",
|
||||||
hql,
|
hql,
|
||||||
|
@ -288,8 +288,8 @@ public class QuerySqmImpl<R>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resultType = resultType;
|
this.resultType = expectedResultType;
|
||||||
this.tupleMetadata = buildTupleMetadata( criteria, resultType );
|
this.tupleMetadata = buildTupleMetadata( criteria, expectedResultType );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateStatement(SqmStatement<R> sqmStatement, Class<R> resultType) {
|
private void validateStatement(SqmStatement<R> sqmStatement, Class<R> resultType) {
|
||||||
|
|
|
@ -21,7 +21,6 @@ import jakarta.persistence.FlushModeType;
|
||||||
import jakarta.persistence.LockModeType;
|
import jakarta.persistence.LockModeType;
|
||||||
import jakarta.persistence.Parameter;
|
import jakarta.persistence.Parameter;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
import jakarta.persistence.Tuple;
|
|
||||||
|
|
||||||
import org.hibernate.CacheMode;
|
import org.hibernate.CacheMode;
|
||||||
import org.hibernate.FlushMode;
|
import org.hibernate.FlushMode;
|
||||||
|
@ -55,7 +54,6 @@ import org.hibernate.query.spi.ScrollableResultsImplementor;
|
||||||
import org.hibernate.query.spi.SelectQueryPlan;
|
import org.hibernate.query.spi.SelectQueryPlan;
|
||||||
import org.hibernate.query.sqm.SqmSelectionQuery;
|
import org.hibernate.query.sqm.SqmSelectionQuery;
|
||||||
import org.hibernate.query.sqm.internal.SqmInterpretationsKey.InterpretationsKeySource;
|
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.JpaCriteriaParameter;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
|
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
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_RETRIEVE_MODE;
|
||||||
import static org.hibernate.jpa.SpecHints.HINT_SPEC_CACHE_STORE_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.spi.SqlOmittingQueryOptions.omitSqlQueryOptions;
|
||||||
|
import static org.hibernate.query.sqm.internal.SqmInterpretationsKey.createInterpretationsKey;
|
||||||
|
import static org.hibernate.query.sqm.tree.SqmCopyContext.simpleContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -86,7 +86,8 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
private final DomainParameterXref domainParameterXref;
|
private final DomainParameterXref domainParameterXref;
|
||||||
private final QueryParameterBindingsImpl parameterBindings;
|
private final QueryParameterBindingsImpl parameterBindings;
|
||||||
|
|
||||||
private final Class<R> resultType;
|
private final Class<R> expectedResultType;
|
||||||
|
private final Class<?> resultType;
|
||||||
private final TupleMetadata tupleMetadata;
|
private final TupleMetadata tupleMetadata;
|
||||||
|
|
||||||
public SqmSelectionQueryImpl(
|
public SqmSelectionQueryImpl(
|
||||||
|
@ -104,30 +105,35 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
|
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
|
||||||
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
|
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
|
||||||
|
|
||||||
|
this.expectedResultType = expectedResultType;
|
||||||
// visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
|
// visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
|
||||||
this.resultType = determineResultType( sqm, expectedResultType );
|
this.resultType = determineResultType( sqm );
|
||||||
|
|
||||||
setComment( hql );
|
setComment( hql );
|
||||||
this.tupleMetadata = buildTupleMetadata( sqm, expectedResultType );
|
this.tupleMetadata = buildTupleMetadata( sqm, expectedResultType );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> Class<T> determineResultType(SqmSelectStatement<?> sqm, Class<?> expectedResultType) {
|
private Class<?> determineResultType(SqmSelectStatement<?> sqm) {
|
||||||
if ( expectedResultType != null && expectedResultType.equals( Tuple.class ) ) {
|
if ( expectedResultType != null ) {
|
||||||
//noinspection unchecked
|
if ( expectedResultType.isArray() ) {
|
||||||
return (Class<T>) Tuple.class;
|
return Object[].class;
|
||||||
}
|
}
|
||||||
|
else if ( List.class.isAssignableFrom( expectedResultType ) ) {
|
||||||
if ( expectedResultType == null || ! expectedResultType.isArray() ) {
|
return expectedResultType;
|
||||||
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
|
}
|
||||||
if ( selections.size() == 1 ) {
|
else if ( isTupleResultClass( expectedResultType ) ) {
|
||||||
final SqmSelection<?> sqmSelection = selections.get( 0 );
|
return expectedResultType;
|
||||||
//noinspection unchecked
|
}
|
||||||
return (Class<T>) sqmSelection.getNodeJavaType().getJavaTypeClass();
|
else {
|
||||||
|
return Object[].class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
//noinspection unchecked
|
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
|
||||||
return (Class<T>) Object[].class;
|
return selections.size() == 1
|
||||||
|
? selections.get(0).getNodeJavaType().getJavaTypeClass()
|
||||||
|
: Object[].class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqmSelectionQueryImpl(
|
public SqmSelectionQueryImpl(
|
||||||
|
@ -136,6 +142,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
SharedSessionContractImplementor session) {
|
SharedSessionContractImplementor session) {
|
||||||
super( session );
|
super( session );
|
||||||
this.hql = memento.getHqlString();
|
this.hql = memento.getHqlString();
|
||||||
|
this.expectedResultType = resultType;
|
||||||
this.resultType = resultType;
|
this.resultType = resultType;
|
||||||
|
|
||||||
final SessionFactoryImplementor factory = session.getFactory();
|
final SessionFactoryImplementor factory = session.getFactory();
|
||||||
|
@ -164,10 +171,10 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
|
|
||||||
public SqmSelectionQueryImpl(
|
public SqmSelectionQueryImpl(
|
||||||
NamedCriteriaQueryMementoImpl memento,
|
NamedCriteriaQueryMementoImpl memento,
|
||||||
Class<R> resultType,
|
Class<R> expectedResultType,
|
||||||
SharedSessionContractImplementor session) {
|
SharedSessionContractImplementor session) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
this( (SqmSelectStatement<R>) memento.getSqmStatement(), resultType, session);
|
this( (SqmSelectStatement<R>) memento.getSqmStatement(), expectedResultType, session );
|
||||||
applyOptions( memento );
|
applyOptions( memento );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,27 +184,20 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
SharedSessionContractImplementor session) {
|
SharedSessionContractImplementor session) {
|
||||||
super( session );
|
super( session );
|
||||||
this.hql = CRITERIA_HQL_STRING;
|
this.hql = CRITERIA_HQL_STRING;
|
||||||
if ( session.isCriteriaCopyTreeEnabled() ) {
|
this.sqm = session.isCriteriaCopyTreeEnabled() ? criteria.copy( simpleContext() ) : criteria;
|
||||||
this.sqm = criteria.copy( SqmCopyContext.simpleContext() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.sqm = criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.domainParameterXref = DomainParameterXref.from( sqm );
|
this.domainParameterXref = DomainParameterXref.from( sqm );
|
||||||
if ( ! domainParameterXref.hasParameters() ) {
|
this.parameterMetadata = domainParameterXref.hasParameters()
|
||||||
this.parameterMetadata = ParameterMetadataImpl.EMPTY;
|
? new ParameterMetadataImpl( domainParameterXref.getQueryParameters() )
|
||||||
}
|
: ParameterMetadataImpl.EMPTY;
|
||||||
else {
|
|
||||||
this.parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
|
|
||||||
}
|
|
||||||
|
|
||||||
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
|
this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory() );
|
||||||
|
|
||||||
// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
|
// 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<?> ) {
|
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();
|
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
|
// 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 ) {
|
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() );
|
visitQueryReturnType( sqm.getQueryPart(), expectedResultType, getSessionFactory() );
|
||||||
|
|
||||||
setComment( hql );
|
setComment( hql );
|
||||||
|
@ -336,12 +337,10 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
// Query plan
|
// Query plan
|
||||||
|
|
||||||
private SelectQueryPlan<R> resolveQueryPlan() {
|
private SelectQueryPlan<R> resolveQueryPlan() {
|
||||||
final QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.createInterpretationsKey( this );
|
final QueryInterpretationCache.Key cacheKey = createInterpretationsKey( this );
|
||||||
if ( cacheKey != null ) {
|
if ( cacheKey != null ) {
|
||||||
return getSession().getFactory().getQueryEngine().getInterpretationCache().resolveSelectQueryPlan(
|
return getSession().getFactory().getQueryEngine().getInterpretationCache()
|
||||||
cacheKey,
|
.resolveSelectQueryPlan( cacheKey, this::buildQueryPlan );
|
||||||
this::buildQueryPlan
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return buildQueryPlan();
|
return buildQueryPlan();
|
||||||
|
@ -354,40 +353,29 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
getSession().getFactory()
|
getSession().getFactory()
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( concreteSqmStatements.length > 1 ) {
|
return concreteSqmStatements.length > 1
|
||||||
return buildAggregatedQueryPlan( concreteSqmStatements );
|
? buildAggregatedQueryPlan( concreteSqmStatements )
|
||||||
}
|
: buildConcreteQueryPlan( concreteSqmStatements[0], getQueryOptions() );
|
||||||
else {
|
|
||||||
return buildConcreteQueryPlan( concreteSqmStatements[0], getResultType(), getQueryOptions() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SelectQueryPlan<R> buildAggregatedQueryPlan(SqmSelectStatement<?>[] concreteSqmStatements) {
|
private SelectQueryPlan<R> buildAggregatedQueryPlan(SqmSelectStatement<?>[] concreteSqmStatements) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
final SelectQueryPlan<R>[] aggregatedQueryPlans = new SelectQueryPlan[ concreteSqmStatements.length ];
|
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
|
// 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++ ) {
|
for ( int i = 0, x = concreteSqmStatements.length; i < x; i++ ) {
|
||||||
aggregatedQueryPlans[i] = buildConcreteQueryPlan(
|
aggregatedQueryPlans[i] = buildConcreteQueryPlan( concreteSqmStatements[i], getQueryOptions() );
|
||||||
concreteSqmStatements[i],
|
|
||||||
getResultType(),
|
|
||||||
getQueryOptions()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AggregatedSelectQueryPlanImpl<>( aggregatedQueryPlans );
|
return new AggregatedSelectQueryPlanImpl<>( aggregatedQueryPlans );
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> SelectQueryPlan<T> buildConcreteQueryPlan(
|
private SelectQueryPlan<R> buildConcreteQueryPlan(
|
||||||
SqmSelectStatement<?> concreteSqmStatement,
|
SqmSelectStatement<?> concreteSqmStatement,
|
||||||
Class<T> resultType,
|
|
||||||
QueryOptions queryOptions) {
|
QueryOptions queryOptions) {
|
||||||
return new ConcreteSqmSelectQueryPlan<>(
|
return new ConcreteSqmSelectQueryPlan<>(
|
||||||
concreteSqmStatement,
|
concreteSqmStatement,
|
||||||
getQueryString(),
|
getQueryString(),
|
||||||
getDomainParameterXref(),
|
getDomainParameterXref(),
|
||||||
resultType,
|
expectedResultType,
|
||||||
tupleMetadata,
|
tupleMetadata,
|
||||||
queryOptions
|
queryOptions
|
||||||
);
|
);
|
||||||
|
@ -399,7 +387,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
// InterpretationsKeySource
|
// InterpretationsKeySource
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<R> getResultType() {
|
public Class<?> getResultType() {
|
||||||
return resultType;
|
return resultType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,7 +672,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmSelectionQuery<R> setParameterList(String name, Collection values) {
|
public SqmSelectionQuery<R> setParameterList(String name, @SuppressWarnings("rawtypes") Collection values) {
|
||||||
super.setParameterList( name, values );
|
super.setParameterList( name, values );
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -720,7 +708,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmSelectionQuery<R> setParameterList(int position, Collection values) {
|
public SqmSelectionQuery<R> setParameterList(int position, @SuppressWarnings("rawtypes") Collection values) {
|
||||||
super.setParameterList( position, values );
|
super.setParameterList( position, values );
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -792,7 +780,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SqmSelectionQuery<R> setProperties(Map map) {
|
public SqmSelectionQuery<R> setProperties(@SuppressWarnings("rawtypes") Map map) {
|
||||||
super.setProperties( map );
|
super.setProperties( map );
|
||||||
return this;
|
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 java.util.List;
|
||||||
import jakarta.persistence.TupleElement;
|
import jakarta.persistence.TupleElement;
|
||||||
|
|
||||||
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
|
|
||||||
import org.hibernate.query.JpaTuple;
|
import org.hibernate.query.JpaTuple;
|
||||||
|
|
||||||
|
import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.getDescriptorByPrimitiveType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the JPA Tuple contract
|
* Implementation of the JPA Tuple contract
|
||||||
*
|
*
|
||||||
|
@ -45,7 +46,7 @@ public class TupleImpl implements JpaTuple {
|
||||||
public <X> X get(String alias, Class<X> type) {
|
public <X> X get(String alias, Class<X> type) {
|
||||||
final Object untyped = get( alias );
|
final Object untyped = get( alias );
|
||||||
if ( untyped != null ) {
|
if ( untyped != null ) {
|
||||||
if (!elementTypeMatches(type, untyped)) {
|
if ( !elementTypeMatches( type, untyped ) ) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
String.format(
|
String.format(
|
||||||
"Requested tuple value [alias=%s, value=%s] cannot be assigned to requested type [%s]",
|
"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) {
|
private <X> boolean elementTypeMatches(Class<X> type, Object untyped) {
|
||||||
return type.isInstance(untyped)
|
return type.isInstance( untyped )
|
||||||
|| type.isPrimitive()
|
|| type.isPrimitive() && getDescriptorByPrimitiveType( type ).getWrapperClass().isInstance( untyped );
|
||||||
&& PrimitiveWrapperHelper.getDescriptorByPrimitiveType( type).getWrapperClass().isInstance( untyped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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