Skip to content

Commit 3af85d0

Browse files
authored
Add a Cloud Spanner with Spring Data R2DBC sample app (GoogleCloudPlatform#5262)
1 parent b163004 commit 3af85d0

File tree

8 files changed

+531
-0
lines changed

8 files changed

+531
-0
lines changed

spanner/r2dbc/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Cloud Spanner R2DBC Example
2+
3+
This sample application demonstrates using Spring Data R2DBC with [Google Cloud Spanner](https://cloud.google.com/spanner/).
4+
5+
## Maven
6+
This sample uses the [Apache Maven][maven] build system. Before getting started, be
7+
sure to [download][maven-download] and [install][maven-install] it. When you use
8+
Maven as described here, it will automatically download the needed client
9+
libraries.
10+
11+
[maven]: https://maven.apache.org
12+
[maven-download]: https://maven.apache.org/download.cgi
13+
[maven-install]: https://maven.apache.org/install.html
14+
15+
## Setup
16+
17+
1. Follow the set-up instructions in [the documentation](https://cloud.google.com/java/docs/setup).
18+
19+
2. Enable APIs for your project.
20+
[Click here](https://console.cloud.google.com/flows/enableapi?apiid=spanner.googleapis.com&showconfirmation=true)
21+
to visit Cloud Platform Console and enable the Google Cloud Spanner API.
22+
23+
3. Create a Cloud Spanner instance and database via the Cloud Plaform Console's
24+
[Cloud Spanner section](http://console.cloud.google.com/spanner).
25+
26+
4. Enable application default credentials by running the command `gcloud auth application-default login`.
27+
28+
## Run the Example
29+
30+
1. Set up the following environment variables to help the application locate your database:
31+
32+
````
33+
export project=[PROJECT]
34+
export instance=[INSTANCE]
35+
export database=[DATABASE]
36+
````
37+
38+
2. Then run the application from command line, after switching to this directory:
39+
40+
````
41+
mvn spring-boot:run
42+
````
43+
44+
3. Go to http://localhost:8080/index.html and experiment with it.
45+
You'll be able to create and drop a simple table called `NAMES`, containing two columns: a unique identifier (`UUID`) and a single data column called `NAME`.
46+
47+
All functionality is done through the Spring Data objects that were automatically configured by Spring Boot.

spanner/r2dbc/pom.xml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>org.example</groupId>
8+
<artifactId>cloud-spanner-r2dbc</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
11+
<properties>
12+
<maven.compiler.target>1.8</maven.compiler.target>
13+
<maven.compiler.source>1.8</maven.compiler.source>
14+
<spring-boot.version>2.4.5</spring-boot.version>
15+
</properties>
16+
17+
<!--
18+
The parent pom defines common style checks and testing strategies for our samples.
19+
Removing or replacing it should not affect the execution of the samples in anyway.
20+
-->
21+
<parent>
22+
<groupId>com.google.cloud.samples</groupId>
23+
<artifactId>shared-configuration</artifactId>
24+
<version>1.0.22</version>
25+
</parent>
26+
27+
<dependencies>
28+
29+
<!-- [START spanner_spring_data_r2dbc_dependency] -->
30+
<dependency>
31+
<groupId>com.google.cloud</groupId>
32+
<artifactId>cloud-spanner-spring-data-r2dbc</artifactId>
33+
<version>0.5.0</version>
34+
</dependency>
35+
<!-- [END spanner_spring_data_r2dbc_dependency] -->
36+
37+
<!-- The driver (cloud-spanner-r2dbc) dependency is actually redundant since the previous
38+
module transitively imports it. The dependency is there for documentation. -->
39+
40+
<!-- [START spanner_r2dbc_dependency] -->
41+
<dependency>
42+
<groupId>com.google.cloud</groupId>
43+
<artifactId>cloud-spanner-r2dbc</artifactId>
44+
<version>0.5.0</version>
45+
</dependency>
46+
<!-- [END spanner_r2dbc_dependency] -->
47+
48+
<!-- Unrelated to R2DBC; Spring Webflux is a web application framework -->
49+
<dependency>
50+
<groupId>org.springframework.boot</groupId>
51+
<artifactId>spring-boot-starter-webflux</artifactId>
52+
<version>${spring-boot.version}</version>
53+
</dependency>
54+
55+
<!-- Test dependencies -->
56+
<dependency>
57+
<groupId>junit</groupId>
58+
<artifactId>junit</artifactId>
59+
<version>4.13.2</version>
60+
<scope>test</scope>
61+
</dependency>
62+
63+
<dependency>
64+
<groupId>org.springframework.boot</groupId>
65+
<artifactId>spring-boot-starter-test</artifactId>
66+
<version>${spring-boot.version}</version>
67+
<scope>test</scope>
68+
</dependency>
69+
70+
</dependencies>
71+
72+
<build>
73+
<plugins>
74+
<plugin>
75+
<groupId>org.springframework.boot</groupId>
76+
<artifactId>spring-boot-maven-plugin</artifactId>
77+
<version>${spring-boot.version}</version>
78+
</plugin>
79+
</plugins>
80+
</build>
81+
82+
</project>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2021 Google Inc.
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+
* http://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 com.example.spanner.r2dbc;
18+
19+
import org.springframework.data.annotation.Id;
20+
import org.springframework.data.relational.core.mapping.Column;
21+
import org.springframework.data.relational.core.mapping.Table;
22+
23+
@Table("NAMES")
24+
public class Name {
25+
26+
@Id
27+
@Column("UUID")
28+
private String uuid;
29+
30+
@Column("NAME")
31+
private String name;
32+
33+
public Name() {
34+
// needed for deserialization
35+
}
36+
37+
public Name(String uuid, String name) {
38+
this.uuid = uuid;
39+
this.name = name;
40+
}
41+
42+
public String getUuid() {
43+
return uuid;
44+
}
45+
46+
public String getName() {
47+
return name;
48+
}
49+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2021 Google Inc.
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+
* http://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 com.example.spanner.r2dbc;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
22+
@SpringBootApplication
23+
public class R2dbcSampleApplication {
24+
25+
public static void main(String[] args) {
26+
SpringApplication.run(R2dbcSampleApplication.class, args);
27+
}
28+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2021 Google Inc.
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+
* http://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 com.example.spanner.r2dbc;
18+
19+
import static org.springframework.data.relational.core.query.Criteria.where;
20+
import static org.springframework.data.relational.core.query.Query.query;
21+
22+
import java.util.UUID;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
25+
import org.springframework.web.bind.annotation.GetMapping;
26+
import org.springframework.web.bind.annotation.PostMapping;
27+
import org.springframework.web.bind.annotation.RequestBody;
28+
import org.springframework.web.bind.annotation.RestController;
29+
import reactor.core.publisher.Flux;
30+
import reactor.core.publisher.Mono;
31+
32+
@RestController
33+
public class WebController {
34+
35+
@Autowired
36+
R2dbcEntityTemplate r2dbcEntityTemplate;
37+
38+
@PostMapping("createTable")
39+
public Mono<String> createTable() {
40+
return r2dbcEntityTemplate.getDatabaseClient()
41+
.sql("CREATE TABLE NAMES "
42+
+ "(UUID STRING(36), NAME STRING(60) NOT NULL) "
43+
+ "PRIMARY KEY (UUID)")
44+
.fetch()
45+
.rowsUpdated()
46+
.map(numRows -> "table NAMES created successfully")
47+
.onErrorResume(error -> Mono.just("table creation failed: " + error.getMessage()));
48+
}
49+
50+
@PostMapping("dropTable")
51+
public Mono<String> dropTable() {
52+
return r2dbcEntityTemplate.getDatabaseClient().sql("DROP TABLE NAMES")
53+
.fetch().rowsUpdated().map(numRows -> "table NAMES dropped successfully")
54+
.onErrorResume(error -> Mono.just("table deletion failed: " + error.getMessage()));
55+
}
56+
57+
@GetMapping("listRows")
58+
public Flux<Name> listRows() {
59+
return r2dbcEntityTemplate.select(Name.class)
60+
.all();
61+
}
62+
63+
@PostMapping("addRow")
64+
public Mono<String> addRow(@RequestBody String newName) {
65+
return r2dbcEntityTemplate.insert(new Name(UUID.randomUUID().toString(), newName))
66+
.map(numRows -> "row inserted successfully")
67+
.onErrorResume(error -> Mono.just("row insertion failed: " + error.getMessage()));
68+
}
69+
70+
@PostMapping("deleteRow")
71+
public Mono<String> deleteRow(@RequestBody String uuid) {
72+
return r2dbcEntityTemplate.delete(Name.class).matching(query(where("uuid").is(uuid)))
73+
.all()
74+
.map(numDeleted -> numDeleted > 0 ? "row deleted successfully" : "row did not exist")
75+
.onErrorResume(error -> Mono.just("row deletion failed: " + error.getMessage()));
76+
}
77+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
spring.r2dbc.url=\
16+
r2dbc:spanner://spanner.googleapis.com:443/projects/${project}/instances/${instance}/databases/${database}

0 commit comments

Comments
 (0)