diff --git a/pom.xml b/pom.xml
index 6fd19eccb9..4e260500de 100755
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-jpa-parent
- 4.0.0-SNAPSHOT
+ 4.0.x-GH-4029-SNAPSHOT
pom
Spring Data JPA Parent
diff --git a/spring-data-envers/pom.xml b/spring-data-envers/pom.xml
index 0bdf2c8e7e..124d4a4271 100755
--- a/spring-data-envers/pom.xml
+++ b/spring-data-envers/pom.xml
@@ -5,12 +5,12 @@
org.springframework.data
spring-data-envers
- 4.0.0-SNAPSHOT
+ 4.0.x-GH-4029-SNAPSHOT
org.springframework.data
spring-data-jpa-parent
- 4.0.0-SNAPSHOT
+ 4.0.x-GH-4029-SNAPSHOT
../pom.xml
diff --git a/spring-data-jpa-distribution/pom.xml b/spring-data-jpa-distribution/pom.xml
index af5244a230..1981be4b34 100644
--- a/spring-data-jpa-distribution/pom.xml
+++ b/spring-data-jpa-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-jpa-parent
- 4.0.0-SNAPSHOT
+ 4.0.x-GH-4029-SNAPSHOT
../pom.xml
diff --git a/spring-data-jpa/pom.xml b/spring-data-jpa/pom.xml
index cbec8a2645..5903a7e834 100644
--- a/spring-data-jpa/pom.xml
+++ b/spring-data-jpa/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-jpa
- 4.0.0-SNAPSHOT
+ 4.0.x-GH-4029-SNAPSHOT
Spring Data JPA
Spring Data module for JPA repositories.
@@ -16,7 +16,7 @@
org.springframework.data
spring-data-jpa-parent
- 4.0.0-SNAPSHOT
+ 4.0.x-GH-4029-SNAPSHOT
../pom.xml
diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java
index 3d5bee6bf9..bf43d72f68 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java
@@ -57,6 +57,7 @@
* Factory for {@link AotQueries}.
*
* @author Mark Paluch
+ * @author Christoph Strobl
* @since 4.0
*/
class QueriesFactory {
@@ -123,7 +124,7 @@ public AotQueries createQueries(RepositoryInformation repositoryInformation, Ret
QueryEnhancerSelector selector, MergedAnnotation query, JpaQueryMethod queryMethod) {
if (query.isPresent() && StringUtils.hasText(query.getString("value"))) {
- return buildStringQuery(repositoryInformation.getDomainType(), returnedType, selector, query, queryMethod);
+ return buildStringQuery(returnedType, selector, query, queryMethod);
}
String queryName = queryMethod.getNamedQueryName();
@@ -138,10 +139,10 @@ private boolean hasNamedQuery(ReturnedType returnedType, String queryName) {
return namedQueries.hasQuery(queryName) || getNamedQuery(returnedType, queryName) != null;
}
- private AotQueries buildStringQuery(Class> domainType, ReturnedType returnedType, QueryEnhancerSelector selector,
+ private AotQueries buildStringQuery(ReturnedType returnedType, QueryEnhancerSelector selector,
MergedAnnotation query, JpaQueryMethod queryMethod) {
- UnaryOperator operator = s -> s.replaceAll("#\\{#entityName}", domainType.getSimpleName());
+ UnaryOperator operator = s -> s.replaceAll("#\\{#entityName}", queryMethod.getEntityInformation().getEntityName());
boolean isNative = query.getBoolean("nativeQuery");
Function queryFunction = isNative ? DeclaredQuery::nativeQuery : DeclaredQuery::jpqlQuery;
queryFunction = operator.andThen(queryFunction);
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/QueriesFactoryUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/QueriesFactoryUnitTests.java
new file mode 100644
index 0000000000..dbd4dc472f
--- /dev/null
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/QueriesFactoryUnitTests.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2025-present 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.
+ */
+package org.springframework.data.jpa.repository.aot;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import jakarta.persistence.EntityManagerFactory;
+
+import org.assertj.core.api.InstanceOfAssertFactories;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.springframework.core.annotation.MergedAnnotation;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.jpa.repository.query.JpaEntityMetadata;
+import org.springframework.data.jpa.repository.query.JpaQueryMethod;
+import org.springframework.data.jpa.repository.query.QueryEnhancerSelector;
+import org.springframework.data.jpa.repository.support.JpaEntityInformation;
+import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
+import org.springframework.data.repository.config.RepositoryConfigurationSource;
+import org.springframework.data.repository.core.RepositoryInformation;
+import org.springframework.data.repository.query.ReturnedType;
+
+/**
+ * Unit tests for {@link QueriesFactory}.
+ *
+ * @author Christoph Strobl
+ */
+class QueriesFactoryUnitTests {
+
+ QueriesFactory factory;
+
+ @BeforeEach
+ void setUp() {
+
+ RepositoryConfigurationSource configSource = Mockito.mock(RepositoryConfigurationSource.class);
+ EntityManagerFactory entityManagerFactory = Mockito.mock(EntityManagerFactory.class);
+
+ factory = new QueriesFactory(configSource, entityManagerFactory, this.getClass().getClassLoader());
+ }
+
+ @Test // GH-4029
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ void stringQueryShouldResolveEntityNameFromJakartaAnnotationIfPresent() {
+
+ RepositoryInformation repositoryInformation = Mockito.mock(RepositoryInformation.class);
+ JpaEntityMetadata> entityMetadata = Mockito.mock(JpaEntityInformation.class);
+ when(entityMetadata.getEntityName()).thenReturn("CustomNamed");
+
+ MergedAnnotation queryAnnotation = Mockito.mock(MergedAnnotation.class);
+ when(queryAnnotation.isPresent()).thenReturn(true);
+ when(queryAnnotation.getString(eq("value"))).thenReturn("select t from #{#entityName} t");
+ when(queryAnnotation.getBoolean(eq("nativeQuery"))).thenReturn(false);
+ when(queryAnnotation.getString("countQuery")).thenReturn("select count(t) from #{#entityName} t");
+
+ JpaQueryMethod queryMethod = Mockito.mock(JpaQueryMethod.class);
+ when(queryMethod.getEntityInformation()).thenReturn((JpaEntityMetadata) entityMetadata);
+
+ AotQueries generatedQueries = factory.createQueries(repositoryInformation,
+ ReturnedType.of(Object.class, Object.class, new SpelAwareProxyProjectionFactory()),
+ QueryEnhancerSelector.DEFAULT_SELECTOR, queryAnnotation, queryMethod);
+
+ assertThat(generatedQueries.result()).asInstanceOf(InstanceOfAssertFactories.type(StringAotQuery.class))
+ .extracting(StringAotQuery::getQueryString).isEqualTo("select t from CustomNamed t");
+ assertThat(generatedQueries.count()).asInstanceOf(InstanceOfAssertFactories.type(StringAotQuery.class))
+ .extracting(StringAotQuery::getQueryString).isEqualTo("select count(t) from CustomNamed t");
+ }
+}