From 985887964d475b15cc8ba25e9a284fe37138240b Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 22 Mar 2024 01:08:38 +0100 Subject: [PATCH] HHH-17873 handle generic supertypes of repositories --- .../test/data/superdao/generic/Book.java | 23 +++++ .../test/data/superdao/generic/Repo.java | 10 ++ .../test/data/superdao/generic/SuperRepo.java | 40 ++++++++ .../data/superdao/generic/SuperRepoTest.java | 29 ++++++ .../annotation/AnnotationMetaEntity.java | 92 ++++++++++--------- .../processor/test/superdao/SuperDaoTest.java | 4 +- .../processor/test/superdao/generic/Book.java | 23 +++++ .../processor/test/superdao/generic/Dao.java | 8 ++ .../test/superdao/generic/SuperDao.java | 23 +++++ .../test/superdao/generic/SuperDaoTest.java | 29 ++++++ 10 files changed, 235 insertions(+), 46 deletions(-) create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Book.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Repo.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepo.java create mode 100644 tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepoTest.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Book.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Dao.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDao.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDaoTest.java diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Book.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Book.java new file mode 100644 index 0000000000..d4fe275c97 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Book.java @@ -0,0 +1,23 @@ +package org.hibernate.processor.test.data.superdao.generic; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import org.hibernate.annotations.NaturalId; +import org.hibernate.processor.test.hqlsql.Publisher; + +import java.math.BigDecimal; +import java.time.LocalDate; + +@Entity +public class Book { + @Id String isbn; + @NaturalId String title; + String text; + @NaturalId String authorName; + @ManyToOne + Publisher publisher; + BigDecimal price; + int pages; + LocalDate publicationDate; +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Repo.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Repo.java new file mode 100644 index 0000000000..a034c373b7 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/Repo.java @@ -0,0 +1,10 @@ +package org.hibernate.processor.test.data.superdao.generic; + +import jakarta.data.repository.Repository; +import org.hibernate.annotations.processing.Find; + +@Repository +public interface Repo extends SuperRepo { + @Find + Book get(String isbn); +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepo.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepo.java new file mode 100644 index 0000000000..df6e251957 --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepo.java @@ -0,0 +1,40 @@ +package org.hibernate.processor.test.data.superdao.generic; + +import jakarta.data.page.Page; +import jakarta.data.page.PageRequest; +import jakarta.data.repository.By; +import jakarta.data.repository.Delete; +import jakarta.data.repository.Find; +import jakarta.data.repository.Save; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + + +public interface SuperRepo { + + @Save + S save(S entity); + + @Save + List saveAll(List entities); + + @Find + Optional findById(@By("#id") K id); + + @Find + Stream findAll(); + + @Find + Page findAll(PageRequest pageRequest); + + @Delete + void deleteById(@By("#id") K id); + + @Delete + void delete(T entity); + + @Delete + void deleteAll(List entities); +} diff --git a/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepoTest.java b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepoTest.java new file mode 100644 index 0000000000..2d76c2676b --- /dev/null +++ b/tooling/metamodel-generator/src/jakartaData/java/org/hibernate/processor/test/data/superdao/generic/SuperRepoTest.java @@ -0,0 +1,29 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.processor.test.data.superdao.generic; + +import org.hibernate.processor.test.util.CompilationTest; +import org.hibernate.processor.test.util.TestUtil; +import org.hibernate.processor.test.util.WithClasses; +import org.junit.Test; + +import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor; + +/** + * @author Gavin King + */ +public class SuperRepoTest extends CompilationTest { + @Test + @WithClasses({ Book.class, SuperRepo.class, Repo.class }) + public void testQueryMethod() { +// System.out.println( TestUtil.getMetaModelSourceAsString( SuperRepo.class ) ); + System.out.println( TestUtil.getMetaModelSourceAsString( Repo.class ) ); + assertMetamodelClassGeneratedFor( Book.class ); +// assertMetamodelClassGeneratedFor( SuperRepo.class ); + assertMetamodelClassGeneratedFor( Repo.class ); + } +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java index eacb0d1a3c..2d059be050 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java @@ -365,7 +365,8 @@ public class AnnotationMetaEntity extends AnnotationMeta { if ( isGetterOrSetter( method ) ) { gettersAndSettersOfClass.add( method ); } - else if ( containsAnnotation( method, HQL, SQL, FIND ) ) { + else if ( element.getTypeParameters().isEmpty() + && containsAnnotation( method, HQL, SQL, FIND ) ) { queryMethods.add( method ); } } @@ -427,32 +428,34 @@ public class AnnotationMetaEntity extends AnnotationMeta { } private void setupSession() { - jakartaDataRepository = hasAnnotation( element, JD_REPOSITORY ); - final ExecutableElement getter = findSessionGetter( element ); - if ( getter != null ) { - // Never make a DAO for Panache subtypes - if ( !isPanacheType( element ) ) { + if ( element.getTypeParameters().isEmpty() ) { + jakartaDataRepository = hasAnnotation( element, JD_REPOSITORY ); + final ExecutableElement getter = findSessionGetter( element ); + if ( getter != null ) { + // Never make a DAO for Panache subtypes + if ( !isPanacheType( element ) ) { + repository = true; + sessionType = addDaoConstructor( getter ); + } + else { + // For Panache subtypes, we look at the session type, but no DAO, we want static methods + sessionType = getter.getReturnType().toString(); + } + } + else if ( element.getKind() == ElementKind.INTERFACE + && ( context.usesQuarkusOrm() || context.usesQuarkusReactive() ) ) { + // if we don't have a getter, but we're in Quarkus, we know how to find the default sessions repository = true; - sessionType = addDaoConstructor( getter ); + sessionType = setupQuarkusDaoConstructor(); } - else { - // For Panache subtypes, we look at the session type, but no DAO, we want static methods - sessionType = getter.getReturnType().toString(); + if ( !repository && jakartaDataRepository ) { + repository = true; + sessionType = HIB_STATELESS_SESSION; + addDaoConstructor( null ); + } + if ( jakartaDataRepository && !quarkusInjection ) { + addDefaultConstructor(); } - } - else if ( element.getKind() == ElementKind.INTERFACE - && ( context.usesQuarkusOrm() || context.usesQuarkusReactive() ) ) { - // if we don't have a getter, but we're in Quarkus, we know how to find the default sessions - repository = true; - sessionType = setupQuarkusDaoConstructor(); - } - if ( !repository && jakartaDataRepository ) { - repository = true; - sessionType = HIB_STATELESS_SESSION; - addDaoConstructor( null ); - } - if ( jakartaDataRepository && !quarkusInjection ) { - addDefaultConstructor(); } } @@ -720,7 +723,10 @@ public class AnnotationMetaEntity extends AnnotationMeta { } private void addQueryMethod(ExecutableElement method) { - TypeMirror returnType = method.getReturnType(); + final ExecutableType methodType = + (ExecutableType) context.getTypeUtils() + .asMemberOf((DeclaredType) element.asType(), method); + final TypeMirror returnType = methodType.getReturnType(); final TypeKind kind = returnType.getKind(); if ( kind == TypeKind.VOID || kind == TypeKind.ARRAY || kind.isPrimitive() ) { addQueryMethod( method, returnType, null ); @@ -872,7 +878,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { final String operation = lifecycleOperation( method ); final VariableElement parameter = method.getParameters().get(0); final TypeMirror declaredParameterType = parameter.asType(); - final TypeMirror parameterType = parameterType( declaredParameterType ); + final TypeMirror parameterType = parameterType( parameter ); final DeclaredType declaredType = entityType( parameterType ); if ( declaredType == null ) { context.message( parameter, @@ -915,6 +921,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { } private @Nullable DeclaredType entityType(TypeMirror parameterType) { + final Types types = context.getTypeUtils(); switch ( parameterType.getKind() ) { case TYPEVAR: final TypeVariable typeVariable = (TypeVariable) parameterType; @@ -922,12 +929,11 @@ public class AnnotationMetaEntity extends AnnotationMeta { //INTENTIONAL FALL THROUGH case DECLARED: final DeclaredType declaredType = (DeclaredType) parameterType; - final Types types = context.getTypeUtils(); final Elements elements = context.getElementUtils(); if ( types.isAssignable( declaredType, types.erasure( elements.getTypeElement(ITERABLE).asType() ) ) && !declaredType.getTypeArguments().isEmpty() ) { - final TypeMirror elementType = parameterType( declaredType.getTypeArguments().get(0) ); + final TypeMirror elementType = types.erasure( declaredType.getTypeArguments().get(0) ); return elementType.getKind() == TypeKind.DECLARED ? (DeclaredType) elementType : null; } else { @@ -935,7 +941,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { } case ARRAY: final ArrayType arrayType = (ArrayType) parameterType; - final TypeMirror componentType = parameterType( arrayType.getComponentType() ); + final TypeMirror componentType = types.erasure( arrayType.getComponentType() ); return componentType.getKind() == TypeKind.DECLARED ? (DeclaredType) componentType : null; default: return null; @@ -1057,7 +1063,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { else { multivalued.add( false ); final Types types = context.getTypeUtils(); - final TypeMirror parameterType = parameter.asType(); + final TypeMirror parameterType = parameterType( parameter ); final String type = parameterType.toString(); boolean pageRequest = type.startsWith(JD_PAGE_REQUEST); if ( isOrderParam(type) || pageRequest ) { @@ -1428,15 +1434,6 @@ public class AnnotationMetaEntity extends AnnotationMeta { return null; } -// final String memberType = attributeType.toString(); -// final String paramType = parameterType.toString(); -// if ( !isLegalAssignment( paramType, memberType ) ) { -// context.message( param, -// "matching field has type '" + memberType -// + "' in entity class '" + entityType + "'", -// Diagnostic.Kind.ERROR ); -// } - if ( checkParameterType( entityType, param, memberType( member ) ) ) { return FieldType.MULTIVALUED; } @@ -1485,7 +1482,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { private boolean checkParameterType(TypeElement entityType, VariableElement param, TypeMirror attributeType) { final Types types = context.getTypeUtils(); if ( entityType.getKind() == CLASS ) { // do no checks if the entity type is a type variable - TypeMirror parameterType = param.asType(); + TypeMirror parameterType = parameterType( param ); if ( types.isSameType( parameterType, attributeType ) ) { return false; } @@ -2088,12 +2085,19 @@ public class AnnotationMetaEntity extends AnnotationMeta { private List parameterTypes(ExecutableElement method) { return method.getParameters().stream() - .map(param -> parameterType(param.asType()).toString()) + .map(param -> parameterType(param).toString()) .collect(toList()); } - private TypeMirror parameterType(TypeMirror type) { - switch (type.getKind()) { + private TypeMirror parameterType(VariableElement parameter) { + final ExecutableElement method = + (ExecutableElement) parameter.getEnclosingElement(); + final ExecutableType methodType = + (ExecutableType) context.getTypeUtils() + .asMemberOf((DeclaredType) element.asType(), method); + final TypeMirror type = methodType.getParameterTypes() + .get( method.getParameters().indexOf(parameter) ); + switch ( type.getKind() ) { case TYPEVAR: final TypeVariable typeVariable = (TypeVariable) type; return context.getTypeUtils().erasure(typeVariable); @@ -2213,7 +2217,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { if ( returnType != null ) { final Types types = context.getTypeUtils(); for ( VariableElement parameter : method.getParameters() ) { - final TypeMirror parameterType = parameter.asType(); + final TypeMirror parameterType = parameterType( parameter ); final TypeMirror typeArgument = getTypeArgument( parameterType ); final String type = parameterType.toString(); final boolean pageRequest = type.startsWith(JD_PAGE_REQUEST); diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/SuperDaoTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/SuperDaoTest.java index d4b215362f..910cf6091a 100644 --- a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/SuperDaoTest.java +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/SuperDaoTest.java @@ -20,10 +20,10 @@ public class SuperDaoTest extends CompilationTest { @Test @WithClasses({ Book.class, SuperDao.class, Dao.class }) public void testQueryMethod() { - System.out.println( TestUtil.getMetaModelSourceAsString( SuperDao.class ) ); +// System.out.println( TestUtil.getMetaModelSourceAsString( SuperDao.class ) ); System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) ); assertMetamodelClassGeneratedFor( Book.class ); - assertMetamodelClassGeneratedFor( SuperDao.class ); +// assertMetamodelClassGeneratedFor( SuperDao.class ); assertMetamodelClassGeneratedFor( Dao.class ); } } diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Book.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Book.java new file mode 100644 index 0000000000..e8a339c5ef --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Book.java @@ -0,0 +1,23 @@ +package org.hibernate.processor.test.superdao.generic; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import org.hibernate.annotations.NaturalId; +import org.hibernate.processor.test.hqlsql.Publisher; + +import java.math.BigDecimal; +import java.time.LocalDate; + +@Entity +public class Book { + @Id String isbn; + @NaturalId String title; + String text; + @NaturalId String authorName; + @ManyToOne + Publisher publisher; + BigDecimal price; + int pages; + LocalDate publicationDate; +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Dao.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Dao.java new file mode 100644 index 0000000000..1de369c775 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/Dao.java @@ -0,0 +1,8 @@ +package org.hibernate.processor.test.superdao.generic; + +import org.hibernate.annotations.processing.Find; + +public interface Dao extends SuperDao { + @Find + Book getConc(String isbn); +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDao.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDao.java new file mode 100644 index 0000000000..86ea4598f3 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDao.java @@ -0,0 +1,23 @@ +package org.hibernate.processor.test.superdao.generic; + +import jakarta.persistence.EntityManager; +import org.hibernate.annotations.processing.Find; +import org.hibernate.annotations.processing.HQL; +import org.hibernate.annotations.processing.Pattern; + +import java.util.List; + +public interface SuperDao { + + EntityManager em(); + + @Find + T get(K isbn); + + @Find + List books1(@Pattern String title); + + @HQL("where title like :title") + List books2(String title); + +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDaoTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDaoTest.java new file mode 100644 index 0000000000..3ff0ea3846 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/processor/test/superdao/generic/SuperDaoTest.java @@ -0,0 +1,29 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.processor.test.superdao.generic; + +import org.hibernate.processor.test.util.CompilationTest; +import org.hibernate.processor.test.util.TestUtil; +import org.hibernate.processor.test.util.WithClasses; +import org.junit.Test; + +import static org.hibernate.processor.test.util.TestUtil.assertMetamodelClassGeneratedFor; + +/** + * @author Gavin King + */ +public class SuperDaoTest extends CompilationTest { + @Test + @WithClasses({ Book.class, SuperDao.class, Dao.class }) + public void testQueryMethod() { + System.out.println( TestUtil.getMetaModelSourceAsString( SuperDao.class ) ); + System.out.println( TestUtil.getMetaModelSourceAsString( Dao.class ) ); + assertMetamodelClassGeneratedFor( Book.class ); + assertMetamodelClassGeneratedFor( SuperDao.class ); + assertMetamodelClassGeneratedFor( Dao.class ); + } +}