better handling of instantiation for @HQL methods

This commit is contained in:
Gavin King 2024-02-12 18:43:12 +01:00
parent d2ca21603b
commit 97099c0280
4 changed files with 68 additions and 12 deletions

View File

@ -318,6 +318,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
}
private final Class<R> expectedResultType;
private final String expectedResultTypeName;
private final String expectedResultTypeShortName;
private final String expectedResultEntity;
private final SqmCreationOptions creationOptions;
private final SqmCreationContext creationContext;
@ -347,24 +349,53 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
SqmCreationOptions creationOptions,
SqmCreationContext creationContext,
String query) {
this( expectedResultType, null, creationOptions, creationContext, query );
this( expectedResultType,
expectedResultType == null ? null : expectedResultType.getTypeName(),
expectedResultType == null ? null : expectedResultType.getSimpleName(),
null, creationOptions, creationContext, query );
}
public SemanticQueryBuilder(
String expectedResultTypeName,
String expectedResultTypeShortName,
String expectedResultEntity,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext,
String query) {
this( null, expectedResultEntity, creationOptions, creationContext, query );
this( null,
expectedResultTypeName,
expectedResultTypeShortName,
expectedResultEntity,
creationOptions, creationContext,
query );
}
public SemanticQueryBuilder(
String expectedResultTypeName,
String expectedResultTypeShortName,
Class<R> expectedResultType,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext,
String query) {
this( expectedResultType,
expectedResultTypeName,
expectedResultTypeShortName,
null,
creationOptions, creationContext,
query );
}
private SemanticQueryBuilder(
Class<R> expectedResultType,
String expectedResultTypeName,
String expectedResultTypeShortName,
String expectedResultEntity,
SqmCreationOptions creationOptions,
SqmCreationContext creationContext,
String query) {
this.expectedResultType = expectedResultType;
this.expectedResultTypeName = expectedResultTypeName;
this.expectedResultTypeShortName = expectedResultTypeShortName;
this.expectedResultEntity = expectedResultEntity;
this.creationOptions = creationOptions;
this.creationContext = creationContext;
@ -1272,7 +1303,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final EntityDomainType<R> entityDescriptor = jpaMetamodel.findEntityType( expectedResultType );
if ( entityDescriptor == null ) {
throw new SemanticException( "Query has no 'from' clause, and the result type '"
+ expectedResultType.getSimpleName() + "' is not an entity type", query );
+ expectedResultTypeShortName + "' is not an entity type", query );
}
return entityDescriptor;
}
@ -1427,8 +1458,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final ParseTree instantiationTarget = ctx.instantiationTarget().getChild( 0 );
if ( instantiationTarget instanceof HqlParser.SimplePathContext ) {
String className = instantiationTarget.getText();
if ( expectedResultType!=null && expectedResultType.getSimpleName().equals( className ) ) {
className = expectedResultType.getName();
if ( expectedResultTypeName != null && expectedResultTypeShortName.equals( className ) ) {
className = expectedResultTypeName;
}
try {
dynamicInstantiation = SqmDynamicInstantiation.forClassInstantiation(

View File

@ -27,6 +27,7 @@ import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -1028,6 +1029,15 @@ public class AnnotationMetaEntity extends AnnotationMeta {
final List<String> paramNames = parameterNames( method );
final List<String> paramTypes = parameterTypes( method );
final String[] sessionType = sessionTypeFromParameters( paramNames, paramTypes );
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
if ( !((DeclaredType) returnType).getTypeArguments().isEmpty() ) {
context.message( method, mirror, value,
"query result type may not be a generic type"
+ " (change '" + returnType +
"' to '" + context.getTypeUtils().erasure( returnType ) + "')",
Diagnostic.Kind.ERROR );
}
}
final QueryMethod attribute =
new QueryMethod(
this,
@ -1150,7 +1160,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
try {
final Class<?> javaResultType = selection.getJavaType();
final TypeElement typeElement = context.getTypeElementForFullyQualifiedName( javaResultType.getName() );
returnTypeCorrect = context.getTypeUtils().isAssignable( returnType, typeElement.asType() );
final Types types = context.getTypeUtils();
returnTypeCorrect = types.isAssignable( returnType, types.erasure( typeElement.asType() ) );
}
catch (Exception e) {
//ignore

View File

@ -104,11 +104,15 @@ public class Validation {
if ( returnType != null && returnType.getKind() == TypeKind.DECLARED ) {
final DeclaredType declaredType = (DeclaredType) returnType;
final TypeElement typeElement = (TypeElement) declaredType.asElement();
if ( isEntity( typeElement ) ) {
return new SemanticQueryBuilder<>( getEntityName( typeElement ), () -> false, factory, hql );
}
final String typeName = typeElement.getQualifiedName().toString();
final String shortName = typeElement.getSimpleName().toString();
return isEntity( typeElement )
? new SemanticQueryBuilder<>( typeName, shortName, getEntityName(typeElement), () -> false, factory, hql )
: new SemanticQueryBuilder<>( typeName, shortName, Object[].class, () -> false, factory, hql );
}
else {
return new SemanticQueryBuilder<>( Object[].class, () -> false, factory, hql );
}
return new SemanticQueryBuilder<>( Object[].class, () -> false, factory, hql );
}
private static HqlParser.StatementContext parseAndCheckSyntax(String hql, Handler handler) {

View File

@ -9,6 +9,7 @@ import org.hibernate.query.Page;
import org.hibernate.query.SelectionQuery;
import java.util.List;
import java.util.Map;
public interface Dao {
@ -48,10 +49,19 @@ public interface Dao {
@HQL("from Book book join fetch book.publisher where book.title like :titlePattern")
List<Book> booksWithPublisherByTitle(String titlePattern, Page page, Order<? super Book> order);
@HQL("select new org.hibernate.jpamodelgen.test.hqlsql.Dto(title, pages) from Book")
@HQL("select title, pages from Book")
List<Dto> dtoQuery();
@HQL("select title, pages from Book")
@HQL("select new org.hibernate.jpamodelgen.test.hqlsql.Dto(title, pages) from Book")
List<Dto> dtoQuery1();
@HQL("select new Dto(title, pages) from Book")
List<Dto> dtoQuery2();
@HQL("select new map(title as title, pages as pages) from Book")
List<Map> dtoQuery3();
@HQL("select new list(title, pages) from Book")
List<List> dtoQuery4();
}