Google Cloud Services - Spanner

This extension allows to inject a com.google.cloud.spanner.Spanner object inside your Quarkus application.

Be sure to have read the Google Cloud Services extension pack global documentation before this one, it contains general configuration and information.

Bootstrapping the project

First, we need a new project.Create a new project with the following command (replace the version placeholder with the correct one):

mvn io.quarkus:quarkus-maven-plugin:<quarkusVersion>:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=spanner-quickstart \
    -Dextensions="resteasy-reactive-jackson,quarkus-google-cloud-spanner"
cd spanner-quickstart

This command generates a Maven project, importing the Google Cloud Spanner extension.

If you already have your Quarkus project configured, you can add the quarkus-google-cloud-spanner extension to your project by running the following command in your project base directory:

./mvnw quarkus:add-extension -Dextensions="quarkus-google-cloud-spanner"

This will add the following to your pom.xml:

<dependency>
    <groupId>io.quarkiverse.googlecloudservices</groupId>
    <artifactId>quarkus-google-cloud-spanner</artifactId>
</dependency>

Preparatory steps

To test Spanner you first need to have a running Spanner cluster named test-instance.

You can create one with gcloud:

gcloud spanner instances create test-instance --config=regional-us-central1 \
    --description="Test Instance" --nodes=1

Then you need a database named test-database.

You can create one with gcloud:

gcloud spanner databases create test-database --instance test-instance

And finally you need to create a table named Singers.

You can do it with gcloud:

gcloud spanner databases ddl update test-database --instance test-instance \
  --ddl='CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX) ) PRIMARY KEY (SingerId)'

Some example

This is an example usage of the extension: we create a REST resource with a single endpoint that inserts four singers inside the Singers table, then reads the all table and returns its elements.

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;

import com.google.cloud.spanner.*;

@Path("/spanner")
public class SpannerResource {
    private static final Logger LOG = Logger.getLogger(SpannerResource.class);

    @Inject
    Spanner spanner;// Inject Spanner

    @ConfigProperty(name = "quarkus.google.cloud.project-id")
    String projectId;// Inject the project name

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String spanner() {
        // Create a database client
        DatabaseId id = DatabaseId.of(projectId, "test-instance", "test-database");
        DatabaseClient dbClient = spanner.getDatabaseClient(id);

        // Insert 4 singer records
        dbClient.readWriteTransaction().run(transaction -> {
            String sql = "INSERT INTO Singers (SingerId, FirstName, LastName) VALUES "
                    + "(12, 'Melissa', 'Garcia'), "
                    + "(13, 'Russell', 'Morales'), "
                    + "(14, 'Jacqueline', 'Long'), "
                    + "(15, 'Dylan', 'Shaw')";
            long rowCount = transaction.executeUpdate(Statement.of(sql));
            LOG.infov("{0} records inserted.", rowCount);
            return null;
        });

        // Read them
        try (ResultSet resultSet = dbClient.singleUse() // Execute a single read or query against Cloud Spanner.
                .executeQuery(Statement.of("SELECT SingerId, FirstName, LastName FROM Singers"))) {
            StringBuilder builder = new StringBuilder();
            while (resultSet.next()) {
                builder.append(resultSet.getLong(0)).append(' ').append(resultSet.getString(1)).append(' ')
                        .append(resultSet.getString(2)).append('\n');
            }
            return builder.toString();
        }
    }

}