diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java index 645aad8475..c2076f02ff 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java @@ -17,6 +17,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.ModuleElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; 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.util.Collections.emptyList; import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY; +import static org.hibernate.processor.validation.ProcessorSessionFactory.findEntityByUnqualifiedName; /** * @author Max Andersen @@ -505,4 +507,26 @@ public final class Context { public void addEnumValue(String type, String value) { 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; + } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java index beaacc8f78..0f77b78509 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMeta.java @@ -13,11 +13,7 @@ import org.hibernate.processor.model.Metamodel; import org.hibernate.processor.util.Constants; import org.hibernate.processor.validation.ProcessorSessionFactory; 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.select.SqmSelectClause; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; 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.getAnnotationMirror; import static org.hibernate.processor.util.TypeUtils.getAnnotationValue; +import static org.hibernate.processor.util.SqmTypeUtils.resultType; public abstract class AnnotationMeta implements Metamodel { @@ -126,7 +123,7 @@ public abstract class AnnotationMeta implements Metamodel { ); } if ( getAnnotationValue( mirror, "resultClass" ) == null ) { - final String resultType = resultType( selectStatement ); + final String resultType = resultType( selectStatement, context ); if ( resultType != null ) { putMember( "QUERY_" + name, 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) { return name.length() >= 2 && name.charAt(0) == '#' diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java index 10f3bb7b48..7f505a9f2e 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NamedQueryMethod.java @@ -5,24 +5,17 @@ package org.hibernate.processor.annotation; import org.checkerframework.checker.nullness.qual.Nullable; -import org.hibernate.processor.Context; import org.hibernate.processor.model.MetaAttribute; import org.hibernate.processor.model.Metamodel; import org.hibernate.processor.util.Constants; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.expression.SqmParameter; 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 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 @@ -96,27 +89,6 @@ class NamedQueryMethod implements MetaAttribute { return "QUERY_" + nameToFieldName(name); } - private String returnType() { - final JavaType javaType = select.getSelection().getJavaTypeDescriptor(); - if ( javaType != null ) { - return javaType.getTypeName(); - } - else { - final List> 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) { if ( addNonnullAnnotation ) { declaration @@ -149,7 +121,7 @@ class NamedQueryMethod implements MetaAttribute { declaration .append(annotationMeta.importType(Constants.LIST)) .append('<') - .append(annotationMeta.importType(returnType())) + .append( annotationMeta.importType( resultType( select, annotationMeta.getContext() ) ) ) .append("> ") .append(name); if ( reactive ) { @@ -193,28 +165,6 @@ class NamedQueryMethod implements MetaAttribute { 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 public String getAttributeNameDeclarationString() { throw new UnsupportedOperationException("operation not supported"); diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/SqmTypeUtils.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/SqmTypeUtils.java new file mode 100644 index 0000000000..6d2a81c32f --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/SqmTypeUtils.java @@ -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> 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[]"; + } + } + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedentity/Book.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedentity/Book.java new file mode 100644 index 0000000000..13c9d8e82c --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedentity/Book.java @@ -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; +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedentity/NamedEntityTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedentity/NamedEntityTest.java new file mode 100644 index 0000000000..27abe9bff9 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/namedentity/NamedEntityTest.java @@ -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] ); + } +}