Skip to content

Commit 36fb86a

Browse files
committed
Update to core r2dbc support
This commit adapts the auto-configuration for the new core r2dbc support in Spring Framework and provides auto-configuration for R2dbcEntityOperations. Closes gh-22708
1 parent 2b99532 commit 36fb86a

File tree

18 files changed

+154
-46
lines changed

18 files changed

+154
-46
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/r2dbc/R2dbcDataAutoConfiguration.java

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import java.util.Collections;
2121
import java.util.List;
2222

23-
import io.r2dbc.spi.ConnectionFactory;
24-
2523
import org.springframework.beans.factory.ObjectProvider;
2624
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2725
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -34,15 +32,14 @@
3432
import org.springframework.data.convert.CustomConversions;
3533
import org.springframework.data.r2dbc.convert.MappingR2dbcConverter;
3634
import org.springframework.data.r2dbc.convert.R2dbcCustomConversions;
37-
import org.springframework.data.r2dbc.core.DatabaseClient;
3835
import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy;
36+
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
3937
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
4038
import org.springframework.data.r2dbc.dialect.DialectResolver;
4139
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
4240
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
43-
import org.springframework.data.r2dbc.support.R2dbcExceptionSubclassTranslator;
44-
import org.springframework.data.r2dbc.support.R2dbcExceptionTranslator;
4541
import org.springframework.data.relational.core.mapping.NamingStrategy;
42+
import org.springframework.r2dbc.core.DatabaseClient;
4643

