Skip to content

Commit

Permalink
Provide more expressive exception for projections without properties.
Browse files Browse the repository at this point in the history
Closes #1813
  • Loading branch information
schauder committed Sep 24, 2024
1 parent 89093cf commit ae27e23
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<DummyProjection> 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() {

Expand Down Expand Up @@ -1428,6 +1436,8 @@ interface DummyEntityRepository extends CrudRepository<DummyEntity, Long>, Query

List<DummyProjection> findProjectedByName(String name);

<T> List<T> findDynamicProjectedByName(String name, Class<T> projection);

@Query(value = "SELECT * FROM DUMMY_ENTITY", rowMapperClass = CustomRowMapper.class)
List<DummyEntity> findAllWithCustomMapper();

Expand Down Expand Up @@ -1941,6 +1951,10 @@ interface DummyProjection {
String getName();
}

interface BrokenProjection {
String name();
}

static final class DtoProjection {
private final String name;

Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit ae27e23

Please sign in to comment.