Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.function.Function;

import jakarta.persistence.EntityGraph;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.CacheMode;
import org.hibernate.EntityNameResolver;
import org.hibernate.Filter;
Expand Down Expand Up @@ -905,22 +906,8 @@ public <R> QueryImplementor<R> createQuery(TypedQueryReference<R> typedQueryRefe
// dynamic native (SQL) query handling

@Override @SuppressWarnings("rawtypes")
public NativeQueryImpl createNativeQuery(String sqlString) {
checkOpen();
pulseTransactionCoordinator();
delayedAfterCompletion();

try {
final NativeQueryImpl query = new NativeQueryImpl<>( sqlString, this );
if ( isEmpty( query.getComment() ) ) {
query.setComment( "dynamic native SQL query" );
}
applyQuerySettingsAndHints( query );
return query;
}
catch (RuntimeException he) {
throw getExceptionConverter().convert( he );
}
public NativeQueryImplementor createNativeQuery(String sqlString) {
return createNativeQuery( sqlString, (Class) null );
}

@Override @SuppressWarnings("rawtypes")
Expand Down Expand Up @@ -953,12 +940,28 @@ protected NamedResultSetMappingMemento getResultSetMappingMemento(String resultS
@Override @SuppressWarnings({"rawtypes", "unchecked"})
//note: we're doing something a bit funny here to work around
// the clashing signatures declared by the supertypes
public NativeQueryImplementor createNativeQuery(String sqlString, Class resultClass) {
final NativeQueryImpl query = createNativeQuery( sqlString );
addResultType( resultClass, query );
return query;
public NativeQueryImplementor createNativeQuery(String sqlString, @Nullable Class resultClass) {
checkOpen();
pulseTransactionCoordinator();
delayedAfterCompletion();

try {
final NativeQueryImpl query = new NativeQueryImpl<>( sqlString, resultClass, this );
if ( isEmpty( query.getComment() ) ) {
query.setComment( "dynamic native SQL query" );
}
applyQuerySettingsAndHints( query );
return query;
}
catch (RuntimeException he) {
throw getExceptionConverter().convert( he );
}
}

/**
* @deprecated Use {@link NativeQueryImpl#NativeQueryImpl(String, Class, SharedSessionContractImplementor)} instead
*/
@Deprecated(forRemoval = true)
protected <T> void addResultType(Class<T> resultClass, NativeQueryImplementor<T> query) {
if ( Tuple.class.equals( resultClass ) ) {
query.setTupleTransformer( NativeQueryTupleTransformer.INSTANCE );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.jpa.spi.NativeQueryConstructorTransformer;
import org.hibernate.jpa.spi.NativeQueryListTransformer;
import org.hibernate.jpa.spi.NativeQueryMapTransformer;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.query.QueryFlushMode;
import org.hibernate.HibernateException;
Expand Down Expand Up @@ -105,17 +109,21 @@
import jakarta.persistence.TypedQuery;
import jakarta.persistence.metamodel.SingularAttribute;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
import org.hibernate.type.spi.TypeConfiguration;

import static java.lang.Character.isWhitespace;
import static java.util.Collections.addAll;
import static org.hibernate.internal.util.ReflectHelper.isClass;
import static org.hibernate.internal.util.StringHelper.unqualify;
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.makeCopy;
import static org.hibernate.jpa.HibernateHints.HINT_NATIVE_LOCK_MODE;
import static org.hibernate.query.results.internal.Builders.resultClassBuilder;
import static org.hibernate.query.results.ResultSetMapping.resolveResultSetMapping;
import static org.hibernate.query.sqm.internal.SqmUtil.isResultTypeAlwaysAllowed;

/**
* @author Steve Ebersole
Expand All @@ -129,6 +137,7 @@ public class NativeQueryImpl<R>
private final List<ParameterOccurrence> parameterOccurrences;
private final QueryParameterBindings parameterBindings;

private final Class<R> resultType;
private final ResultSetMapping resultSetMapping;
private final boolean resultMappingSuppliedToCtor;

Expand Down Expand Up @@ -166,6 +175,7 @@ public NativeQueryImpl(
return false;
}
},
null,
session
);
}
Expand Down Expand Up @@ -218,26 +228,9 @@ public NativeQueryImpl(
return false;
}
},
resultJavaType,
session
);

if ( resultJavaType == Tuple.class ) {
setTupleTransformer( new NativeQueryTupleTransformer() );
}
else if ( resultJavaType != null && !resultJavaType.isArray() ) {
switch ( resultSetMapping.getNumberOfResultBuilders() ) {
case 0:
throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" );
case 1:
final Class<?> actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 ).getJavaType();
if ( actualResultJavaType != null && !resultJavaType.isAssignableFrom( actualResultJavaType ) ) {
throw buildIncompatibleException( resultJavaType, actualResultJavaType );
}
break;
default:
throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" );
}
}
}

/**
Expand All @@ -258,6 +251,7 @@ public NativeQueryImpl(
mappingMemento.resolve( resultSetMapping, querySpaceConsumer, context );
return true;
},
null,
session
);

Expand All @@ -268,6 +262,15 @@ public NativeQueryImpl(
Supplier<ResultSetMapping> resultSetMappingCreator,
ResultSetMappingHandler resultSetMappingHandler,
SharedSessionContractImplementor session) {
this( memento, resultSetMappingCreator, resultSetMappingHandler, null, session );
}

public NativeQueryImpl(
NamedNativeQueryMemento<?> memento,
Supplier<ResultSetMapping> resultSetMappingCreator,
ResultSetMappingHandler resultSetMappingHandler,
@Nullable Class<R> resultType,
SharedSessionContractImplementor session) {
super( session );

this.originalSqlString = memento.getOriginalSqlString();
Expand All @@ -279,13 +282,35 @@ public NativeQueryImpl(
this.parameterMetadata = parameterInterpretation.toParameterMetadata( session );
this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences();
this.parameterBindings = parameterMetadata.createBindings( session.getFactory() );
this.resultType = resultType;
this.querySpaces = new HashSet<>();

this.resultSetMapping = resultSetMappingCreator.get();

this.resultMappingSuppliedToCtor =
resultSetMappingHandler.resolveResultSetMapping( resultSetMapping, querySpaces::add, this );

if ( resultType != null ) {
if ( !isResultTypeAlwaysAllowed( resultType ) ) {
switch ( resultSetMapping.getNumberOfResultBuilders() ) {
case 0:
throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" );
case 1:
final Class<?> actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 )
.getJavaType();
if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) {
throw buildIncompatibleException( resultType, actualResultJavaType );
}
break;
default:
throw new IllegalArgumentException(
"Cannot create TypedQuery for query with more than one return" );
}
}
else {
setTupleTransformerForResultType( resultType );
}
}
applyOptions( memento );
}

Expand All @@ -301,6 +326,7 @@ public NativeQueryImpl(
this.parameterMetadata = parameterInterpretation.toParameterMetadata( session );
this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences();
this.parameterBindings = parameterMetadata.createBindings( session.getFactory() );
this.resultType = null;
this.querySpaces = new HashSet<>();

this.resultSetMapping = buildResultSetMapping( resultSetMappingMemento.getName(), false, session );
Expand All @@ -310,6 +336,10 @@ public NativeQueryImpl(
}

public NativeQueryImpl(String sqlString, SharedSessionContractImplementor session) {
this( sqlString, null, session );
}

public NativeQueryImpl(String sqlString, @Nullable Class<R> resultType, SharedSessionContractImplementor session) {
super( session );

this.querySpaces = new HashSet<>();
Expand All @@ -320,11 +350,46 @@ public NativeQueryImpl(String sqlString, SharedSessionContractImplementor sessio
this.parameterMetadata = parameterInterpretation.toParameterMetadata( session );
this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences();
this.parameterBindings = parameterMetadata.createBindings( session.getFactory() );
this.resultType = resultType;
if ( resultType != null ) {
setTupleTransformerForResultType( resultType );
}

this.resultSetMapping = resolveResultSetMapping( sqlString, true, session.getFactory() );
this.resultMappingSuppliedToCtor = false;
}

protected <T> void setTupleTransformerForResultType(Class<T> resultClass) {
final TupleTransformer<?> tupleTransformer = determineTupleTransformerForResultType( resultClass );
if ( tupleTransformer != null ) {
setTupleTransformer( tupleTransformer );
}
}

protected @Nullable TupleTransformer<?> determineTupleTransformerForResultType(Class<?> resultClass) {
if ( Tuple.class.equals( resultClass ) ) {
return NativeQueryTupleTransformer.INSTANCE;
}
else if ( Map.class.equals( resultClass ) ) {
return NativeQueryMapTransformer.INSTANCE;
}
else if ( List.class.equals( resultClass ) ) {
return NativeQueryListTransformer.INSTANCE;
}
else if ( resultClass != Object.class && resultClass != Object[].class ) {
if ( isClass( resultClass ) && !hasJavaTypeDescriptor( resultClass ) ) {
// not a basic type
return new NativeQueryConstructorTransformer<>( resultClass );
}
}
return null;
}

private <T> boolean hasJavaTypeDescriptor(Class<T> resultClass) {
final JavaType<Object> descriptor = getTypeConfiguration().getJavaTypeRegistry().findDescriptor( resultClass );
return descriptor != null && descriptor.getClass() != UnknownBasicJavaType.class;
}

@FunctionalInterface
private interface ResultSetMappingHandler {
boolean resolveResultSetMapping(
Expand Down Expand Up @@ -436,11 +501,16 @@ public QueryParameterBindings getParameterBindings() {
return getQueryParameterBindings();
}

@Override
public Class<R> getResultType() {
return resultType;
}

@Override
public NamedNativeQueryMemento<?> toMemento(String name) {
return new NamedNativeQueryMementoImpl<>(
name,
extractResultClass( resultSetMapping ),
resultType != null ? resultType : extractResultClass( resultSetMapping ),
sqlString,
originalSqlString,
resultSetMapping.getMappingIdentifier(),
Expand All @@ -459,14 +529,14 @@ public NamedNativeQueryMemento<?> toMemento(String name) {
);
}

private Class<?> extractResultClass(ResultSetMapping resultSetMapping) {
private Class<R> extractResultClass(ResultSetMapping resultSetMapping) {
final List<ResultBuilder> resultBuilders = resultSetMapping.getResultBuilders();
if ( resultBuilders.size() == 1 ) {
final ResultBuilder resultBuilder = resultBuilders.get( 0 );
if ( resultBuilder instanceof ImplicitResultClassBuilder
|| resultBuilder instanceof ImplicitModelPartResultBuilderEntity
|| resultBuilder instanceof DynamicResultBuilderEntityCalculated ) {
return resultBuilder.getJavaType();
return (Class<R>) resultBuilder.getJavaType();
}
}
return null;
Expand Down Expand Up @@ -618,13 +688,29 @@ public KeyedResultList<R> getKeyedResultList(KeyedPage<R> page) {
}

protected SelectQueryPlan<R> resolveSelectQueryPlan() {
final ResultSetMapping mapping;
if ( resultType != null && resultSetMapping.isDynamic() && resultSetMapping.getNumberOfResultBuilders() == 0 ) {
mapping = ResultSetMapping.resolveResultSetMapping( originalSqlString, true, getSessionFactory() );

if ( getSessionFactory().getMappingMetamodel().isEntityClass( resultType ) ) {
mapping.addResultBuilder(
Builders.entityCalculated( unqualify( resultType.getName() ), resultType.getName(),
LockMode.READ, getSessionFactory() ) );
}
else if ( !isResultTypeAlwaysAllowed( resultType )
&& (!isClass( resultType ) || hasJavaTypeDescriptor( resultType )) ) {
mapping.addResultBuilder( Builders.resultClassBuilder( resultType, getSessionFactory() ) );
}
}
else {
mapping = resultSetMapping;
}
return isCacheableQuery()
? getInterpretationCache()
.resolveSelectQueryPlan( selectInterpretationsKey(), this::createQueryPlan )
: createQueryPlan();
? getInterpretationCache().resolveSelectQueryPlan( selectInterpretationsKey( mapping ), () -> createQueryPlan( mapping ) )
: createQueryPlan( mapping );
}

private NativeSelectQueryPlan<R> createQueryPlan() {
private NativeSelectQueryPlan<R> createQueryPlan(ResultSetMapping resultSetMapping) {
final NativeSelectQueryDefinition<R> queryDefinition = new NativeSelectQueryDefinition<>() {
final String sqlString = expandParameterLists();

Expand Down Expand Up @@ -834,7 +920,7 @@ public static int determineBindValueMaxCount(boolean paddingEnabled, int inExprL
return bindValueMaxCount;
}

private SelectInterpretationsKey selectInterpretationsKey() {
private SelectInterpretationsKey selectInterpretationsKey(ResultSetMapping resultSetMapping) {
return new SelectInterpretationsKey(
getQueryString(),
resultSetMapping,
Expand Down
Loading
Loading