Skip to content

Commit 960ab15

Browse files
committed
Document how to initialize a database with R2DBC
This commit adds a section to the reference guide on how to initialize a database using R2DBC. 2 smoke tests are also added to validate this behaviour with Flyway and Liquibase. Closes gh-20742
1 parent 12123d4 commit 960ab15

File tree

16 files changed

+554
-1
lines changed

16 files changed

+554
-1
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1937,7 +1937,7 @@ It is a Hibernate feature (and has nothing to do with Spring).
19371937

19381938

19391939
[[howto-initialize-a-database-using-spring-jdbc]]
1940-
=== Initialize a Database
1940+
=== Initialize a Database using basic SQL scripts
19411941
Spring Boot can automatically create the schema (DDL scripts) of your `DataSource` and initialize it (DML scripts).
19421942
It loads SQL from the standard root classpath locations: `schema.sql` and `data.sql`, respectively.
19431943
In addition, Spring Boot processes the `schema-$\{platform}.sql` and `data-$\{platform}.sql` files (if present), where `platform` is the value of `spring.datasource.platform`.
@@ -1965,6 +1965,24 @@ Make sure to disable `spring.jpa.hibernate.ddl-auto` if you use `schema.sql`.
19651965

19661966

19671967

1968+
[[howto-initialize-a-database-using-r2dc]]
1969+
=== Initialize a Database Using R2DBC
1970+
If you are using R2DBC, the regular `DataSource` auto-configuration backs off so none of the options described above can be used.
1971+
1972+
If you are using Spring Data R2DBC, you can initialize the database on startup using simple SQL scripts as shown in the following example:
1973+
1974+
[source,java,indent=0]
1975+
----
1976+
include::{code-examples}/r2dbc/R2dbcDatabaseInitializationExample.java[tag=configuration]
1977+
----
1978+
1979+
Alternatively, you can configure either <<howto-execute-flyway-database-migrations-on-startup,Flyway>> or <<howto-execute-liquibase-database-migrations-on-startup,Liquibase>> to configure a `DataSource` for you for the duration of the migration.
1980+
Both these libraries offer properties to set the `url`, `username` and `password` of the database to migrate.
1981+
1982+
NOTE: When choosing this option, `org.springframework:spring-jdbc` is still a required dependency.
1983+
1984+
1985+
19681986
[[howto-initialize-a-spring-batch-database]]
19691987
=== Initialize a Spring Batch Database
19701988
If you use Spring Batch, it comes pre-packaged with SQL initialization scripts for most popular database platforms.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.docs.r2dbc;
18+
19+
import io.r2dbc.spi.ConnectionFactory;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.core.io.DefaultResourceLoader;
24+
import org.springframework.core.io.Resource;
25+
import org.springframework.core.io.ResourceLoader;
26+
import org.springframework.data.r2dbc.connectionfactory.init.ResourceDatabasePopulator;
27+
28+
/**
29+
* Example configuration for initializing a database using R2DBC.
30+
*
31+
* @author Stephane Nicoll
32+
*/
33+
public class R2dbcDatabaseInitializationExample {
34+
35+
// tag::configuration[]
36+
@Configuration(proxyBeanMethods = false)
37+
static class DatabaseInitializationConfiguration {
38+
39+
@Autowired
40+
void initializeDatabase(ConnectionFactory connectionFactory) {
41+
ResourceLoader resourceLoader = new DefaultResourceLoader();
42+
Resource[] scripts = new Resource[] { resourceLoader.getResource("classpath:schema.sql"),
43+
resourceLoader.getResource("classpath:data.sql") };
44+
new ResourceDatabasePopulator(scripts).execute(connectionFactory).block();
45+
}
46+
47+
}
48+
// end::configuration[]
49+
50+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
plugins {
2+
id "java"
3+
id "org.springframework.boot.conventions"
4+
}
5+
6+
description = "Spring Boot Data R2DBC with Flyway smoke test"
7+
8+
dependencies {
9+
implementation(platform(project(":spring-boot-project:spring-boot-parent")))
10+
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-data-r2dbc"))
11+
12+
runtimeOnly("io.r2dbc:r2dbc-postgresql")
13+
runtimeOnly("org.flywaydb:flyway-core")
14+
runtimeOnly("org.postgresql:postgresql")
15+
runtimeOnly("org.springframework:spring-jdbc")
16+
17+
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
18+
testImplementation("io.projectreactor:reactor-test")
19+
testImplementation("org.testcontainers:junit-jupiter")
20+
testImplementation("org.testcontainers:postgresql")
21+
22+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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 smoketest.data.r2dbc;
18+
19+
import org.springframework.data.annotation.Id;
20+
21+
public class City {
22+
23+
@Id
24+
private Long id;
25+
26+
private String name;
27+
28+
private String state;
29+
30+
private String country;
31+
32+
protected City() {
33+
}
34+
35+
public City(String name, String country) {
36+
this.name = name;
37+
this.country = country;
38+
}
39+
40+
public Long getId() {
41+
return this.id;
42+
}
43+
44+
public String getName() {
45+
return this.name;
46+
}
47+
48+
public String getState() {
49+
return this.state;
50+
}
51+
52+
public String getCountry() {
53+
return this.country;
54+
}
55+
56+
@Override
57+
public String toString() {
58+
return getName() + "," + getState() + "," + getCountry();
59+
}
60+
61+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 smoketest.data.r2dbc;
18+
19+
import reactor.core.publisher.Flux;
20+
21+
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
22+
23+
public interface CityRepository extends ReactiveCrudRepository<City, Long> {
24+
25+
Flux<City> findByState(String state);
26+
27+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 smoketest.data.r2dbc;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
22+
@SpringBootApplication
23+
public class SampleR2dbcFlywayApplication {
24+
25+
public static void main(String[] args) {
26+
SpringApplication.run(SampleR2dbcFlywayApplication.class, args);
27+
}
28+
29+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
spring.r2dbc.url=r2dbc:postgresql://user:secret@localhost/test_flyway
2+
3+
spring.flyway.url=jdbc:postgresql://localhost/test_flyway
4+
spring.flyway.user=user
5+
spring.flyway.password=secret
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CREATE TABLE CITY (
2+
id INTEGER PRIMARY KEY,
3+
name VARCHAR(30),
4+
state VARCHAR(30),
5+
country VARCHAR(30)
6+
);
7+
8+
INSERT INTO CITY (ID, NAME, STATE, COUNTRY) values (2000, 'Washington', 'DC', 'US');
9+
INSERT INTO CITY (ID, NAME, STATE, COUNTRY) values (2001, 'San Francisco', 'CA', 'US');
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 smoketest.data.r2dbc;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.testcontainers.containers.PostgreSQLContainer;
21+
import org.testcontainers.junit.jupiter.Container;
22+
import org.testcontainers.junit.jupiter.Testcontainers;
23+
import reactor.test.StepVerifier;
24+
25+
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
27+
import org.springframework.test.context.DynamicPropertyRegistry;
28+
import org.springframework.test.context.DynamicPropertySource;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
32+
/**
33+
* Tests for {@link CityRepository}.
34+
*/
35+
@Testcontainers(disabledWithoutDocker = true)
36+
@DataR2dbcTest
37+
class CityRepositoryTests {
38+
39+
@Container
40+
static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>().withDatabaseName("test_flyway");
41+
42+
@DynamicPropertySource
43+
static void postgresqlProperties(DynamicPropertyRegistry registry) {
44+
registry.add("spring.r2dbc.url", CityRepositoryTests::r2dbcUrl);
45+
registry.add("spring.r2dbc.username", postgresql::getUsername);
46+
registry.add("spring.r2dbc.password", postgresql::getPassword);
47+
48+
// configure flyway to use the same database
49+
registry.add("spring.flyway.url", postgresql::getJdbcUrl);
50+
registry.add("spring.flyway.user", postgresql::getUsername);
51+
registry.add("spring.flyway.password", postgresql::getPassword);
52+
}
53+
54+
@Autowired
55+
private CityRepository repository;
56+
57+
@Test
58+
void databaseHasBeenInitialized() {
59+
StepVerifier.create(this.repository.findByState("DC").filter((city) -> city.getName().equals("Washington")))
60+
.consumeNextWith((city) -> assertThat(city.getId()).isNotNull()).verifyComplete();
61+
}
62+
63+
private static String r2dbcUrl() {
64+
return String.format("r2dbc:postgresql://%s:%s/%s", postgresql.getContainerIpAddress(),
65+
postgresql.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), postgresql.getDatabaseName());
66+
}
67+
68+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
plugins {
2+
id "java"
3+
id "org.springframework.boot.conventions"
4+
}
5+
6+
description = "Spring Boot Data R2DBC with Liquibase smoke test"
7+
8+
dependencies {
9+
implementation(platform(project(":spring-boot-project:spring-boot-parent")))
10+
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-data-r2dbc"))
11+
12+
runtimeOnly("io.r2dbc:r2dbc-postgresql")
13+
runtimeOnly("org.liquibase:liquibase-core")
14+
runtimeOnly("org.postgresql:postgresql")
15+
runtimeOnly("org.springframework:spring-jdbc")
16+
17+
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))
18+
testImplementation("io.projectreactor:reactor-test")
19+
testImplementation("org.testcontainers:junit-jupiter")
20+
testImplementation("org.testcontainers:postgresql")
21+
22+
}

0 commit comments

Comments
 (0)