From 430d98450364a384d1eff418369917f38ecf11b9 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 14 May 2019 09:35:46 +0200 Subject: [PATCH] #109 - Fix simple type conversion for projection queries. We now correctly consider built-in converters for simple types that are read through DatabaseClient. Simple type projections typically select a single column and expect a result stream of simple values such as selecting a count and retrieving a Mono. --- .../DefaultReactiveDataAccessStrategy.java | 3 +- .../data/r2dbc/config/H2IntegrationTests.java | 120 ++++++++++++++++++ ...bstractDatabaseClientIntegrationTests.java | 25 +++- 3 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/springframework/data/r2dbc/config/H2IntegrationTests.java diff --git a/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java b/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java index 841c6c1..5e135eb 100644 --- a/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java @@ -76,7 +76,8 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra Assert.notNull(dialect, "Dialect must not be null"); R2dbcCustomConversions customConversions = new R2dbcCustomConversions( - StoreConversions.of(dialect.getSimpleTypeHolder()), Collections.emptyList()); + StoreConversions.of(dialect.getSimpleTypeHolder(), R2dbcCustomConversions.STORE_CONVERTERS), + Collections.emptyList()); RelationalMappingContext context = new RelationalMappingContext(); context.setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); diff --git a/src/test/java/org/springframework/data/r2dbc/config/H2IntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/config/H2IntegrationTests.java new file mode 100644 index 0000000..d045065 --- /dev/null +++ b/src/test/java/org/springframework/data/r2dbc/config/H2IntegrationTests.java @@ -0,0 +1,120 @@ +/* + * Copyright 2019 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.r2dbc.config; + +import io.r2dbc.spi.ConnectionFactory; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.dao.DataAccessException; +import org.springframework.data.annotation.Id; +import org.springframework.data.r2dbc.core.DatabaseClient; +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; +import org.springframework.data.r2dbc.repository.query.Query; +import org.springframework.data.r2dbc.testing.H2TestSupport; +import org.springframework.data.relational.core.mapping.Table; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration test for {@link DatabaseClient} and repositories using H2. + * + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@ContextConfiguration +public class H2IntegrationTests { + + private JdbcTemplate jdbc = new JdbcTemplate(H2TestSupport.createDataSource()); + + @Autowired DatabaseClient databaseClient; + @Autowired H2Repository repository; + + @Before + public void before() { + + try { + jdbc.execute("DROP TABLE legoset"); + } catch (DataAccessException e) {} + jdbc.execute(H2TestSupport.CREATE_TABLE_LEGOSET); + } + + @Test // gh-109 + public void shouldSelectCountWithDatabaseClient() { + + jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)"); + + databaseClient.execute().sql("SELECT COUNT(*) FROM legoset") // + .as(Long.class) // + .fetch() // + .all() // + .as(StepVerifier::create) // + .expectNext(1L) // + .verifyComplete(); + } + + @Test // gh-109 + public void shouldSelectCountWithRepository() { + + jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)"); + + repository.selectCount() // + .as(StepVerifier::create) // + .expectNext(1L) // + .verifyComplete(); + } + + @Configuration + @EnableR2dbcRepositories(considerNestedRepositories = true, + includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = H2Repository.class), + basePackageClasses = H2Repository.class) + static class H2Configuration extends AbstractR2dbcConfiguration { + + @Override + public ConnectionFactory connectionFactory() { + return H2TestSupport.createConnectionFactory(); + } + } + + interface H2Repository extends ReactiveCrudRepository { + + @Query("SELECT COUNT(*) FROM legoset") + Mono selectCount(); + } + + @Data + @Table("legoset") + @AllArgsConstructor + @NoArgsConstructor + static class LegoSet { + @Id Integer id; + String name; + Integer manual; + } +} diff --git a/src/test/java/org/springframework/data/r2dbc/core/AbstractDatabaseClientIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/core/AbstractDatabaseClientIntegrationTests.java index 595f7c8..62b5ea2 100644 --- a/src/test/java/org/springframework/data/r2dbc/core/AbstractDatabaseClientIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/core/AbstractDatabaseClientIntegrationTests.java @@ -33,7 +33,6 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.annotation.Id; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; -import org.springframework.data.r2dbc.core.DatabaseClient; import org.springframework.data.r2dbc.query.Criteria; import org.springframework.data.r2dbc.query.Update; import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport; @@ -340,6 +339,30 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr .verifyComplete(); } + @Test // gh-109 + public void selectSimpleTypeProjection() { + + jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)"); + + DatabaseClient databaseClient = DatabaseClient.create(connectionFactory); + + databaseClient.execute().sql("SELECT COUNT(*) FROM legoset") // + .as(Long.class) // + .fetch() // + .all() // + .as(StepVerifier::create) // + .expectNext(1L) // + .verifyComplete(); + + databaseClient.execute().sql("SELECT name FROM legoset") // + .as(String.class) // + .fetch() // + .one() // + .as(StepVerifier::create) // + .expectNext("SCHAUFELRADBAGGER") // + .verifyComplete(); + } + @Test // gh-8 public void selectWithCriteria() {