diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DtoProjectionTransformerDelegate.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DtoProjectionTransformerDelegate.java
index d57a83ab9..fd362c1e4 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DtoProjectionTransformerDelegate.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/DtoProjectionTransformerDelegate.java
@@ -42,7 +42,7 @@ class DtoProjectionTransformerDelegate {
public QueryTokenStream transformSelectionList(QueryTokenStream selectionList) {
if (!returnedType.isProjecting() || returnedType.getReturnedType().isInterface()
- || selectionList.stream().anyMatch(it -> it.equals(TOKEN_NEW))) {
+ || !returnedType.needsCustomConstruction() || selectionList.stream().anyMatch(it -> it.equals(TOKEN_NEW))) {
return selectionList;
}
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractDtoQueryTransformerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractDtoQueryTransformerUnitTests.java
new file mode 100644
index 000000000..e72ded4fc
--- /dev/null
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/AbstractDtoQueryTransformerUnitTests.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2024-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.
+ */
+package org.springframework.data.jpa.repository.query;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.lang.reflect.Method;
+
+import org.antlr.v4.runtime.tree.ParseTreeVisitor;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.data.jpa.provider.PersistenceProvider;
+import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
+import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
+import org.springframework.data.repository.query.QueryMethod;
+
+/**
+ * Support class for unit tests for {@link DtoProjectionTransformerDelegate}.
+ *
+ * @author Mark Paluch
+ */
+abstract class AbstractDtoQueryTransformerUnitTests
> {
+
+ JpaQueryMethod method = getMethod("dtoProjection");
+
+ @Test // GH-3076
+ void shouldTranslateSingleProjectionToDto() {
+
+ P parser = parse("SELECT p from Person p");
+
+ QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
+
+ assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
+ "SELECT new org.springframework.data.jpa.repository.query.AbstractDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar) from Person p");
+ }
+
+ @Test // GH-3076
+ void shouldRewriteQueriesWithSubselect() {
+
+ P parser = parse("select u from User u left outer join u.roles r where r in (select r from Role r)");
+
+ QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
+
+ assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
+ "select new org.springframework.data.jpa.repository.query.AbstractDtoQueryTransformerUnitTests$MyRecord(u.foo, u.bar) from User u left outer join u.roles r where r in (select r from Role r)");
+ }
+
+ @Test // GH-3076
+ void shouldNotRewriteQueriesWithoutProperties() {
+
+ JpaQueryMethod method = getMethod("noProjection");
+ P parser = parse("select u from User u");
+
+ QueryTokenStream visit = getTransformer(parser, method).visit(parser.getContext());
+
+ assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("select u from User u");
+ }
+
+ @Test // GH-3076
+ void shouldNotTranslateConstructorExpressionQuery() {
+
+ P parser = parse("SELECT NEW com.foo(p) from Person p");
+
+ QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
+
+ assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("SELECT NEW com.foo(p) from Person p");
+ }
+
+ @Test
+ void shouldTranslatePropertySelectionToDto() {
+
+ P parser = parse("SELECT p.foo, p.bar, sum(p.age) from Person p");
+
+ QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
+
+ assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
+ "SELECT new org.springframework.data.jpa.repository.query.AbstractDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar, sum(p.age)) from Person p");
+ }
+
+ private JpaQueryMethod getMethod(String name, Class>... parameterTypes) {
+
+ try {
+ Method method = MyRepo.class.getMethod(name, parameterTypes);
+ PersistenceProvider persistenceProvider = PersistenceProvider.HIBERNATE;
+
+ return new JpaQueryMethod(method, new DefaultRepositoryMetadata(MyRepo.class),
+ new SpelAwareProxyProjectionFactory(), persistenceProvider);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ abstract P parse(String query);
+
+ private ParseTreeVisitor getTransformer(P parser) {
+ return getTransformer(parser, method);
+ }
+
+ abstract ParseTreeVisitor getTransformer(P parser, QueryMethod method);
+
+ interface MyRepo extends Repository {
+
+ MyRecord dtoProjection();
+
+ EmptyClass noProjection();
+ }
+
+ record Person(String id) {
+
+ }
+
+ record MyRecord(String foo, String bar) {
+
+ }
+
+ static class EmptyClass {
+
+ }
+}
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlDtoQueryTransformerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlDtoQueryTransformerUnitTests.java
index b8ac4f35a..967af726e 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlDtoQueryTransformerUnitTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/EqlDtoQueryTransformerUnitTests.java
@@ -15,102 +15,26 @@
*/
package org.springframework.data.jpa.repository.query;
-import static org.assertj.core.api.Assertions.*;
-
-import java.lang.reflect.Method;
-
-import org.junit.jupiter.api.Test;
+import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.springframework.data.domain.Sort;
-import org.springframework.data.jpa.provider.PersistenceProvider;
-import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
-import org.springframework.data.repository.Repository;
-import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
+import org.springframework.data.repository.query.QueryMethod;
/**
* Unit tests for {@link DtoProjectionTransformerDelegate}.
*
* @author Mark Paluch
*/
-class EqlDtoQueryTransformerUnitTests {
+class EqlDtoQueryTransformerUnitTests extends AbstractDtoQueryTransformerUnitTests {
- JpaQueryMethod method = getMethod("dtoProjection");
-
- @Test // GH-3076
- void shouldTranslateSingleProjectionToDto() {
-
- JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser.parseQuery("SELECT p from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "SELECT new org.springframework.data.jpa.repository.query.EqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar) from Person p");
+ @Override
+ JpaQueryEnhancer.EqlQueryParser parse(String query) {
+ return JpaQueryEnhancer.EqlQueryParser.parseQuery(query);
}
- @Test // GH-3076
- void shouldRewriteQueriesWithSubselect() {
-
- JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser
- .parseQuery("select u from User u left outer join u.roles r where r in (select r from Role r)");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "select new org.springframework.data.jpa.repository.query.EqlDtoQueryTransformerUnitTests$MyRecord(u.foo, u.bar) from User u left outer join u.roles r where r in (select r from Role r)");
- }
-
- @Test // GH-3076
- void shouldNotTranslateConstructorExpressionQuery() {
-
- JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser
- .parseQuery("SELECT NEW Foo(p) from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("SELECT NEW Foo(p) from Person p");
- }
-
- @Test
- void shouldTranslatePropertySelectionToDto() {
-
- JpaQueryEnhancer.EqlQueryParser parser = JpaQueryEnhancer.EqlQueryParser
- .parseQuery("SELECT p.foo, p.bar, sum(p.age) from Person p");
-
- EqlSortedQueryTransformer transformer = getTransformer(parser);
- QueryTokenStream visit = transformer.visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "SELECT new org.springframework.data.jpa.repository.query.EqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar, sum(p.age)) from Person p");
- }
-
- private JpaQueryMethod getMethod(String name, Class>... parameterTypes) {
-
- try {
- Method method = MyRepo.class.getMethod(name, parameterTypes);
- PersistenceProvider persistenceProvider = PersistenceProvider.HIBERNATE;
-
- return new JpaQueryMethod(method, new DefaultRepositoryMetadata(MyRepo.class),
- new SpelAwareProxyProjectionFactory(), persistenceProvider);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- }
-
- private EqlSortedQueryTransformer getTransformer(JpaQueryEnhancer.EqlQueryParser parser) {
+ @Override
+ ParseTreeVisitor getTransformer(JpaQueryEnhancer.EqlQueryParser parser, QueryMethod method) {
return new EqlSortedQueryTransformer(Sort.unsorted(), parser.getQueryInformation(),
method.getResultProcessor().getReturnedType());
}
-
- interface MyRepo extends Repository {
-
- MyRecord dtoProjection();
- }
-
- record Person(String id) {
-
- }
-
- record MyRecord(String foo, String bar) {
-
- }
}
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlDtoQueryTransformerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlDtoQueryTransformerUnitTests.java
index 9afdcd976..4ac9f6a9c 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlDtoQueryTransformerUnitTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/HqlDtoQueryTransformerUnitTests.java
@@ -15,101 +15,27 @@
*/
package org.springframework.data.jpa.repository.query;
-import static org.assertj.core.api.Assertions.*;
-
-import java.lang.reflect.Method;
-
-import org.junit.jupiter.api.Test;
+import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.springframework.data.domain.Sort;
-import org.springframework.data.jpa.provider.PersistenceProvider;
-import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
-import org.springframework.data.repository.Repository;
-import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
+import org.springframework.data.repository.query.QueryMethod;
/**
* Unit tests for {@link DtoProjectionTransformerDelegate}.
*
* @author Mark Paluch
*/
-class HqlDtoQueryTransformerUnitTests {
+class HqlDtoQueryTransformerUnitTests extends AbstractDtoQueryTransformerUnitTests {
- JpaQueryMethod method = getMethod("dtoProjection");
-
- @Test // GH-3076
- void shouldTranslateSingleProjectionToDto() {
-
- JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser.parseQuery("SELECT p from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "SELECT new org.springframework.data.jpa.repository.query.HqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar) from Person p");
+ @Override
+ JpaQueryEnhancer.HqlQueryParser parse(String query) {
+ return JpaQueryEnhancer.HqlQueryParser.parseQuery(query);
}
- @Test // GH-3076
- void shouldRewriteQueriesWithSubselect() {
-
- JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser
- .parseQuery("select u from User u left outer join u.roles r where r in (select r from Role r)");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "select new org.springframework.data.jpa.repository.query.HqlDtoQueryTransformerUnitTests$MyRecord(u.foo, u.bar) from User u left outer join u.roles r where r in (select r from Role r)");
- }
-
- @Test // GH-3076
- void shouldNotTranslateConstructorExpressionQuery() {
-
- JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser
- .parseQuery("SELECT NEW String(p) from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("SELECT NEW String(p) from Person p");
- }
-
- @Test
- void shouldTranslatePropertySelectionToDto() {
-
- JpaQueryEnhancer.HqlQueryParser parser = JpaQueryEnhancer.HqlQueryParser
- .parseQuery("SELECT p.foo, p.bar, sum(p.age) from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "SELECT new org.springframework.data.jpa.repository.query.HqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar, sum(p.age)) from Person p");
- }
-
- private JpaQueryMethod getMethod(String name, Class>... parameterTypes) {
-
- try {
- Method method = MyRepo.class.getMethod(name, parameterTypes);
- PersistenceProvider persistenceProvider = PersistenceProvider.HIBERNATE;
-
- return new JpaQueryMethod(method, new DefaultRepositoryMetadata(MyRepo.class),
- new SpelAwareProxyProjectionFactory(), persistenceProvider);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- }
-
- private HqlSortedQueryTransformer getTransformer(JpaQueryEnhancer.HqlQueryParser parser) {
+ @Override
+ ParseTreeVisitor getTransformer(JpaQueryEnhancer.HqlQueryParser parser, QueryMethod method) {
return new HqlSortedQueryTransformer(Sort.unsorted(), parser.getQueryInformation(),
method.getResultProcessor().getReturnedType());
}
- interface MyRepo extends Repository {
-
- MyRecord dtoProjection();
- }
-
- record Person(String id) {
-
- }
-
- record MyRecord(String foo, String bar) {
-
- }
}
diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlDtoQueryTransformerUnitTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlDtoQueryTransformerUnitTests.java
index d0c8fa130..a82aaf758 100644
--- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlDtoQueryTransformerUnitTests.java
+++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlDtoQueryTransformerUnitTests.java
@@ -15,101 +15,27 @@
*/
package org.springframework.data.jpa.repository.query;
-import static org.assertj.core.api.Assertions.*;
-
-import java.lang.reflect.Method;
-
-import org.junit.jupiter.api.Test;
+import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.springframework.data.domain.Sort;
-import org.springframework.data.jpa.provider.PersistenceProvider;
-import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
-import org.springframework.data.repository.Repository;
-import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
+import org.springframework.data.repository.query.QueryMethod;
/**
* Unit tests for {@link DtoProjectionTransformerDelegate}.
*
* @author Mark Paluch
*/
-class JpqlDtoQueryTransformerUnitTests {
+class JpqlDtoQueryTransformerUnitTests extends AbstractDtoQueryTransformerUnitTests {
- JpaQueryMethod method = getMethod("dtoProjection");
-
- @Test // GH-3076
- void shouldTranslateSingleProjectionToDto() {
-
- JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser.parseQuery("SELECT p from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "SELECT new org.springframework.data.jpa.repository.query.JpqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar) from Person p");
+ @Override
+ JpaQueryEnhancer.JpqlQueryParser parse(String query) {
+ return JpaQueryEnhancer.JpqlQueryParser.parseQuery(query);
}
- @Test // GH-3076
- void shouldRewriteQueriesWithSubselect() {
-
- JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser
- .parseQuery("select u from User u left outer join u.roles r where r in (select r from Role r)");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "select new org.springframework.data.jpa.repository.query.JpqlDtoQueryTransformerUnitTests$MyRecord(u.foo, u.bar) from User u left outer join u.roles r where r in (select r from Role r)");
- }
-
- @Test // GH-3076
- void shouldNotTranslateConstructorExpressionQuery() {
-
- JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser
- .parseQuery("SELECT NEW Foo(p) from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo("SELECT NEW Foo(p) from Person p");
- }
-
- @Test
- void shouldTranslatePropertySelectionToDto() {
-
- JpaQueryEnhancer.JpqlQueryParser parser = JpaQueryEnhancer.JpqlQueryParser
- .parseQuery("SELECT p.foo, p.bar, sum(p.age) from Person p");
-
- QueryTokenStream visit = getTransformer(parser).visit(parser.getContext());
-
- assertThat(QueryRenderer.TokenRenderer.render(visit)).isEqualTo(
- "SELECT new org.springframework.data.jpa.repository.query.JpqlDtoQueryTransformerUnitTests$MyRecord(p.foo, p.bar, sum(p.age)) from Person p");
- }
-
- private JpaQueryMethod getMethod(String name, Class>... parameterTypes) {
-
- try {
- Method method = MyRepo.class.getMethod(name, parameterTypes);
- PersistenceProvider persistenceProvider = PersistenceProvider.HIBERNATE;
-
- return new JpaQueryMethod(method, new DefaultRepositoryMetadata(MyRepo.class),
- new SpelAwareProxyProjectionFactory(), persistenceProvider);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
- }
-
- private JpqlSortedQueryTransformer getTransformer(JpaQueryEnhancer.JpqlQueryParser parser) {
+ @Override
+ ParseTreeVisitor getTransformer(JpaQueryEnhancer.JpqlQueryParser parser, QueryMethod method) {
return new JpqlSortedQueryTransformer(Sort.unsorted(), parser.getQueryInformation(),
method.getResultProcessor().getReturnedType());
}
- interface MyRepo extends Repository {
-
- MyRecord dtoProjection();
- }
-
- record Person(String id) {
-
- }
-
- record MyRecord(String foo, String bar) {
-
- }
}