From f28a2c2052bfced1f994d68d40f77852ef2e7045 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 5 Jun 2025 09:31:24 +0200 Subject: [PATCH] Polishing. Use getPackageName() instead of getPackage().getName(), remove essentially duplicate tests and fix array type handling of Q types by using the arrays component type for assignability checks. Original Pull Request: #3284 --- .../data/util/QTypeContributor.java | 21 ++++--- src/test/java/QTypeInDefaultPackage.java | 23 +++++++ .../data/util/QTypeContributorUnitTests.java | 62 ++++++++++--------- 3 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 src/test/java/QTypeInDefaultPackage.java diff --git a/src/main/java/org/springframework/data/util/QTypeContributor.java b/src/main/java/org/springframework/data/util/QTypeContributor.java index 8932bdcc9..cb7748771 100644 --- a/src/main/java/org/springframework/data/util/QTypeContributor.java +++ b/src/main/java/org/springframework/data/util/QTypeContributor.java @@ -18,11 +18,11 @@ package org.springframework.data.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jspecify.annotations.Nullable; - import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.TypeReference; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl @@ -38,18 +38,20 @@ public class QTypeContributor { Class entityPathType = getEntityPathType(classLoader); - if (entityPathType == null) { - return; - } - - if (type.isPrimitive() || type.isArray()) { + if (entityPathType == null || type.isPrimitive()) { return; } String queryClassName = getQueryClassName(type); + if (ClassUtils.isPresent(queryClassName, classLoader)) { - if (ClassUtils.isAssignable(entityPathType, ClassUtils.forName(queryClassName, classLoader))) { + Class actualType = ClassUtils.forName(queryClassName, classLoader); + if (actualType.isArray()) { + actualType = actualType.getComponentType(); + } + + if (ClassUtils.isAssignable(entityPathType, actualType)) { logger.debug("Registering Q type %s for %s."); context.getRuntimeHints().reflection().registerType(TypeReference.of(queryClassName), @@ -84,7 +86,10 @@ public class QTypeContributor { private static String getQueryClassName(Class domainClass) { String simpleClassName = ClassUtils.getShortName(domainClass); - String pkgName = domainClass.getPackage().getName(); + String pkgName = domainClass.getPackageName(); + if (ObjectUtils.isEmpty(pkgName)) { + return String.format("Q%s%s", getClassBase(simpleClassName), domainClass.getSimpleName()); + } return String.format("%s.Q%s%s", pkgName, getClassBase(simpleClassName), domainClass.getSimpleName()); } diff --git a/src/test/java/QTypeInDefaultPackage.java b/src/test/java/QTypeInDefaultPackage.java new file mode 100644 index 000000000..a116b9041 --- /dev/null +++ b/src/test/java/QTypeInDefaultPackage.java @@ -0,0 +1,23 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Dummy type looking like a QueryDSL Generated one but its not - does not extend the + * {@link com.querydsl.core.types.dsl.EntityPathBase}) + * + * @author Christoph Strobl + */ +public class QTypeInDefaultPackage {} diff --git a/src/test/java/org/springframework/data/util/QTypeContributorUnitTests.java b/src/test/java/org/springframework/data/util/QTypeContributorUnitTests.java index ae60190c0..af0bebc5a 100644 --- a/src/test/java/org/springframework/data/util/QTypeContributorUnitTests.java +++ b/src/test/java/org/springframework/data/util/QTypeContributorUnitTests.java @@ -15,7 +15,12 @@ */ package org.springframework.data.util; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; + +import java.net.URLClassLoader; +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.Test; import org.springframework.aot.generate.ClassNameGenerator; @@ -26,7 +31,6 @@ import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.data.aot.sample.ConfigWithQuerydslPredicateExecutor.Person; import org.springframework.data.aot.sample.QConfigWithQuerydslPredicateExecutor_Person; import org.springframework.data.classloadersupport.HidingClassLoader; -import org.springframework.data.querydsl.User; import org.springframework.javapoet.ClassName; import com.querydsl.core.types.EntityPath; @@ -34,6 +38,7 @@ import com.querydsl.core.types.EntityPath; /** * Unit tests for {@link QTypeContributor}. * + * @author Christoph Strobl * @author ckdgus08 */ class QTypeContributorUnitTests { @@ -75,8 +80,8 @@ class QTypeContributorUnitTests { RuntimeHintsPredicates.reflection().onType(QConfigWithQuerydslPredicateExecutor_Person.class).negate()); } - @Test // DATAMONGO-4958 - void doesNotAddQTypeHintForArrayType() { + @Test // GH-3284 + void addsQTypeHintForArrayType() { GenerationContext generationContext = new DefaultGenerationContext( new ClassNameGenerator(ClassName.get(this.getClass())), new InMemoryGeneratedFiles()); @@ -85,48 +90,47 @@ class QTypeContributorUnitTests { assertThat(generationContext.getRuntimeHints()).matches( RuntimeHintsPredicates.reflection().onType(QConfigWithQuerydslPredicateExecutor_Person.class).negate()); - assertThat(generationContext.getRuntimeHints()).matches( - RuntimeHintsPredicates.reflection().onType(QConfigWithQuerydslPredicateExecutor_Person[].class).negate()); + assertThat(generationContext.getRuntimeHints()) + .matches(RuntimeHintsPredicates.reflection().onType(QConfigWithQuerydslPredicateExecutor_Person[].class)); } - @Test // DATAMONGO-4958 - void addsQTypeHintForQUserType() { + @Test // GH-3284 + void doesNotAddQTypeHintForPrimitiveType() { GenerationContext generationContext = new DefaultGenerationContext( new ClassNameGenerator(ClassName.get(this.getClass())), new InMemoryGeneratedFiles()); - QTypeContributor.contributeEntityPath(User.class, generationContext, getClass().getClassLoader()); + QTypeContributor.contributeEntityPath(int.class, generationContext, getClass().getClassLoader()); - var qUserHintCount = generationContext.getRuntimeHints().reflection().typeHints() - .filter(hint -> hint.getType().getName().equals("org.springframework.data.querydsl.QUser")) - .count(); - assertThat(qUserHintCount).isEqualTo(1); + assertThat(generationContext.getRuntimeHints().reflection().typeHints()).isEmpty(); } - @Test // DATAMONGO-4958 - void doesNotAddQTypeHintForQUserArrayType() { + @Test // GH-3284 + void doesNotFailForTypeInDefaultPackage() throws Exception { GenerationContext generationContext = new DefaultGenerationContext( new ClassNameGenerator(ClassName.get(this.getClass())), new InMemoryGeneratedFiles()); - var classLoader = getClass().getClassLoader(); - QTypeContributor.contributeEntityPath(User[].class, generationContext, classLoader); + class CapturingClassLoader extends ClassLoader { - assertThat(generationContext.getRuntimeHints().reflection().typeHints()).isEmpty(); - var qUserHintCount = generationContext.getRuntimeHints().reflection().typeHints() - .filter(hint -> hint.getType().getName().equals("org.springframework.data.querydsl.QUser")) - .count(); - assertThat(qUserHintCount).isEqualTo(0); - } + final Set lookups = new HashSet<>(10); - @Test // DATAMONGO-4958 - void doesNotAddQTypeHintForPrimitiveType() { + CapturingClassLoader() { + super(URLClassLoader.getSystemClassLoader()); + } - GenerationContext generationContext = new DefaultGenerationContext( - new ClassNameGenerator(ClassName.get(this.getClass())), new InMemoryGeneratedFiles()); + @Override + public Class loadClass(String name) throws ClassNotFoundException { + lookups.add(name); + return super.loadClass(name); + } + } - QTypeContributor.contributeEntityPath(int.class, generationContext, getClass().getClassLoader()); + CapturingClassLoader classLoaderToUse = new CapturingClassLoader(); - assertThat(generationContext.getRuntimeHints().reflection().typeHints()).isEmpty(); + var typeInDefaultPackage = Class.forName("TypeInDefaultPackage"); + assertThatNoException().isThrownBy( + () -> QTypeContributor.contributeEntityPath(typeInDefaultPackage, generationContext, classLoaderToUse)); + assertThat(classLoaderToUse.lookups).contains("QTypeInDefaultPackage"); } }