diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/SpringDataJPAProcessor.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/SpringDataJPAProcessor.java index 7d6b672c080a6..2b47978a8c807 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/SpringDataJPAProcessor.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/SpringDataJPAProcessor.java @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -95,7 +95,7 @@ IgnorableNonIndexedClasses ignorable() { @BuildStep void registerReflection(BuildProducer producer) { - producer.produce(new ReflectiveClassBuildItem(true, false, + producer.produce(ReflectiveClassBuildItem.builder( "org.springframework.data.domain.Page", "org.springframework.data.domain.Slice", "org.springframework.data.domain.PageImpl", @@ -103,7 +103,7 @@ void registerReflection(BuildProducer producer) { "org.springframework.data.domain.Sort", "org.springframework.data.domain.Chunk", "org.springframework.data.domain.PageRequest", - "org.springframework.data.domain.AbstractPageRequest")); + "org.springframework.data.domain.AbstractPageRequest").methods(true).fields(false).build()); } @BuildStep @@ -118,7 +118,7 @@ void build(CombinedIndexBuildItem index, detectAndLogSpecificSpringPropertiesIfExist(); IndexView indexView = index.getIndex(); - List interfacesExtendingRepository = getAllInterfacesExtending(DotNames.SUPPORTED_REPOSITORIES, + LinkedHashSet interfacesExtendingRepository = getAllInterfacesExtending(DotNames.SUPPORTED_REPOSITORIES, indexView); addRepositoryDefinitionInstances(indexView, interfacesExtendingRepository); @@ -134,21 +134,15 @@ void build(CombinedIndexBuildItem index, } private void addInterfacesExtendingIntermediateRepositories(IndexView indexView, - List interfacesExtendingRepository) { + Set interfacesExtendingRepository) { Collection noRepositoryBeanRepos = getAllNoRepositoryBeanInterfaces(indexView); - Iterator iterator = noRepositoryBeanRepos.iterator(); - while (iterator.hasNext()) { - DotName interfaceName = iterator.next(); - if (DotNames.SUPPORTED_REPOSITORIES.contains(interfaceName)) { - iterator.remove(); - } - } - List interfacesExtending = getAllInterfacesExtending(noRepositoryBeanRepos, indexView); + noRepositoryBeanRepos.removeIf(DotNames.SUPPORTED_REPOSITORIES::contains); + Set interfacesExtending = getAllInterfacesExtending(noRepositoryBeanRepos, indexView); interfacesExtendingRepository.addAll(interfacesExtending); } // classes annotated with @RepositoryDefinition behave exactly as if they extended Repository - private void addRepositoryDefinitionInstances(IndexView indexView, List interfacesExtendingRepository) { + private void addRepositoryDefinitionInstances(IndexView indexView, Set interfacesExtendingRepository) { Collection repositoryDefinitions = indexView .getAnnotations(DotNames.SPRING_DATA_REPOSITORY_DEFINITION); for (AnnotationInstance repositoryDefinition : repositoryDefinitions) { @@ -223,35 +217,15 @@ private void detectAndLogSpecificSpringPropertiesIfExist() { } } - private void removeNoRepositoryBeanClasses(List interfacesExtendingRepository) { - Iterator iterator = interfacesExtendingRepository.iterator(); - while (iterator.hasNext()) { - ClassInfo next = iterator.next(); - if (next.classAnnotation(DotNames.SPRING_DATA_NO_REPOSITORY_BEAN) != null) { - iterator.remove(); - } - } + private void removeNoRepositoryBeanClasses(Set interfacesExtendingRepository) { + interfacesExtendingRepository.removeIf( + next -> next.declaredAnnotation(DotNames.SPRING_DATA_NO_REPOSITORY_BEAN) != null); } - // inefficient implementation, see: https://github.com/wildfly/jandex/issues/65 - private List getAllInterfacesExtending(Collection targets, IndexView index) { - List result = new ArrayList<>(); - Collection knownClasses = index.getKnownClasses(); - for (ClassInfo clazz : knownClasses) { - if (!Modifier.isInterface(clazz.flags())) { - continue; - } - List interfaceNames = clazz.interfaceNames(); - boolean found = false; - for (DotName interfaceName : interfaceNames) { - if (targets.contains(interfaceName)) { - found = true; - break; - } - } - if (found) { - result.add(clazz); - } + private LinkedHashSet getAllInterfacesExtending(Collection targets, IndexView index) { + LinkedHashSet result = new LinkedHashSet<>(); + for (DotName target : targets) { + result.addAll(index.getAllKnownSubinterfaces(target)); } return result; } @@ -269,7 +243,7 @@ private Set implementCrudRepositories(BuildProducer generatedClasses, BuildProducer additionalBeans, BuildProducer reflectiveClasses, - List crudRepositoriesToImplement, IndexView index) { + Set crudRepositoriesToImplement, IndexView index) { ClassOutput beansClassOutput = new GeneratedBeanGizmoAdaptor(generatedBeans); ClassOutput otherClassOutput = new GeneratedClassGizmoAdaptor(generatedClasses, true); @@ -282,7 +256,7 @@ private Set implementCrudRepositories(BuildProducer { // the generated classes that implement interfaces for holding custom query results need // to be registered for reflection here since this is the only point where the generated class is known - reflectiveClasses.produce(new ReflectiveClassBuildItem(true, false, className)); + reflectiveClasses.produce(ReflectiveClassBuildItem.builder(className).methods(true).fields(false).build()); }), JavaJpaTypeBundle.BUNDLE); Set entities = new HashSet<>(); diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java index 133b8b2f1cb68..a21110370604e 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/DerivedMethodsAdder.java @@ -6,8 +6,10 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import jakarta.transaction.Transactional; @@ -56,7 +58,7 @@ public DerivedMethodsAdder(IndexView index, TypeBundle typeBundle, ClassOutput n public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescriptor, String generatedClassName, ClassInfo repositoryClassInfo, ClassInfo entityClassInfo) { MethodNameParser methodNameParser = new MethodNameParser(entityClassInfo, index); - List repoMethods = new ArrayList<>(repositoryClassInfo.methods()); + LinkedHashSet repoMethods = new LinkedHashSet<>(repositoryClassInfo.methods()); // Remember custom return type methods: {resultType:[methodName]} Map> customResultTypes = new HashMap<>(3); @@ -64,10 +66,7 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr //As intermediate interfaces are supported for spring data repositories, we need to search the methods declared in such interfaced and add them to the methods to implement list for (DotName extendedInterface : repositoryClassInfo.interfaceNames()) { - if (GenerationUtil.isIntermediateRepository(extendedInterface, index)) { - List methods = index.getClassByName(extendedInterface).methods(); - repoMethods.addAll(methods); - } + addAllMethodOfIntermediateRepository(extendedInterface, repoMethods); } for (MethodInfo method : repoMethods) { if (method.annotation(DotNames.SPRING_DATA_QUERY) != null) { // handled by CustomQueryMethodsAdder @@ -288,6 +287,17 @@ public void add(ClassCreator classCreator, FieldDescriptor entityClassFieldDescr } } + private void addAllMethodOfIntermediateRepository(DotName interfaceDotName, Set result) { + if (GenerationUtil.isIntermediateRepository(interfaceDotName, index)) { + ClassInfo classInfo = index.getClassByName(interfaceDotName); + List methods = classInfo.methods(); + result.addAll(methods); + for (DotName superInterface : classInfo.interfaceNames()) { + addAllMethodOfIntermediateRepository(superInterface, result); + } + } + } + private void generateCustomResultTypes(DotName interfaceName, DotName implName, ClassInfo entityClassInfo, List queryMethods) { diff --git a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/GenerationUtil.java b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/GenerationUtil.java index ecf36a30a1845..f85c020a2b950 100644 --- a/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/GenerationUtil.java +++ b/extensions/spring-data-jpa/deployment/src/main/java/io/quarkus/spring/data/deployment/generate/GenerationUtil.java @@ -31,23 +31,17 @@ static List extendedSpringDataRepos(ClassInfo repositoryToImplement, In if (DotNames.SUPPORTED_REPOSITORIES.contains(interfaceName)) { result.add(interfaceName); } else { - ClassInfo intermediateInterfaces = index.getClassByName(interfaceName); - List dns = intermediateInterfaces.interfaceNames(); - for (DotName in : dns) { - result.addAll(extendedSpringDataRepos(intermediateInterfaces, index)); - } + result.addAll(extendedSpringDataRepos(index.getClassByName(interfaceName), index)); } } return result; } static boolean isIntermediateRepository(DotName interfaceName, IndexView indexView) { - if (!DotNames.SUPPORTED_REPOSITORIES.contains(interfaceName)) { - ClassInfo intermediateInterface = indexView.getClassByName(interfaceName); - List extendedSpringDataRepos = extendedSpringDataRepos(intermediateInterface, indexView); - return DotNames.SUPPORTED_REPOSITORIES.stream().anyMatch(item -> extendedSpringDataRepos.contains(item)); + if (DotNames.SUPPORTED_REPOSITORIES.contains(interfaceName)) { + return false; } - return false; + return !extendedSpringDataRepos(indexView.getClassByName(interfaceName), indexView).isEmpty(); } static Set interfaceMethods(Collection interfaces, IndexView index) { diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/ByPassHolder.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/ByPassHolder.java new file mode 100644 index 0000000000000..5d3192d4fcc95 --- /dev/null +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/ByPassHolder.java @@ -0,0 +1,6 @@ +package io.quarkus.it.spring.data.jpa; + +public interface ByPassHolder { + + boolean isBypass(); +} diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/IntermediateRepository.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BypassHolderRepository.java similarity index 72% rename from integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/IntermediateRepository.java rename to integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BypassHolderRepository.java index 66bfb2b63ab43..c7665642dcde5 100644 --- a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/IntermediateRepository.java +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/BypassHolderRepository.java @@ -6,7 +6,7 @@ import org.springframework.data.repository.NoRepositoryBean; @NoRepositoryBean -public interface IntermediateRepository extends JpaRepository { +public interface BypassHolderRepository extends JpaRepository { default public void doNothing() { } @@ -15,4 +15,5 @@ default public T findMandatoryById(ID id) { return findById(id).orElseThrow(() -> new IllegalStateException("not found: " + id)); } + Post findFirstByBypassTrue(); } diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/IntermediatePostRepository.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/IntermediatePostRepository.java new file mode 100644 index 0000000000000..7f2528a208182 --- /dev/null +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/IntermediatePostRepository.java @@ -0,0 +1,12 @@ +package io.quarkus.it.spring.data.jpa; + +import java.time.ZonedDateTime; +import java.util.List; + +/** + * Used to ensure that entity relationships work correctly + */ +public interface IntermediatePostRepository extends BypassHolderRepository { + + List findByPostedBefore(ZonedDateTime zdt); +} diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Post.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Post.java index 4e921073643e4..8bae2cbac9c67 100644 --- a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Post.java +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/Post.java @@ -18,7 +18,7 @@ @Entity(name = "Post") @Table(name = "post") -public class Post { +public class Post implements ByPassHolder { @Id @SequenceGenerator(name = "postSeqGen", sequenceName = "postSeq", initialValue = 100, allocationSize = 1) diff --git a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/PostRepository.java b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/PostRepository.java index 610ee5d70f48a..13d4eeafe0660 100644 --- a/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/PostRepository.java +++ b/integration-tests/spring-data-jpa/src/main/java/io/quarkus/it/spring/data/jpa/PostRepository.java @@ -1,16 +1,11 @@ package io.quarkus.it.spring.data.jpa; -import java.time.ZonedDateTime; import java.util.List; /** * Used to ensure that entity relationships work correctly */ -public interface PostRepository extends IntermediateRepository { - - Post findFirstByBypassTrue(); - - List findByPostedBefore(ZonedDateTime zdt); +public interface PostRepository extends IntermediatePostRepository { List findAllByOrganization(String organization); diff --git a/integration-tests/spring-data-jpa/src/main/resources/application.properties b/integration-tests/spring-data-jpa/src/main/resources/application.properties index 57b530c1c638f..9953061a83078 100644 --- a/integration-tests/spring-data-jpa/src/main/resources/application.properties +++ b/integration-tests/spring-data-jpa/src/main/resources/application.properties @@ -11,6 +11,3 @@ quarkus.native.additional-build-args=--initialize-at-run-time=io.quarkus.it.spri #quarkus.hibernate-orm.log.sql=true %prod.quarkus.hibernate-orm.sql-load-script=import.sql - -# added only to ensure that building the native binary with this flag works properly -quarkus.native.inline-before-analysis=true