HHH 18705 - Hibernate processor creates bad TypedReferenceQuery when @Entity have name attribute (#9064)

- entityType moved into org.hibernate.processor.Context
- result/type(returnType) moved into new utility class org.hibernate.processor.util.SqmTypeUtils
This commit is contained in:
Čedomir Igaly 2024-11-16 16:11:35 +01:00 committed by GitHub
parent d6e1c9b2a8
commit b1ee3d4334
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 124 additions and 78 deletions

View File

@ -17,6 +17,7 @@ import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
@ -34,6 +35,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import static java.lang.Boolean.parseBoolean; import static java.lang.Boolean.parseBoolean;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY; import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY;
import static org.hibernate.processor.validation.ProcessorSessionFactory.findEntityByUnqualifiedName;
/** /**
* @author Max Andersen * @author Max Andersen
@ -505,4 +507,26 @@ public final class Context {
public void addEnumValue(String type, String value) { public void addEnumValue(String type, String value) {
enumTypesByValue.computeIfAbsent( value, s -> new TreeSet<>() ).add( type ); enumTypesByValue.computeIfAbsent( value, s -> new TreeSet<>() ).add( type );
} }
@Nullable
public TypeElement entityType(String entityName) {
final Elements elementUtils = getElementUtils();
final String qualifiedName = qualifiedNameForEntityName(entityName);
if ( qualifiedName != null ) {
return elementUtils.getTypeElement(qualifiedName);
}
TypeElement symbol =
findEntityByUnqualifiedName( entityName,
elementUtils.getModuleElement("") );
if ( symbol != null ) {
return symbol;
}
for ( ModuleElement module : elementUtils.getAllModuleElements() ) {
symbol = findEntityByUnqualifiedName( entityName, module );
if ( symbol != null ) {
return symbol;
}
}
return null;
}
} }

View File

@ -13,11 +13,7 @@ import org.hibernate.processor.model.Metamodel;
import org.hibernate.processor.util.Constants; import org.hibernate.processor.util.Constants;
import org.hibernate.processor.validation.ProcessorSessionFactory; import org.hibernate.processor.validation.ProcessorSessionFactory;
import org.hibernate.processor.validation.Validation; import org.hibernate.processor.validation.Validation;
import org.hibernate.query.criteria.JpaEntityJoin;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
@ -33,6 +29,7 @@ import static org.hibernate.processor.util.Constants.TYPED_QUERY_REFERENCE;
import static org.hibernate.processor.util.TypeUtils.containsAnnotation; import static org.hibernate.processor.util.TypeUtils.containsAnnotation;
import static org.hibernate.processor.util.TypeUtils.getAnnotationMirror; import static org.hibernate.processor.util.TypeUtils.getAnnotationMirror;
import static org.hibernate.processor.util.TypeUtils.getAnnotationValue; import static org.hibernate.processor.util.TypeUtils.getAnnotationValue;
import static org.hibernate.processor.util.SqmTypeUtils.resultType;
public abstract class AnnotationMeta implements Metamodel { public abstract class AnnotationMeta implements Metamodel {
@ -126,7 +123,7 @@ public abstract class AnnotationMeta implements Metamodel {
); );
} }
if ( getAnnotationValue( mirror, "resultClass" ) == null ) { if ( getAnnotationValue( mirror, "resultClass" ) == null ) {
final String resultType = resultType( selectStatement ); final String resultType = resultType( selectStatement, context );
if ( resultType != null ) { if ( resultType != null ) {
putMember( "QUERY_" + name, putMember( "QUERY_" + name,
new TypedMetaAttribute( this, name, "QUERY_", resultType, new TypedMetaAttribute( this, name, "QUERY_", resultType,
@ -139,27 +136,6 @@ public abstract class AnnotationMeta implements Metamodel {
} }
} }
private static @Nullable String resultType(SqmSelectStatement<?> selectStatement) {
final JpaSelection<?> selection = selectStatement.getSelection();
if (selection == null) {
return null;
}
else if (selection instanceof SqmSelectClause from) {
return from.getSelectionItems().size() > 1
? "Object[]"
: from.getSelectionItems().get(0).getJavaTypeName();
}
else if (selection instanceof JpaRoot<?> root) {
return root.getModel().getTypeName();
}
else if (selection instanceof JpaEntityJoin<?, ?> join) {
return join.getModel().getTypeName();
}
else {
return selection.getJavaTypeName();
}
}
private static boolean isQueryMethodName(String name) { private static boolean isQueryMethodName(String name) {
return name.length() >= 2 return name.length() >= 2
&& name.charAt(0) == '#' && name.charAt(0) == '#'

View File

@ -5,24 +5,17 @@
package org.hibernate.processor.annotation; package org.hibernate.processor.annotation;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.processor.Context;
import org.hibernate.processor.model.MetaAttribute; import org.hibernate.processor.model.MetaAttribute;
import org.hibernate.processor.model.Metamodel; import org.hibernate.processor.model.Metamodel;
import org.hibernate.processor.util.Constants; import org.hibernate.processor.util.Constants;
import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import org.hibernate.type.descriptor.java.JavaType;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.util.List;
import java.util.TreeSet; import java.util.TreeSet;
import static org.hibernate.processor.util.StringUtil.nameToFieldName; import static org.hibernate.processor.util.StringUtil.nameToFieldName;
import static org.hibernate.processor.validation.ProcessorSessionFactory.findEntityByUnqualifiedName; import static org.hibernate.processor.util.SqmTypeUtils.resultType;
/** /**
* @author Gavin King * @author Gavin King
@ -96,27 +89,6 @@ class NamedQueryMethod implements MetaAttribute {
return "QUERY_" + nameToFieldName(name); return "QUERY_" + nameToFieldName(name);
} }
private String returnType() {
final JavaType<?> javaType = select.getSelection().getJavaTypeDescriptor();
if ( javaType != null ) {
return javaType.getTypeName();
}
else {
final List<SqmSelectableNode<?>> items =
select.getQuerySpec().getSelectClause().getSelectionItems();
final SqmExpressible<?> expressible;
if ( items.size() == 1 && ( expressible = items.get( 0 ).getExpressible() ) != null ) {
final String typeName = expressible.getTypeName();
final TypeElement entityType = entityType( typeName );
return entityType == null ? typeName : entityType.getQualifiedName().toString();
}
else {
return "Object[]";
}
}
}
void notNull(StringBuilder declaration) { void notNull(StringBuilder declaration) {
if ( addNonnullAnnotation ) { if ( addNonnullAnnotation ) {
declaration declaration
@ -149,7 +121,7 @@ class NamedQueryMethod implements MetaAttribute {
declaration declaration
.append(annotationMeta.importType(Constants.LIST)) .append(annotationMeta.importType(Constants.LIST))
.append('<') .append('<')
.append(annotationMeta.importType(returnType())) .append( annotationMeta.importType( resultType( select, annotationMeta.getContext() ) ) )
.append("> ") .append("> ")
.append(name); .append(name);
if ( reactive ) { if ( reactive ) {
@ -193,28 +165,6 @@ class NamedQueryMethod implements MetaAttribute {
return "unknown".equals(paramType) ? "Object" : annotationMeta.importType(paramType); return "unknown".equals(paramType) ? "Object" : annotationMeta.importType(paramType);
} }
private @Nullable TypeElement entityType(String entityName) {
final Context context = annotationMeta.getContext();
final Elements elementUtils = context.getElementUtils();
final String qualifiedName = context.qualifiedNameForEntityName(entityName);
if ( qualifiedName != null ) {
return elementUtils.getTypeElement(qualifiedName);
}
TypeElement symbol =
findEntityByUnqualifiedName( entityName,
elementUtils.getModuleElement("") );
if ( symbol != null ) {
return symbol;
}
for ( ModuleElement module : elementUtils.getAllModuleElements() ) {
symbol = findEntityByUnqualifiedName( entityName, module );
if ( symbol != null ) {
return symbol;
}
}
return null;
}
@Override @Override
public String getAttributeNameDeclarationString() { public String getAttributeNameDeclarationString() {
throw new UnsupportedOperationException("operation not supported"); throw new UnsupportedOperationException("operation not supported");

View File

@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.processor.util;
import org.hibernate.processor.Context;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
import javax.lang.model.element.TypeElement;
import java.util.List;
public final class SqmTypeUtils {
private SqmTypeUtils() {
}
public static String resultType(SqmSelectStatement<?> selectStatement, Context context) {
final String javaTypeName = selectStatement.getSelection().getJavaTypeName();
if ( javaTypeName != null ) {
return javaTypeName;
}
else {
final List<SqmSelectableNode<?>> items =
selectStatement.getQuerySpec().getSelectClause().getSelectionItems();
final SqmExpressible<?> expressible;
if ( items.size() == 1 && (expressible = items.get( 0 ).getExpressible()) != null ) {
final String typeName = expressible.getTypeName();
final TypeElement entityType = context.entityType( typeName );
return entityType == null ? typeName : entityType.getQualifiedName().toString();
}
else {
return "Object[]";
}
}
}
}

View File

@ -0,0 +1,18 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.processor.test.namedentity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.NamedQuery;
@Entity(name = "Liber")
@NamedQuery(name = "findAllBooks", query = "from Liber")
public class Book {
@Id
private Integer id;
private String name;
}

View File

@ -0,0 +1,40 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.processor.test.namedentity;
import jakarta.persistence.TypedQueryReference;
import org.hibernate.processor.test.util.CompilationTest;
import org.hibernate.processor.test.util.WithClasses;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor;
import static org.hibernate.processor.test.util.TestUtil.assertPresenceOfFieldInMetamodelFor;
import static org.hibernate.processor.test.util.TestUtil.getFieldFromMetamodelFor;
import static org.hibernate.processor.test.util.TestUtil.getMetaModelSourceAsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class NamedEntityTest extends CompilationTest {
@Test
@WithClasses(Book.class)
public void test() {
System.out.println( getMetaModelSourceAsString( Book.class ) );
assertMetamodelClassGeneratedFor( Book.class );
assertPresenceOfFieldInMetamodelFor( Book.class, "QUERY_FIND_ALL_BOOKS" );
final Field field = getFieldFromMetamodelFor( Book.class, "_findAllBooks_" );
assertEquals( TypedQueryReference.class, field.getType() );
final Type genericType = field.getGenericType();
assertTrue( genericType instanceof ParameterizedType );
final ParameterizedType parameterizedType = (ParameterizedType) genericType;
assertEquals( Book.class, parameterizedType.getActualTypeArguments()[0] );
}
}