diff --git a/pom.xml b/pom.xml
index 335f5969b8..e933d3ac2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
pom
Spring Data Relational Parent
diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml
index 84eeb178e0..1ea4218b33 100644
--- a/spring-data-jdbc-distribution/pom.xml
+++ b/spring-data-jdbc-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
../pom.xml
diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml
index 266af71b96..f902f56226 100644
--- a/spring-data-jdbc/pom.xml
+++ b/spring-data-jdbc/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-jdbc
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
Spring Data JDBC
Spring Data module for JDBC repositories.
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java
index 8351db58b7..141b5c88bc 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java
@@ -33,6 +33,7 @@
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.sql.Column;
+import org.springframework.data.relational.core.sql.EmptySelectListException;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.Expressions;
import org.springframework.data.relational.core.sql.Functions;
@@ -177,13 +178,23 @@ protected ParametrizedQuery complete(@Nullable Criteria criteria, Sort sort) {
completedBuildSelect = selectOrderBuilder.lock(this.lockMode.get().value());
}
- Select select = completedBuildSelect.build();
+ Select select = getSelect(completedBuildSelect);
String sql = SqlRenderer.create(renderContextFactory.createRenderContext()).render(select);
return new ParametrizedQuery(sql, parameterSource);
}
+ private Select getSelect(SelectBuilder.BuildSelect completedBuildSelect) {
+
+ try {
+ return completedBuildSelect.build();
+ } catch (EmptySelectListException cause) {
+ throw new IllegalStateException(
+ returnedType.getReturnedType().getName() + " does not define any properties to select", cause);
+ }
+ }
+
SelectBuilder.SelectOrdered applyOrderBy(Sort sort, RelationalPersistentEntity> entity, Table table,
SelectBuilder.SelectOrdered selectOrdered) {
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java
index 008f923208..75cb7e1e09 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java
@@ -41,7 +41,6 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationListener;
@@ -51,16 +50,7 @@
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.annotation.Id;
-import org.springframework.data.domain.Example;
-import org.springframework.data.domain.ExampleMatcher;
-import org.springframework.data.domain.Limit;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.ScrollPosition;
-import org.springframework.data.domain.Slice;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.domain.Window;
+import org.springframework.data.domain.*;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query;
@@ -572,6 +562,24 @@ public void partTreeQueryProjectionShouldReturnProjectedEntities() {
assertThat(result.get(0).getName()).isEqualTo("Entity Name");
}
+ @Test // GH-1813
+ public void partTreeQueryDynamicProjectionShouldReturnProjectedEntities() {
+
+ repository.save(createDummyEntity());
+
+ List result = repository.findDynamicProjectedByName("Entity Name", DummyProjection.class);
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).getName()).isEqualTo("Entity Name");
+ }
+
+ @Test // GH-1813
+ public void partTreeQueryDynamicProjectionWithBrokenProjectionShouldError() {
+
+ assertThatThrownBy(() -> repository.findDynamicProjectedByName("Entity Name", BrokenProjection.class))
+ .hasMessageContaining("BrokenProjection does not define any properties to select");
+ }
+
@Test // GH-971
public void pageQueryProjectionShouldReturnProjectedEntities() {
@@ -1428,6 +1436,8 @@ interface DummyEntityRepository extends CrudRepository, Query
List findProjectedByName(String name);
+ List findDynamicProjectedByName(String name, Class projection);
+
@Query(value = "SELECT * FROM DUMMY_ENTITY", rowMapperClass = CustomRowMapper.class)
List findAllWithCustomMapper();
@@ -1941,6 +1951,10 @@ interface DummyProjection {
String getName();
}
+ interface BrokenProjection {
+ String name();
+ }
+
static final class DtoProjection {
private final String name;
diff --git a/spring-data-r2dbc/pom.xml b/spring-data-r2dbc/pom.xml
index e335b56501..648460352f 100644
--- a/spring-data-r2dbc/pom.xml
+++ b/spring-data-r2dbc/pom.xml
@@ -6,7 +6,7 @@
4.0.0
spring-data-r2dbc
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
Spring Data R2DBC
Spring Data module for R2DBC
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java
index d55b34023a..6d7b386065 100644
--- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java
+++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java
@@ -27,7 +27,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
-
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -51,14 +50,12 @@
@ExtendWith(SpringExtension.class)
public class ProjectingRepositoryIntegrationTests {
- @Autowired
- private ImmutableObjectRepository repository;
+ @Autowired private ImmutableObjectRepository repository;
private JdbcTemplate jdbc;
@Configuration
- @EnableR2dbcRepositories(
- includeFilters = @ComponentScan.Filter(value = ImmutableObjectRepository.class, type = FilterType.ASSIGNABLE_TYPE),
- considerNestedRepositories = true)
+ @EnableR2dbcRepositories(includeFilters = @ComponentScan.Filter(value = ImmutableObjectRepository.class,
+ type = FilterType.ASSIGNABLE_TYPE), considerNestedRepositories = true)
static class TestConfiguration extends AbstractR2dbcConfiguration {
@Override
public ConnectionFactory connectionFactory() {
@@ -74,9 +71,7 @@ void before() {
try {
this.jdbc.execute("DROP TABLE immutable_non_null");
- }
- catch (DataAccessException e) {
- }
+ } catch (DataAccessException e) {}
this.jdbc.execute("CREATE TABLE immutable_non_null (id serial PRIMARY KEY, name varchar(255), email varchar(255))");
this.jdbc.execute("INSERT INTO immutable_non_null VALUES (42, 'Walter', 'heisenberg@the-white-family.com')");
@@ -100,8 +95,7 @@ protected ConnectionFactory createConnectionFactory() {
return H2TestSupport.createConnectionFactory();
}
- @Test
- // GH-1687
+ @Test // GH-1687
void shouldApplyProjectionDirectly() {
repository.findProjectionByEmail("heisenberg@the-white-family.com") //
@@ -111,8 +105,7 @@ void shouldApplyProjectionDirectly() {
}).verifyComplete();
}
- @Test
- // GH-1687
+ @Test // GH-1687
void shouldApplyEntityQueryProjectionDirectly() {
repository.findAllByEmail("heisenberg@the-white-family.com") //
@@ -134,8 +127,7 @@ interface ImmutableObjectRepository extends ReactiveCrudRepository4.0.0
spring-data-relational
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
Spring Data Relational
Spring Data Relational support
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-relational-parent
- 3.4.0-SNAPSHOT
+ 3.4.0-1813-improve-error-message-SNAPSHOT
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/EmptySelectListException.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/EmptySelectListException.java
new file mode 100644
index 0000000000..2b5abf9a16
--- /dev/null
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/EmptySelectListException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 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.relational.core.sql;
+
+/**
+ * Exception denoting the absence of a select list from a query.
+ *
+ * @author Jens Schauder
+ * @since 3.4
+ */
+public class EmptySelectListException extends IllegalStateException {
+ public EmptySelectListException() {
+ super("SELECT does not declare a select list");
+ }
+}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java
index cdaef37344..89c09c85ee 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java
@@ -54,7 +54,7 @@ private void doValidate(Select select) {
select.visit(this);
if (selectFieldCount == 0) {
- throw new IllegalStateException("SELECT does not declare a select list");
+ throw new EmptySelectListException();
}
for (TableLike table : requiredBySelect) {