HHH-16077 - Added named native queries cannot specify result-class

This commit is contained in:
Steve Ebersole 2023-01-23 20:26:49 -06:00
parent 4a37bf8017
commit e7b2f9b121
7 changed files with 58 additions and 18 deletions

View File

@ -858,7 +858,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
query.addEntity( "alias1", resultClass.getName(), LockMode.READ ); query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
} }
else if ( resultClass != Object.class && resultClass != Object[].class ) { else if ( resultClass != Object.class && resultClass != Object[].class ) {
query.addScalar( 1, resultClass ); query.addResultTypeClass( resultClass );
} }
return query; return query;
} }

View File

@ -246,8 +246,13 @@ public class Builders {
public static ResultBuilder resultClassBuilder( public static ResultBuilder resultClassBuilder(
Class<?> resultMappingClass, Class<?> resultMappingClass,
ResultSetMappingResolutionContext resolutionContext) { ResultSetMappingResolutionContext resolutionContext) {
final MappingMetamodelImplementor mappingMetamodel = resolutionContext return resultClassBuilder( resultMappingClass, resolutionContext.getSessionFactory() );
.getSessionFactory() }
public static ResultBuilder resultClassBuilder(
Class<?> resultMappingClass,
SessionFactoryImplementor sessionFactory) {
final MappingMetamodelImplementor mappingMetamodel = sessionFactory
.getRuntimeMetamodels() .getRuntimeMetamodels()
.getMappingMetamodel(); .getMappingMetamodel();
final EntityMappingType entityMappingType = mappingMetamodel.findEntityDescriptor( resultMappingClass ); final EntityMappingType entityMappingType = mappingMetamodel.findEntityDescriptor( resultMappingClass );

View File

@ -91,6 +91,9 @@ public class ResultSetMappingImpl implements ResultSetMapping {
} }
public List<ResultBuilder> getResultBuilders() { public List<ResultBuilder> getResultBuilders() {
if ( resultBuilders == null ) {
return Collections.emptyList();
}
return Collections.unmodifiableList( resultBuilders ); return Collections.unmodifiableList( resultBuilders );
} }

View File

@ -12,6 +12,7 @@ import java.util.Set;
import org.hibernate.CacheMode; import org.hibernate.CacheMode;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.query.named.AbstractNamedQueryMemento; import org.hibernate.query.named.AbstractNamedQueryMemento;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sql.spi.NamedNativeQueryMemento; import org.hibernate.query.sql.spi.NamedNativeQueryMemento;
@ -127,7 +128,7 @@ public class NamedNativeQueryMementoImpl extends AbstractNamedQueryMemento imple
originalSqlString, originalSqlString,
resultSetMappingName, resultSetMappingName,
resultSetMappingClass, resultSetMappingClass,
querySpaces, CollectionHelper.makeCopy( querySpaces ),
getCacheable(), getCacheable(),
getCacheRegion(), getCacheRegion(),
getCacheMode(), getCacheMode(),

View File

@ -61,6 +61,8 @@ import org.hibernate.query.results.ResultSetMappingImpl;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.query.results.dynamic.DynamicResultBuilderEntityStandard; import org.hibernate.query.results.dynamic.DynamicResultBuilderEntityStandard;
import org.hibernate.query.results.dynamic.DynamicResultBuilderInstantiation; import org.hibernate.query.results.dynamic.DynamicResultBuilderInstantiation;
import org.hibernate.query.results.implicit.ImplicitModelPartResultBuilderEntity;
import org.hibernate.query.results.implicit.ImplicitResultClassBuilder;
import org.hibernate.query.spi.AbstractQuery; import org.hibernate.query.spi.AbstractQuery;
import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.MutableQueryOptions; import org.hibernate.query.spi.MutableQueryOptions;
@ -246,12 +248,6 @@ public class NativeQueryImpl<R>
return true; return true;
} }
if ( resultJavaType != null && resultJavaType != Tuple.class && !resultJavaType.isArray() ) {
// todo : allow the expected Java type imply a builder to use
// resultSetMapping.addResultBuilder( resultClassBuilder( resultJavaType, context ) );
// return true;
}
return false; return false;
}, },
session session
@ -260,19 +256,21 @@ public class NativeQueryImpl<R>
if ( resultJavaType == Tuple.class ) { if ( resultJavaType == Tuple.class ) {
setTupleTransformer( new NativeQueryTupleTransformer() ); setTupleTransformer( new NativeQueryTupleTransformer() );
} }
else if ( resultJavaType != null && resultJavaType != Object[].class ) { else if ( resultJavaType != null && !resultJavaType.isArray() ) {
switch ( resultSetMapping.getNumberOfResultBuilders() ) { switch ( resultSetMapping.getNumberOfResultBuilders() ) {
case 0: case 0: {
throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" ); throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" );
case 1: }
// would be nice to support types that are "wrappable", as in `JavaType#wrap` case 1: {
final Class<?> actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 ).getJavaType(); final Class<?> actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 ).getJavaType();
if ( actualResultJavaType != null && !resultJavaType.isAssignableFrom( actualResultJavaType ) ) { if ( actualResultJavaType != null && !resultJavaType.isAssignableFrom( actualResultJavaType ) ) {
throw buildIncompatibleException( resultJavaType, actualResultJavaType ); throw buildIncompatibleException( resultJavaType, actualResultJavaType );
} }
break; break;
default: }
default: {
throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" ); throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" );
}
} }
} }
} }
@ -449,7 +447,7 @@ public class NativeQueryImpl<R>
sqlString, sqlString,
originalSqlString, originalSqlString,
resultSetMapping.getMappingIdentifier(), resultSetMapping.getMappingIdentifier(),
null, extractResultClass( resultSetMapping ),
querySpaces, querySpaces,
isCacheable(), isCacheable(),
getCacheRegion(), getCacheRegion(),
@ -465,6 +463,22 @@ public class NativeQueryImpl<R>
); );
} }
private Class<?> extractResultClass(ResultSetMappingImpl resultSetMapping) {
final List<ResultBuilder> resultBuilders = resultSetMapping.getResultBuilders();
if ( resultBuilders.size() == 1 ) {
final ResultBuilder resultBuilder = resultBuilders.get( 0 );
if ( resultBuilder instanceof ImplicitResultClassBuilder ) {
final ImplicitResultClassBuilder resultTypeBuilder = (ImplicitResultClassBuilder) resultBuilder;
return resultTypeBuilder.getJavaType();
}
else if ( resultBuilder instanceof ImplicitModelPartResultBuilderEntity ) {
final ImplicitModelPartResultBuilderEntity resultTypeBuilder = (ImplicitModelPartResultBuilderEntity) resultBuilder;
return resultTypeBuilder.getJavaType();
}
}
return null;
}
@Override @Override
public LockModeType getLockMode() { public LockModeType getLockMode() {
// the JPA spec requires IllegalStateException here, even // the JPA spec requires IllegalStateException here, even
@ -841,6 +855,11 @@ public class NativeQueryImpl<R>
: null; : null;
} }
public void addResultTypeClass(Class<?> resultClass) {
assert CollectionHelper.isEmpty( resultSetMapping.getResultBuilders() );
registerBuilder( Builders.resultClassBuilder( resultClass, getSessionFactory() ) );
}
@Override @Override
public NativeQueryImplementor<R> addScalar(String columnAlias) { public NativeQueryImplementor<R> addScalar(String columnAlias) {
return registerBuilder( Builders.scalar( columnAlias ) ); return registerBuilder( Builders.scalar( columnAlias ) );

View File

@ -26,6 +26,7 @@ import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQueries; import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery; import jakarta.persistence.NamedQuery;
import jakarta.persistence.Query; import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;
import static org.hibernate.jpa.HibernateHints.HINT_NATIVE_LOCK_MODE; import static org.hibernate.jpa.HibernateHints.HINT_NATIVE_LOCK_MODE;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@ -195,6 +196,19 @@ public class NamedQueryTest extends BaseEntityManagerFunctionalTestCase {
} ); } );
} }
@Test
@TestForIssue(jiraKey = "HHH-11413")
public void testNamedQueryAddedFromTypedNativeQuery() {
doInJPA( this::entityManagerFactory, entityManager -> {
final Query query = entityManager.createNativeQuery( "select g.title from Game g where title = ?", String.class );
entityManagerFactory().addNamedQuery( "the-query", query );
final TypedQuery<String> namedQuery = entityManager.createNamedQuery( "the-query", String.class );
namedQuery.setParameter( 1, "abc" );
namedQuery.getResultList();
} );
}
@Test @Test
@TestForIssue(jiraKey = "HHH-14816") @TestForIssue(jiraKey = "HHH-14816")
public void testQueryHintLockMode() { public void testQueryHintLockMode() {

View File

@ -13,7 +13,6 @@ import org.hibernate.query.Query;
import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactory;
@ -67,7 +66,6 @@ public class NativeQueryAsNamedTests {
* Seems like this should work, but currently does not * Seems like this should work, but currently does not
*/ */
@Test @Test
@FailureExpected( reason = "Session#createNamedQuery for a native-query does not like passing the result-class" )
public void testResultClass(SessionFactoryScope scope) { public void testResultClass(SessionFactoryScope scope) {
scope.inTransaction( (session) -> { scope.inTransaction( (session) -> {
final NativeQuery<String> nativeQuery = session.createNativeQuery( THE_SELECT, String.class ); final NativeQuery<String> nativeQuery = session.createNativeQuery( THE_SELECT, String.class );