4744
/**
4845
* {@link EnableAutoConfiguration Auto-configuration} for {@link DatabaseClient}.
@@ -52,24 +49,21 @@
5249
* @since 2.3.0
5350
*/
5451
@Configuration(proxyBeanMethods = false)
55-
@ConditionalOnClass(DatabaseClient.class)
56-
@ConditionalOnMissingBean(DatabaseClient.class)
57-
@ConditionalOnSingleCandidate(ConnectionFactory.class)
52+
@ConditionalOnClass({ DatabaseClient.class, R2dbcEntityTemplate.class })
53+
@ConditionalOnSingleCandidate(DatabaseClient.class)
5854
@AutoConfigureAfter(R2dbcAutoConfiguration.class)
5955
public class R2dbcDataAutoConfiguration {
6056

61-
private final ConnectionFactory connectionFactory;
57+
private final DatabaseClient databaseClient;
6258

63-
public R2dbcDataAutoConfiguration(ConnectionFactory connectionFactory) {
64-
this.connectionFactory = connectionFactory;
59+
public R2dbcDataAutoConfiguration(DatabaseClient databaseClient) {
60+
this.databaseClient = databaseClient;
6561
}
6662

6763
@Bean
6864
@ConditionalOnMissingBean
69-
public DatabaseClient r2dbcDatabaseClient(ReactiveDataAccessStrategy dataAccessStrategy,
70-
R2dbcExceptionTranslator exceptionTranslator) {
71-
return DatabaseClient.builder().connectionFactory(this.connectionFactory).dataAccessStrategy(dataAccessStrategy)
72-
.exceptionTranslator(exceptionTranslator).build();
65+
public R2dbcEntityTemplate r2dbcEntityTemplate(ReactiveDataAccessStrategy reactiveDataAccessStrategy) {
66+
return new R2dbcEntityTemplate(this.databaseClient, reactiveDataAccessStrategy);
7367
}
7468

7569
@Bean
@@ -87,24 +81,22 @@ public R2dbcMappingContext r2dbcMappingContext(ObjectProvider<NamingStrategy> na
8781
public ReactiveDataAccessStrategy reactiveDataAccessStrategy(R2dbcMappingContext mappingContext,
8882
R2dbcCustomConversions r2dbcCustomConversions) {
8983
MappingR2dbcConverter converter = new MappingR2dbcConverter(mappingContext, r2dbcCustomConversions);
90-
return new DefaultReactiveDataAccessStrategy(DialectResolver.getDialect(this.connectionFactory), converter);
84+
return new DefaultReactiveDataAccessStrategy(getDialect(), converter);
9185
}
9286

9387
@Bean
9488
@ConditionalOnMissingBean
9589
public R2dbcCustomConversions r2dbcCustomConversions() {
96-
R2dbcDialect dialect = DialectResolver.getDialect(this.connectionFactory);
90+
R2dbcDialect dialect = getDialect();
9791
List<Object> converters = new ArrayList<>(dialect.getConverters());
9892
converters.addAll(R2dbcCustomConversions.STORE_CONVERTERS);
9993
return new R2dbcCustomConversions(
10094
CustomConversions.StoreConversions.of(dialect.getSimpleTypeHolder(), converters),
10195
Collections.emptyList());
10296
}
10397

104-
@Bean
105-
@ConditionalOnMissingBean
106-
public R2dbcExceptionTranslator r2dbcExceptionTranslator() {
107-
return new R2dbcExceptionSubclassTranslator();
98+
private R2dbcDialect getDialect() {
99+
return DialectResolver.getDialect(this.databaseClient.getConnectionFactory());
108100
}
109101

110102
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/r2dbc/R2dbcRepositoriesAutoConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2727
import org.springframework.context.annotation.Configuration;
2828
import org.springframework.context.annotation.Import;
29-
import org.springframework.data.r2dbc.core.DatabaseClient;
3029
import org.springframework.data.r2dbc.repository.R2dbcRepository;
3130
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
3231
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean;
32+
import org.springframework.r2dbc.core.DatabaseClient;
3333

3434
/**
3535
* {@link EnableAutoConfiguration Auto-configuration} for Spring Data R2DBC Repositories.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.r2dbc;
18+
19+
import io.r2dbc.spi.ConnectionFactory;
20+
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.r2dbc.core.DatabaseClient;
27+
28+
/**
29+
* Configuration of the R2DBC infrastructure based on a {@link ConnectionFactory}.
30+
*
31+
* @author Stephane Nicoll
32+
*/
33+
@Configuration(proxyBeanMethods = false)
34+
@ConditionalOnClass(DatabaseClient.class)
35+
@ConditionalOnSingleCandidate(ConnectionFactory.class)
36+
class ConnectionFactoryDependentConfiguration {
37+
38+
@Bean
39+
@ConditionalOnMissingBean
40+
DatabaseClient r2dbcDatabaseClient(ConnectionFactory connectionFactory) {
41+
return DatabaseClient.builder().connectionFactory(connectionFactory).build();
42+
}
43+
44+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
@ConditionalOnClass(ConnectionFactory.class)
3838
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
3939
@EnableConfigurationProperties(R2dbcProperties.class)
40-
@Import({ ConnectionFactoryConfigurations.Pool.class, ConnectionFactoryConfigurations.Generic.class })
40+
@Import({ ConnectionFactoryConfigurations.Pool.class, ConnectionFactoryConfigurations.Generic.class,
41+
ConnectionFactoryDependentConfiguration.class })
4142
public class R2dbcAutoConfiguration {
4243

4344
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.boot.autoconfigure.data.r2dbc;
17+
package org.springframework.boot.autoconfigure.r2dbc;
1818

1919
import io.r2dbc.spi.ConnectionFactory;
2020

@@ -28,7 +28,7 @@
2828
import org.springframework.context.annotation.Bean;
2929
import org.springframework.context.annotation.Configuration;
3030
import org.springframework.core.Ordered;
31-
import org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager;
31+
import org.springframework.r2dbc.connection.R2dbcTransactionManager;
3232
import org.springframework.transaction.ReactiveTransactionManager;
3333

3434
/**

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfigura
5757
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
5858
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
5959
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
60-
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
6160
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
6261
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
6362
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
@@ -106,6 +105,7 @@ org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration,\
106105
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
107106
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
108107
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
108+
org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
109109
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
110110
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
111111
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/r2dbc/R2dbcDataAutoConfigurationTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.springframework.boot.autoconfigure.AutoConfigurations;
2222
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
2323
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
24-
import org.springframework.data.r2dbc.core.DatabaseClient;
24+
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
2525

2626
import static org.assertj.core.api.Assertions.assertThat;
2727

@@ -36,8 +36,8 @@ class R2dbcDataAutoConfigurationTests {
3636
.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class, R2dbcDataAutoConfiguration.class));
3737

3838
@Test
39-
void databaseClientExists() {
40-
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(DatabaseClient.class));
39+
void r2dbcEntityTemplateIsConfigured() {
40+
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(R2dbcEntityTemplate.class));
4141
}
4242

4343
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/r2dbc/R2dbcRepositoriesAutoConfigurationTests.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,21 @@
2323
import org.springframework.beans.factory.annotation.Autowired;
2424
import org.springframework.boot.autoconfigure.AutoConfigurations;
2525
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
26+
import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage;
2627
import org.springframework.boot.autoconfigure.data.r2dbc.city.City;
2728
import org.springframework.boot.autoconfigure.data.r2dbc.city.CityRepository;
2829
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
30+
import org.springframework.boot.test.context.FilteredClassLoader;
2931
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
3032
import org.springframework.context.annotation.Configuration;
3133
import org.springframework.core.io.DefaultResourceLoader;
3234
import org.springframework.core.io.Resource;
3335
import org.springframework.core.io.ResourceLoader;
34-
import org.springframework.data.r2dbc.connectionfactory.init.ResourceDatabasePopulator;
35-
import org.springframework.data.r2dbc.core.DatabaseClient;
3636
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
3737
import org.springframework.data.r2dbc.repository.config.R2dbcRepositoryConfigurationExtension;
3838
import org.springframework.data.repository.Repository;
39+
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
40+
import org.springframework.r2dbc.core.DatabaseClient;
3941

4042
import static org.assertj.core.api.Assertions.assertThat;
4143

@@ -58,6 +60,7 @@ void backsOffWithNoConnectionFactory() {
5860
@Test
5961
void backsOffWithNoDatabaseClientOperations() {
6062
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
63+
.withClassLoader(new FilteredClassLoader("org.springframework.r2dbc"))
6164
.withUserConfiguration(TestConfiguration.class).run((context) -> {
6265
assertThat(context).doesNotHaveBean(DatabaseClient.class);
6366
assertThat(context).doesNotHaveBean(R2dbcRepositoryConfigurationExtension.class);
@@ -105,7 +108,7 @@ void initializeDatabase(ConnectionFactory connectionFactory) {
105108
ResourceLoader resourceLoader = new DefaultResourceLoader();
106109
Resource[] scripts = new Resource[] { resourceLoader.getResource("classpath:data-city-schema.sql"),
107110
resourceLoader.getResource("classpath:city.sql") };
108-
new ResourceDatabasePopulator(scripts).execute(connectionFactory).block();
111+
new ResourceDatabasePopulator(scripts).populate(connectionFactory).block();
109112
}
110113

111114
}
@@ -117,6 +120,7 @@ static class TestConfiguration {
117120
}
118121

119122
@Configuration(proxyBeanMethods = false)
123+
@TestAutoConfigurationPackage(EmptyDataPackage.class)
120124
static class EmptyConfiguration {
121125

122126
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/r2dbc/R2dbcAutoConfigurationTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
4040
import org.springframework.context.annotation.Bean;
4141
import org.springframework.context.annotation.Configuration;
42+
import org.springframework.r2dbc.core.DatabaseClient;
4243

4344
import static org.assertj.core.api.Assertions.assertThat;
4445

@@ -240,6 +241,24 @@ void configureWithDataSourceAutoConfigurationDoesNotCreateDataSource() {
240241
.doesNotHaveBean(DataSource.class));
241242
}
242243

244+
@Test
245+
void databaseClientIsConfigured() {
246+
this.contextRunner.withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName())
247+
.run((context) -> {
248+
assertThat(context).hasSingleBean(ConnectionFactory.class).hasSingleBean(DatabaseClient.class);
249+
assertThat(context.getBean(DatabaseClient.class).getConnectionFactory())
250+
.isSameAs(context.getBean(ConnectionFactory.class));
251+
});
252+
}
253+
254+
@Test
255+
void databaseClientBacksOffIfSpringR2dbcIsNotAvailable() {
256+
this.contextRunner.withClassLoader(new FilteredClassLoader("org.springframework.r2dbc"))
257+
.withPropertyValues("spring.r2dbc.url:r2dbc:h2:mem:///" + randomDatabaseName())
258+
.run((context) -> assertThat(context).hasSingleBean(ConnectionFactory.class)
259+
.doesNotHaveBean(DatabaseClient.class));
260+
}
261+
243262
private String randomDatabaseName() {
244263
return "testdb-" + UUID.randomUUID();
245264
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.boot.autoconfigure.data.r2dbc;
17+
package org.springframework.boot.autoconfigure.r2dbc;
1818

1919
import io.r2dbc.spi.Connection;
2020
import io.r2dbc.spi.ConnectionFactory;
@@ -27,7 +27,6 @@
2727
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
2828
import org.springframework.context.annotation.Bean;
2929
import org.springframework.context.annotation.Configuration;
30-
import org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager;
3130
import org.springframework.transaction.ReactiveTransactionManager;
3231
import org.springframework.transaction.annotation.EnableTransactionManagement;
3332
import org.springframework.transaction.annotation.Transactional;
@@ -39,7 +38,7 @@
3938
import static org.mockito.Mockito.mock;
4039

4140
/**
42-
* Tests for {@link R2dbcTransactionManager}.
41+
* Tests for {@link R2dbcTransactionManagerAutoConfiguration}.
4342
*
4443
* @author Mark Paluch
4544
* @author Oliver Drotbohm
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.r2dbc;
18+
19+
import io.r2dbc.pool.ConnectionPool;
20+
import io.r2dbc.spi.ConnectionFactory;
21+
22+
import org.springframework.boot.autoconfigure.r2dbc.SimpleConnectionFactoryProvider.SimpleTestConnectionFactory;
23+
import org.springframework.r2dbc.core.binding.BindMarkersFactory;
24+
import org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver.BindMarkerFactoryProvider;
25+
26+
/**
27+
* Simple {@link BindMarkerFactoryProvider} for {@link SimpleConnectionFactoryProvider}.
28+
*
29+
* @author Stephane Nicoll
30+
*/
31+
public class SimpleBindMarkerFactoryProvider implements BindMarkerFactoryProvider {
32+
33+
@Override
34+
public BindMarkersFactory getBindMarkers(ConnectionFactory connectionFactory) {
35+
if (unwrapIfNecessary(connectionFactory) instanceof SimpleTestConnectionFactory) {
36+
return BindMarkersFactory.anonymous("?");
37+
}
38+
return null;
39+
}
40+
41+
private ConnectionFactory unwrapIfNecessary(ConnectionFactory connectionFactory) {
42+
if (connectionFactory instanceof ConnectionPool) {
43+
return ((ConnectionPool) connectionFactory).unwrap();
44+
}
45+
return connectionFactory;
46+
}
47+
48+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.springframework.r2dbc.core.binding.BindMarkersFactoryResolver$BindMarkerFactoryProvider=org.springframework.boot.autoconfigure.r2dbc.SimpleBindMarkerFactoryProvider

spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1972,7 +1972,7 @@ You can tune that behavior by setting `spring.datasource.continue-on-error`.
19721972
=== Initialize a Database Using R2DBC
19731973
If you are using R2DBC, the regular `DataSource` auto-configuration backs off so none of the options described above can be used.
19741974

1975-
If you are using Spring Data R2DBC, you can initialize the database on startup using SQL scripts as shown in the following example:
1975+
You can initialize the database on startup using SQL scripts as shown in the following example:
19761976

19771977
[source,java,indent=0]
19781978
----

spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4215,7 +4215,7 @@ If you want to make sure that each context has a separate embedded database, you
42154215

42164216
[[boot-features-r2dbc-using-database-client]]
42174217
==== Using DatabaseClient
4218-
Spring Data's `DatabaseClient` class is auto-configured, and you can `@Autowire` it directly into your own beans, as shown in the following example:
4218+
A `DatabaseClient` bean is auto-configured, and you can `@Autowire` it directly into your own beans, as shown in the following example:
42194219

42204220
[source,java,indent=0]
42214221
----

0 commit comments

Comments
 (0)