1. Übersicht
Google Cloud Spanner ist ein vollständig verwalteter, horizontal skalierbarer, global verteilter, relationaler Datenbankdienst, der ACID-Transaktionen und SQL-Semantik bietet, ohne Leistung und Hochverfügbarkeit zu beeinträchtigen.
In diesem Lab erfahren Sie, wie Sie eine Cloud Spanner-Instanz einrichten. Sie durchlaufen die Schritte zum Erstellen einer Datenbank und eines Schemas, die für eine Bestenliste für Spiele verwendet werden können. Zuerst erstellen Sie eine Tabelle „Players“ zum Speichern von Spielerinformationen und eine Tabelle „Scores“ zum Speichern von Spielergebnissen.
Als Nächstes füllen Sie die Tabellen mit Beispieldaten. Zum Schluss führen Sie einige Beispielabfragen für die zehn wichtigsten Elemente aus und löschen dann die Instanz, um Ressourcen freizugeben.
Lerninhalte
- Cloud Spanner-Instanz einrichten
- Datenbank und Tabellen erstellen
- Commit-Zeitstempelspalte verwenden
- Daten mit Zeitstempeln in die Cloud Spanner-Datenbanktabelle laden
- Cloud Spanner-Datenbank abfragen
- Cloud Spanner-Instanz löschen
Voraussetzungen
Wie werden Sie diese Anleitung verwenden?
Wie würden Sie Ihre Erfahrung mit der Google Cloud Platform bewerten?
2. Einrichtung und Anforderungen
Umgebung zum selbstbestimmten Lernen einrichten
Wenn Sie noch kein Google-Konto (Gmail oder Google Apps) haben, müssen Sie eines erstellen. Melden Sie sich in der Google Cloud Console ( console.cloud.google.com) an und erstellen Sie ein neues Projekt.
Wenn Sie bereits ein Projekt haben, klicken Sie oben links in der Console auf das Drop-down-Menü zur Projektauswahl:

Klicken Sie im angezeigten Dialogfeld auf die Schaltfläche „NEUES PROJEKT“, um ein neues Projekt zu erstellen:

Wenn Sie noch kein Projekt haben, wird ein Dialogfeld wie das folgende angezeigt, in dem Sie Ihr erstes Projekt erstellen können:

Im nachfolgenden Dialogfeld zum Erstellen von Projekten können Sie die Details Ihres neuen Projekts eingeben:

Merken Sie sich die Projekt-ID. Sie ist für alle Google Cloud-Projekte ein eindeutiger Name. Der Name oben ist bereits vergeben und kann nicht verwendet werden. Sie wird später in diesem Codelab als PROJECT_ID bezeichnet.
Als Nächstes müssen Sie, falls noch nicht geschehen, die Abrechnung in der Entwicklerkonsole aktivieren, um Google Cloud-Ressourcen verwenden zu können, und die Cloud Spanner API aktivieren.

Dieses Codelab sollte Sie nicht mehr als ein paar Dollar kosten, aber es könnte mehr sein, wenn Sie sich für mehr Ressourcen entscheiden oder wenn Sie sie laufen lassen (siehe Abschnitt „Bereinigen“ am Ende dieses Dokuments). Die Preise für Google Cloud Spanner sind hier dokumentiert.
Neuen Nutzern der Google Cloud Platform steht eine kostenlose Testversion mit einem Guthaben von 300$ zur Verfügung. Dieses Codelab sollte damit vollständig kostenlos sein.
Google Cloud Shell einrichten
Während Sie Google Cloud und Spanner von Ihrem Laptop aus per Fernzugriff nutzen können, wird in diesem Codelab Google Cloud Shell verwendet, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Diese Debian-basierte virtuelle Maschine verfügt über alle Entwicklungstools, die Sie benötigen. Sie bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft in Google Cloud, was die Netzwerkleistung und Authentifizierung erheblich verbessert. Für dieses Codelab benötigen Sie also nur einen Browser (es funktioniert auch auf einem Chromebook).
- Klicken Sie zum Aktivieren von Cloud Shell in der Cloud Console einfach auf Cloud Shell aktivieren
. Die Bereitstellung und Verbindung mit der Umgebung sollte nur wenige Augenblicke dauern.
Sobald die Verbindung mit der Cloud Shell hergestellt ist, sehen Sie, dass Sie bereits authentifiziert sind und für das Projekt schon Ihre PROJECT_ID eingestellt ist.
gcloud auth list
Befehlsausgabe
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Befehlsausgabe
[core] project = <PROJECT_ID>
Wenn das Projekt aus irgendeinem Grund nicht festgelegt ist, führen Sie einfach den folgenden Befehl aus:
gcloud config set project <PROJECT_ID>
Suchst du nach deinem PROJECT_ID? Sehen Sie nach, welche ID Sie in den Einrichtungsschritten verwendet haben, oder suchen Sie sie im Cloud Console-Dashboard:
In Cloud Shell werden auch einige Umgebungsvariablen standardmäßig festgelegt, die für zukünftige Befehle nützlich sein können.
echo $GOOGLE_CLOUD_PROJECT
Befehlsausgabe
<PROJECT_ID>
- Legen Sie zum Schluss die Standardzone und die Projektkonfiguration fest.
gcloud config set compute/zone us-central1-f
Sie können verschiedene Zonen auswählen. Weitere Informationen finden Sie unter Regionen und Zonen.
Zusammenfassung
In diesem Schritt richten Sie Ihre Umgebung ein.
Als Nächstes
Als Nächstes richten Sie eine Cloud Spanner-Instanz ein.
3. Cloud Spanner-Instanz einrichten
In diesem Schritt richten wir unsere Cloud Spanner-Instanz für dieses Codelab ein. Suchen Sie nach dem Spanner-Eintrag
im Dreistrich-Menü oben links
oder suchen Sie nach Spanner, indem Sie „/“ drücken und „Spanner“ eingeben.

Klicken Sie dann auf
und füllen Sie das Formular aus, indem Sie den Instanznamen cloudspanner-leaderboard für Ihre Instanz eingeben, eine Konfiguration auswählen (regionale Instanz) und die Anzahl der Knoten festlegen. Für dieses Codelab benötigen wir nur einen Knoten. Für Produktionsinstanzen und um das Cloud Spanner-SLA zu erfüllen, müssen Sie mindestens drei Knoten in Ihrer Cloud Spanner-Instanz ausführen.
Klicken Sie abschließend auf „Erstellen“. Innerhalb von Sekunden steht Ihnen eine Cloud Spanner-Instanz zur Verfügung.

Im nächsten Schritt verwenden wir die Java-Clientbibliothek, um eine Datenbank und ein Schema in unserer neuen Instanz zu erstellen.
4. Datenbank und Schema erstellen
In diesem Schritt erstellen wir unsere Beispieldatenbank und unser Schema.
Wir verwenden die Java-Clientbibliothek, um zwei Tabellen zu erstellen: eine „Players“-Tabelle für Spielerinformationen und eine „Scores“-Tabelle zum Speichern von Spielergebnissen. Dazu erstellen wir eine Java-Konsolenanwendung in Cloud Shell.
Klonen Sie zuerst den Beispielcode für dieses Codelab von GitHub, indem Sie den folgenden Befehl in Cloud Shell eingeben:
git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
Wechseln Sie dann in das Verzeichnis „applications“, in dem Sie Ihre Anwendung erstellen.
cd java-docs-samples/spanner/leaderboard
Der gesamte für dieses Codelab erforderliche Code befindet sich im vorhandenen Verzeichnis java-docs-samples/spanner/leaderboard/complete als ausführbare C#-Anwendung mit dem Namen Leaderboard. Sie dient als Referenz, während Sie das Codelab durcharbeiten. Wir erstellen ein neues Verzeichnis und erstellen nach und nach eine Kopie der Bestenlistenanwendung.
Erstellen Sie mit dem folgenden Befehl ein neues Verzeichnis mit dem Namen „codelab“ für die Anwendung und wechseln Sie dorthin:
mkdir codelab && cd $_
Erstellen Sie mit dem folgenden Maven-Befehl (mvn) eine neue einfache Java-Anwendung mit dem Namen „Leaderboard“:
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.google.codelabs -DartifactId=leaderboard -DarchetypeVersion=1.4
Mit diesem Befehl wird eine einfache Konsolenanwendung erstellt, die aus zwei primären Dateien besteht: der Maven-App-Konfigurationsdatei pom.xml und der Java-App-Datei App.java.
Wechseln Sie als Nächstes in das gerade erstellte Bestenlistenverzeichnis und listen Sie dessen Inhalt auf:
cd leaderboard && ls
Die Datei pom.xml und das Verzeichnis src sollten aufgeführt sein:
pom.xml src
Als Nächstes aktualisieren wir diese Konsolenanwendung, indem wir App.java bearbeiten, damit die Java Spanner-Clientbibliothek verwendet wird, um eine Bestenliste mit zwei Tabellen zu erstellen: „Players“ (Spieler) und „Scores“ (Ergebnisse). Das können Sie direkt im Cloud Shell-Editor tun:
Öffnen Sie den Cloud Shell-Editor, indem Sie auf das unten hervorgehobene Symbol klicken:

Öffnen Sie die Datei „pom.xml“ im Ordner „leaderboard“. Öffnen Sie die Datei „pom.xml“ im Ordner java-docs-samples\ spanner\leaderboard\codelab\leaderboard.In dieser Datei wird das Maven-Buildsystem so konfiguriert, dass unsere Anwendung in ein JAR-Archiv kompiliert wird, einschließlich aller Abhängigkeiten.
Fügen Sie direkt unter dem vorhandenen </properties>-Element den folgenden neuen Abschnitt zur Abhängigkeitsverwaltung hinzu:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-bom</artifactId>
<version>0.83.0-alpha</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Fügen Sie außerdem im vorhandenen Abschnitt <dependencies> eine neue Abhängigkeit hinzu, um die Cloud Spanner-Clientbibliothek für Java der Anwendung hinzuzufügen.
<dependency>
<!-- Version auto-managed by BOM -->
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner</artifactId>
</dependency>
Ersetzen Sie dann den vorhandenen <build>-Abschnitt der Datei pom.xml durch den folgenden <build>-Abschnitt:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<finalName>leaderboard</finalName>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.google.codelabs.App</mainClass>
</manifest>
</archive>
<appendAssemblyId>false</appendAssemblyId>
<attach>false</attach>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</build>
Speichern Sie die Änderungen, die Sie an der Datei „pom.xml“ vorgenommen haben, indem Sie im Menü „Datei“ des Cloud Shell-Editors „Speichern“ auswählen oder die Tastenkombination „Strg“ + „S“ drücken.
Öffnen Sie als Nächstes die Datei App.java im Cloud Shell-Editor im Ordner src/main/java/com/google/codelabs/. Ersetzen Sie den vorhandenen Code der Datei durch den Code, der zum Erstellen der Datenbank leaderboard und der Tabellen Players und Scores erforderlich ist. Fügen Sie dazu den folgenden Java-Code in die Datei App.java ein:
package com.google.codelabs;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
/**
* Example code for using the Cloud Spanner API with the Google Cloud Java client library
* to create a simple leaderboard.
*
* This example demonstrates:
*
* <p>
*
* <ul>
* <li>Creating a Cloud Spanner database.
* </ul>
*/
public class App {
static void create(DatabaseAdminClient dbAdminClient, DatabaseId db) {
OperationFuture<Database, CreateDatabaseMetadata> op =
dbAdminClient.createDatabase(
db.getInstanceId().getInstance(),
db.getDatabase(),
Arrays.asList(
"CREATE TABLE Players(\n"
+ " PlayerId INT64 NOT NULL,\n"
+ " PlayerName STRING(2048) NOT NULL\n"
+ ") PRIMARY KEY(PlayerId)",
"CREATE TABLE Scores(\n"
+ " PlayerId INT64 NOT NULL,\n"
+ " Score INT64 NOT NULL,\n"
+ " Timestamp TIMESTAMP NOT NULL\n"
+ " OPTIONS(allow_commit_timestamp=true)\n"
+ ") PRIMARY KEY(PlayerId, Timestamp),\n"
+ "INTERLEAVE IN PARENT Players ON DELETE NO ACTION"));
try {
// Initiate the request which returns an OperationFuture.
Database dbOperation = op.get();
System.out.println("Created database [" + dbOperation.getId() + "]");
} catch (ExecutionException e) {
// If the operation failed during execution, expose the cause.
throw (SpannerException) e.getCause();
} catch (InterruptedException e) {
// Throw when a thread is waiting, sleeping, or otherwise occupied,
// and the thread is interrupted, either before or during the activity.
throw SpannerExceptionFactory.propagateInterrupt(e);
}
}
static void printUsageAndExit() {
System.out.println("Leaderboard 1.0.0");
System.out.println("Usage:");
System.out.println(" java -jar leaderboard.jar "
+ "<command> <instance_id> <database_id> [command_option]");
System.out.println("");
System.out.println("Examples:");
System.out.println(" java -jar leaderboard.jar create my-instance example-db");
System.out.println(" - Create a sample Cloud Spanner database along with "
+ "sample tables in your project.\n");
System.exit(1);
}
public static void main(String[] args) throws Exception {
if (!(args.length == 3 || args.length == 4)) {
printUsageAndExit();
}
SpannerOptions options = SpannerOptions.newBuilder().build();
Spanner spanner = options.getService();
try {
String command = args[0];
DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]);
DatabaseClient dbClient = spanner.getDatabaseClient(db);
DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient();
switch (command) {
case "create":
create(dbAdminClient, db);
break;
default:
printUsageAndExit();
}
} finally {
spanner.close();
}
System.out.println("Closed client");
}
}
Speichern Sie die Änderungen, die Sie an der Datei App.java vorgenommen haben, indem Sie im Cloud Shell-Editor im Menü „Datei“ die Option „Speichern“ auswählen.
In der Datei App.java im Verzeichnis java-docs-samples/spanner/leaderboard/step4/src finden Sie ein Beispiel dafür, wie Ihre App.java-Datei aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls create hinzugefügt haben.
Führen Sie zum Erstellen Ihrer App „mvn package“ in dem Verzeichnis aus, in dem sich die Datei pom.xml befindet:
mvn package
Nachdem Ihre Java-JAR-Datei erfolgreich erstellt wurde, führen Sie die resultierende Anwendung in der Cloud Shell aus, indem Sie den folgenden Befehl eingeben:
java -jar target/leaderboard.jar
Die Ausgabe sollte etwa so aussehen:
Leaderboard 1.0.0
Usage:
java -jar leaderboard.jar <command> <instance_id> <database_id> [command_option]
Examples:
java -jar leaderboard.jar create my-instance example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
Aus dieser Antwort geht hervor, dass es sich um die Anwendung Leaderboard handelt, die derzeit einen möglichen Befehl hat: create. Die erwarteten Argumente des create-Befehls sind „Instance ID“ (Instanz-ID) und „Database ID“ (Datenbank-ID).
Führen Sie nun den folgenden Befehl aus.
java -jar target/leaderboard.jar create cloudspanner-leaderboard leaderboard
Nach einigen Sekunden sollte eine Antwort wie die folgende angezeigt werden:
Created database [projects/your-project/instances/cloudspanner-leaderboard/databases/leaderboard]
Im Cloud Spanner-Bereich der Cloud Console sollten Ihre neue Datenbank und die neuen Tabellen im Menü auf der linken Seite angezeigt werden.

Im nächsten Schritt aktualisieren wir unsere Anwendung, um einige Daten in Ihre neue Datenbank zu laden.
5. Daten laden
Wir haben jetzt eine Datenbank mit dem Namen leaderboard, die zwei Tabellen enthält: Players und Scores. Jetzt verwenden wir die Java-Clientbibliothek, um die Tabelle Players mit Spielern und die Tabelle Scores mit zufälligen Punktzahlen für jeden Spieler zu füllen.
Öffnen Sie den Cloud Shell-Editor, falls er noch nicht geöffnet ist. Klicken Sie dazu auf das unten hervorgehobene Symbol:

Bearbeiten Sie als Nächstes die Datei App.java im Cloud Shell Editor, um einen insert-Befehl hinzuzufügen, mit dem 100 Spieler in die Tabelle Players eingefügt werden können. Alternativ kann der Befehl verwendet werden, um für jeden Spieler in der Tabelle Players vier zufällige Ergebnisse in die Tabelle Scores einzufügen.
Aktualisieren Sie zuerst den Abschnitt imports oben in der App-Datei. Ersetzen Sie den aktuellen Inhalt durch Folgendes:
package com.google.codelabs;
import static com.google.cloud.spanner.TransactionRunner.TransactionCallable;
import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Mutation;
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spanner.TransactionContext;
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
Fügen Sie als Nächstes die folgenden Methoden „insert“, „insertPlayers“ und „insertScores“ unter der vorhandenen create()-Methode und über der vorhandenen printUsageAndExit()-Methode ein:
static void insert(DatabaseClient dbClient, String insertType) {
try {
insertType = insertType.toLowerCase();
} catch (Exception e) {
// Invalid input received, set insertType to empty string.
insertType = "";
}
if (insertType.equals("players")) {
// Insert players.
insertPlayers(dbClient);
} else if (insertType.equals("scores")) {
// Insert scores.
insertScores(dbClient);
} else {
// Invalid input.
System.out.println("Invalid value for 'type of insert'. "
+ "Specify a valid value: 'players' or 'scores'.");
System.exit(1);
}
}
static void insertPlayers(DatabaseClient dbClient) {
dbClient
.readWriteTransaction()
.run(
new TransactionCallable<Void>() {
@Override
public Void run(TransactionContext transaction) throws Exception {
// Get the number of players.
String sql = "SELECT Count(PlayerId) as PlayerCount FROM Players";
ResultSet resultSet = transaction.executeQuery(Statement.of(sql));
long numberOfPlayers = 0;
if (resultSet.next()) {
numberOfPlayers = resultSet.getLong("PlayerCount");
}
// Insert 100 player records into the Players table.
List<Statement> stmts = new ArrayList<Statement>();
long randomId;
for (int x = 1; x <= 100; x++) {
numberOfPlayers++;
randomId = (long) Math.floor(Math.random() * 9_000_000_000L) + 1_000_000_000L;
Statement statement =
Statement
.newBuilder(
"INSERT INTO Players (PlayerId, PlayerName) "
+ "VALUES (@PlayerId, @PlayerName) ")
.bind("PlayerId")
.to(randomId)
.bind("PlayerName")
.to("Player " + numberOfPlayers)
.build();
stmts.add(statement);
}
transaction.batchUpdate(stmts);
return null;
}
});
System.out.println("Done inserting player records...");
}
static void insertScores(DatabaseClient dbClient) {
boolean playerRecordsFound = false;
ResultSet resultSet =
dbClient
.singleUse()
.executeQuery(Statement.of("SELECT * FROM Players"));
while (resultSet.next()) {
playerRecordsFound = true;
final long playerId = resultSet.getLong("PlayerId");
dbClient
.readWriteTransaction()
.run(
new TransactionCallable<Void>() {
@Override
public Void run(TransactionContext transaction) throws Exception {
// Initialize objects for random Score and random Timestamp.
LocalDate endDate = LocalDate.now();
long end = endDate.toEpochDay();
int startYear = endDate.getYear() - 2;
int startMonth = endDate.getMonthValue();
int startDay = endDate.getDayOfMonth();
LocalDate startDate = LocalDate.of(startYear, startMonth, startDay);
long start = startDate.toEpochDay();
Random r = new Random();
List<Statement> stmts = new ArrayList<Statement>();
// Insert 4 score records into the Scores table
// for each player in the Players table.
for (int x = 1; x <= 4; x++) {
// Generate random score between 1,000,000 and 1,000
long randomScore = r.nextInt(1000000 - 1000) + 1000;
// Get random day within the past two years.
long randomDay = ThreadLocalRandom.current().nextLong(start, end);
LocalDate randomDayDate = LocalDate.ofEpochDay(randomDay);
LocalTime randomTime = LocalTime.of(
r.nextInt(23), r.nextInt(59), r.nextInt(59), r.nextInt(9999));
LocalDateTime randomDate = LocalDateTime.of(randomDayDate, randomTime);
Instant randomInstant = randomDate.toInstant(ZoneOffset.UTC);
Statement statement =
Statement
.newBuilder(
"INSERT INTO Scores (PlayerId, Score, Timestamp) "
+ "VALUES (@PlayerId, @Score, @Timestamp) ")
.bind("PlayerId")
.to(playerId)
.bind("Score")
.to(randomScore)
.bind("Timestamp")
.to(randomInstant.toString())
.build();
stmts.add(statement);
}
transaction.batchUpdate(stmts);
return null;
}
});
}
if (!playerRecordsFound) {
System.out.println("Parameter 'scores' is invalid since "
+ "no player records currently exist. First insert players "
+ "then insert scores.");
System.exit(1);
} else {
System.out.println("Done inserting score records...");
}
}
Damit der Befehl insert funktioniert, fügen Sie der „main“-Methode Ihrer App in der switch (command)-Anweisung den folgenden Code hinzu :
case "insert":
String insertType;
try {
insertType = args[3];
} catch (ArrayIndexOutOfBoundsException exception) {
insertType = "";
}
insert(dbClient, insertType);
break;
Wenn Sie fertig sind, sollte die switch (command)-Anweisung so aussehen:
switch (command) {
case "create":
create(dbAdminClient, db);
break;
case "insert":
String insertType;
try {
insertType = args[3];
} catch (ArrayIndexOutOfBoundsException exception) {
insertType = "";
}
insert(dbClient, insertType);
break;
default:
printUsageAndExit();
}
Der letzte Schritt, um Ihrer App die Funktion „Einfügen“ hinzuzufügen, besteht darin, der printUsageAndExit()-Methode Hilfetext für den Befehl „Einfügen“ hinzuzufügen. Fügen Sie der printUsageAndExit()-Methode die folgenden Codezeilen hinzu, um Hilfetext für den Befehl „Einfügen“ einzufügen:
System.out.println(" java -jar leaderboard.jar insert my-instance example-db players");
System.out.println(" - Insert 100 sample Player records into the database.\n");
System.out.println(" java -jar leaderboard.jar insert my-instance example-db scores");
System.out.println(" - Insert sample score data into Scores sample Cloud Spanner "
+ "database table.\n");
Speichern Sie die Änderungen, die Sie an der Datei App.java vorgenommen haben, indem Sie im Cloud Shell-Editor im Menü „Datei“ die Option „Speichern“ auswählen.
In der Datei App.java im Verzeichnis java-docs-samples/spanner/leaderboard/step5/src finden Sie ein Beispiel dafür, wie Ihre App.java-Datei aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls insert hinzugefügt haben.
Erstellen Sie die App nun neu und führen Sie sie aus, um zu bestätigen, dass der neue insert-Befehl in der Liste der möglichen Befehle der App enthalten ist.
Führen Sie zum Erstellen der App mvn package im Verzeichnis aus, in dem sich die Datei „pom.xml“ befindet:
mvn package
Wenn Ihre Java-JAR-Datei erfolgreich erstellt wurde, führen Sie den folgenden Befehl aus:
java -jar target/leaderboard.jar
Der Befehl insert sollte jetzt in der Standardausgabe der App enthalten sein:
Leaderboard 1.0.0
Usage:
java -jar leaderboard.jar <command> <instance_id> <database_id> [command_option]
Examples:
java -jar leaderboard.jar create my-instance example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
java -jar leaderboard.jar insert my-instance example-db players
- Insert 100 sample Player records into the database.
java -jar leaderboard.jar insert my-instance example-db scores
- Insert sample score data into Scores sample Cloud Spanner database table.
Aus der Antwort geht hervor, dass es neben der Instanz-ID und der Datenbank-ID ein weiteres Argument gibt, das den Wert „players“ oder „scores“ haben kann.
Führen wir nun den Befehl insert mit denselben Argumentwerten aus, die wir beim Aufrufen des Befehls create verwendet haben. Wir fügen „players“ als zusätzliches Argument für „type of insert“ hinzu.
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard players
Nach einigen Sekunden sollte eine Antwort wie die folgende angezeigt werden:
Done inserting player records...
Nun verwenden wir die Java-Clientbibliothek, um die Tabelle Scores mit vier zufälligen Punktzahlen und Zeitstempeln für jeden Spieler in der Tabelle Players zu füllen.
Die Spalte Timestamp der Tabelle Scores wurde mit der folgenden SQL-Anweisung, die beim vorherigen Ausführen des Befehls create ausgeführt wurde, als „Commit-Zeitstempel“-Spalte definiert:
CREATE TABLE Scores(
PlayerId INT64 NOT NULL,
Score INT64 NOT NULL,
Timestamp TIMESTAMP NOT NULL OPTIONS(allow_commit_timestamp=true)
) PRIMARY KEY(PlayerId, Timestamp),
INTERLEAVE IN PARENT Players ON DELETE NO ACTION
Beachten Sie das Attribut OPTIONS(allow_commit_timestamp=true). Dadurch wird Timestamp zu einer „Commit-Zeitstempel“-Spalte, die bei INSERT- und UPDATE-Vorgängen für eine bestimmte Tabellenzeile automatisch mit dem genauen Transaktionszeitstempel gefüllt werden kann.
Sie können auch eigene Zeitstempelwerte in eine Commit-Zeitstempelspalte einfügen, sofern Sie einen Zeitstempel mit einem Wert in der Vergangenheit einfügen. Das werden wir in diesem Codelab tun.
Führen wir nun den Befehl insert mit denselben Argumentwerten aus, die wir beim Aufrufen des Befehls create verwendet haben, und fügen „scores“ als zusätzliches Argument „type of insert“ hinzu.
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard scores
Nach einigen Sekunden sollte eine Antwort wie die folgende angezeigt werden:
Done inserting score records...
Wenn Sie insert mit dem als scores angegebenen „type of insert“ (Einfügetyp) ausführen, wird die Methode insertScores aufgerufen, die die folgenden Code-Snippets verwendet, um einen zufällig generierten Zeitstempel mit einem Datum und einer Uhrzeit in der Vergangenheit einzufügen:
LocalDate endDate = LocalDate.now();
long end = endDate.toEpochDay();
int startYear = endDate.getYear() - 2;
int startMonth = endDate.getMonthValue();
int startDay = endDate.getDayOfMonth();
LocalDate startDate = LocalDate.of(startYear, startMonth, startDay);
long start = startDate.toEpochDay();
...
long randomDay = ThreadLocalRandom.current().nextLong(start, end);
LocalDate randomDayDate = LocalDate.ofEpochDay(randomDay);
LocalTime randomTime = LocalTime.of(
r.nextInt(23), r.nextInt(59), r.nextInt(59), r.nextInt(9999));
LocalDateTime randomDate = LocalDateTime.of(randomDayDate, randomTime);
Instant randomInstant = randomDate.toInstant(ZoneOffset.UTC);
...
.bind("Timestamp")
.to(randomInstant.toString())
Wenn Sie die Spalte Timestamp automatisch mit dem Zeitstempel des genauen Zeitpunkts der „Insert“-Transaktion füllen möchten, können Sie stattdessen die Java-Konstante Value.COMMIT_TIMESTAMP wie im folgenden Code-Snippet einfügen:
.bind("Timestamp")
.to(Value.COMMIT_TIMESTAMP)
Nachdem wir die Daten geladen haben, überprüfen wir die Werte, die wir gerade in unsere neuen Tabellen geschrieben haben. Wählen Sie zuerst die Datenbank leaderboard und dann die Tabelle Players aus. Klicken Sie auf den Tab Data. Sie sollten sehen, dass die Spalten PlayerId und PlayerName der Tabelle Daten enthalten.

Als Nächstes prüfen wir, ob die Tabelle „Scores“ auch Daten enthält. Klicken Sie dazu auf die Tabelle Scores und wählen Sie den Tab Data aus. Sie sollten sehen, dass die Spalten PlayerId, Timestamp und Score der Tabelle Daten enthalten.

Gut gemacht! Wir aktualisieren unsere App, um einige Abfragen auszuführen, mit denen wir eine Bestenliste für Spiele erstellen können.
6. Bestenlistenabfragen ausführen
Nachdem wir unsere Datenbank eingerichtet und Informationen in unsere Tabellen geladen haben, erstellen wir nun eine Bestenliste mit diesen Daten. Dazu müssen wir die folgenden vier Fragen beantworten:
- Welche Spieler gehören zu den „Top 10“ aller Zeiten?
- Welche Spieler gehören zu den „Top Ten“ des Jahres?
- Welche Spieler gehören zu den Top 10 des Monats?
- Welche Spieler gehören zu den „Top Ten“ der Woche?
Wir aktualisieren unsere App, damit die SQL-Abfragen ausgeführt werden, mit denen diese Fragen beantwortet werden.
Wir fügen einen query-Befehl hinzu, mit dem die Abfragen ausgeführt werden können, um die Fragen zu beantworten, die die für unsere Bestenliste erforderlichen Informationen liefern.
Bearbeiten Sie die Datei App.java im Cloud Shell-Editor, um die App zu aktualisieren und einen query-Befehl hinzuzufügen. Der Befehl query besteht aus zwei query-Methoden. Eine verwendet nur ein DatabaseClient-Argument und die andere ein zusätzliches timespan-Argument, um das Filtern von Ergebnissen nach einem in Stunden angegebenen Zeitraum zu ermöglichen.
Fügen Sie die folgenden beiden query-Methoden unter der vorhandenen insertScores()-Methode und über der vorhandenen printUsageAndExit()-Methode hinzu:
static void query(DatabaseClient dbClient) {
String scoreDate;
String score;
ResultSet resultSet =
dbClient
.singleUse()
.executeQuery(
Statement.of(
"SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp "
+ "FROM Players p "
+ "JOIN Scores s ON p.PlayerId = s.PlayerId "
+ "ORDER BY s.Score DESC LIMIT 10"));
while (resultSet.next()) {
scoreDate = String.valueOf(resultSet.getTimestamp("Timestamp"));
score = String.format("%,d", resultSet.getLong("Score"));
System.out.printf(
"PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
resultSet.getLong("PlayerId"), resultSet.getString("PlayerName"), score,
scoreDate.substring(0,10));
}
}
static void query(DatabaseClient dbClient, int timespan) {
String scoreDate;
String score;
Statement statement =
Statement
.newBuilder(
"SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp "
+ "FROM Players p "
+ "JOIN Scores s ON p.PlayerId = s.PlayerId "
+ "WHERE s.Timestamp > "
+ "TIMESTAMP_SUB(CURRENT_TIMESTAMP(), "
+ " INTERVAL @Timespan HOUR) "
+ "ORDER BY s.Score DESC LIMIT 10")
.bind("Timespan")
.to(timespan)
.build();
ResultSet resultSet =
dbClient
.singleUse()
.executeQuery(statement);
while (resultSet.next()) {
scoreDate = String.valueOf(resultSet.getTimestamp("Timestamp"));
score = String.format("%,d", resultSet.getLong("Score"));
System.out.printf(
"PlayerId: %d PlayerName: %s Score: %s Timestamp: %s\n",
resultSet.getLong("PlayerId"), resultSet.getString("PlayerName"), score,
scoreDate.substring(0,10));
}
}
Damit der Befehl query funktioniert, fügen Sie der Anweisung switch(command) in der „main“-Methode Ihrer App den folgenden Code hinzu:
case "query":
if (args.length == 4) {
int timespan = 0;
try {
timespan = Integer.parseInt(args[3]);
} catch (NumberFormatException e) {
System.err.println("query command's 'timespan' parameter must be a valid integer.");
System.exit(1);
}
query(dbClient, timespan);
} else {
query(dbClient);
}
break;
Der letzte Schritt zum Hinzufügen der „query“-Funktion zu Ihrer App besteht darin, der printUsageAndExit()-Methode Hilfetext für den „query“-Befehl hinzuzufügen. Fügen Sie der printUsageAndExit()-Methode die folgenden Codezeilen hinzu, um Hilfetext für den Befehl „query“ einzufügen:
System.out.println(" java -jar leaderboard.jar query my-instance example-db");
System.out.println(" - Query players with top ten scores of all time.\n");
System.out.println(" java -jar leaderboard.jar query my-instance example-db 168");
System.out.println(" - Query players with top ten scores within a timespan "
+ "specified in hours.\n");
Speichern Sie die Änderungen, die Sie an der Datei App.java vorgenommen haben, indem Sie im Cloud Shell-Editor im Menü „Datei“ die Option „Speichern“ auswählen.
In der Datei App.java im Verzeichnis dotnet-docs-samples/applications/leaderboard/step6/src finden Sie ein Beispiel dafür, wie Ihre App.java-Datei aussehen sollte, nachdem Sie den Code zum Aktivieren des Befehls query hinzugefügt haben.
Führen Sie zum Erstellen der App mvn package im Verzeichnis aus, in dem sich die Datei „pom.xml“ befindet:
mvn package
Führen Sie die App nun aus, um zu bestätigen, dass der neue query-Befehl in der Liste der möglichen Befehle der App enthalten ist. Führen Sie dazu diesen Befehl aus:
java -jar target/leaderboard.jar
Der Befehl query sollte jetzt als neue Befehlsoption in der Standardausgabe der App enthalten sein:
Leaderboard 1.0.0
Usage:
java -jar leaderboard.jar <command> <instance_id> <database_id> [command_option]
Examples:
java -jar leaderboard.jar create my-instance example-db
- Create a sample Cloud Spanner database along with sample tables in your project.
java -jar leaderboard.jar insert my-instance example-db players
- Insert 100 sample Player records into the database.
java -jar leaderboard.jar insert my-instance example-db scores
- Insert sample score data into Scores sample Cloud Spanner database table.
java -jar leaderboard.jar query my-instance example-db
- Query players with top ten scores of all time.
java -jar leaderboard.jar query my-instance example-db 168
- Query players with top ten scores within a timespan specified in hours.
Aus der Antwort geht hervor, dass wir mit dem Befehl query neben den Argumenten „Instance ID“ (Instanz-ID) und „Database ID“ (Datenbank-ID) auch einen optionalen Zeitraum in Stunden angeben können, der zum Filtern von Datensätzen anhand ihres Werts in der Spalte Timestamp der Tabelle Scores verwendet werden soll. Da das Argument „timespan“ optional ist, werden keine Datensätze nach Zeitstempeln gefiltert, wenn es nicht angegeben wird. Wir können also den Befehl query ohne einen „timespan“-Wert verwenden, um eine Liste der zehn besten Spieler aller Zeiten zu erhalten.
Führen wir den Befehl query ohne Angabe eines „Zeitraums“ aus. Verwenden Sie dazu dieselben Argumentwerte wie beim Ausführen des Befehls create.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard
Sie sollten eine Antwort mit den zehn besten Spielern aller Zeiten sehen, die in etwa so aussieht:
PlayerId: 4018687297 PlayerName: Player 83 Score: 999,618 Timestamp: 2017-07-01
PlayerId: 4018687297 PlayerName: Player 83 Score: 998,956 Timestamp: 2017-09-02
PlayerId: 4285713246 PlayerName: Player 51 Score: 998,648 Timestamp: 2017-12-01
PlayerId: 5267931774 PlayerName: Player 49 Score: 997,733 Timestamp: 2017-11-09
PlayerId: 1981654448 PlayerName: Player 35 Score: 997,480 Timestamp: 2018-12-06
PlayerId: 4953940705 PlayerName: Player 87 Score: 995,184 Timestamp: 2018-09-14
PlayerId: 2456736905 PlayerName: Player 84 Score: 992,881 Timestamp: 2017-04-14
PlayerId: 8234617611 PlayerName: Player 19 Score: 992,399 Timestamp: 2017-12-27
PlayerId: 1788051688 PlayerName: Player 76 Score: 992,265 Timestamp: 2018-11-22
PlayerId: 7127686505 PlayerName: Player 97 Score: 992,038 Timestamp: 2017-12-02
Führen Sie nun den Befehl query mit den erforderlichen Argumenten aus, um die zehn besten Spieler des Jahres abzufragen. Geben Sie dazu einen „timespan“ an, der der Anzahl der Stunden in einem Jahr entspricht, also 8.760.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 8760
Sie sollten eine Antwort mit den zehn besten Spielern des Jahres sehen, die so aussieht:
PlayerId: 1981654448 PlayerName: Player 35 Score: 997,480 Timestamp: 2018-12-06
PlayerId: 4953940705 PlayerName: Player 87 Score: 995,184 Timestamp: 2018-09-14
PlayerId: 1788051688 PlayerName: Player 76 Score: 992,265 Timestamp: 2018-11-22
PlayerId: 6862349579 PlayerName: Player 30 Score: 990,877 Timestamp: 2018-09-14
PlayerId: 5529627211 PlayerName: Player 16 Score: 989,142 Timestamp: 2018-03-30
PlayerId: 9743904155 PlayerName: Player 1 Score: 988,765 Timestamp: 2018-05-30
PlayerId: 6809119884 PlayerName: Player 7 Score: 986,673 Timestamp: 2018-05-16
PlayerId: 2132710638 PlayerName: Player 54 Score: 983,108 Timestamp: 2018-09-11
PlayerId: 2320093590 PlayerName: Player 79 Score: 981,373 Timestamp: 2018-05-07
PlayerId: 9554181430 PlayerName: Player 80 Score: 981,087 Timestamp: 2018-06-21
Führen Sie nun den Befehl query aus, um die zehn besten Spieler des Monats abzufragen. Geben Sie dazu einen „timespan“ an, der der Anzahl der Stunden in einem Monat entspricht, also 730.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 730
Sie sollten eine Antwort mit den zehn besten Spielern des Monats sehen, die in etwa so aussieht:
PlayerId: 3869829195 PlayerName: Player 69 Score: 949,686 Timestamp: 2019-02-19
PlayerId: 7448359883 PlayerName: Player 20 Score: 938,998 Timestamp: 2019-02-07
PlayerId: 1981654448 PlayerName: Player 35 Score: 929,003 Timestamp: 2019-02-22
PlayerId: 9336678658 PlayerName: Player 44 Score: 914,106 Timestamp: 2019-01-27
PlayerId: 6968576389 PlayerName: Player 40 Score: 898,041 Timestamp: 2019-02-21
PlayerId: 5529627211 PlayerName: Player 16 Score: 896,433 Timestamp: 2019-01-29
PlayerId: 9395039625 PlayerName: Player 59 Score: 879,495 Timestamp: 2019-02-09
PlayerId: 2094604854 PlayerName: Player 39 Score: 860,434 Timestamp: 2019-02-01
PlayerId: 9395039625 PlayerName: Player 59 Score: 849,955 Timestamp: 2019-02-21
PlayerId: 4285713246 PlayerName: Player 51 Score: 805,654 Timestamp: 2019-02-02
Führen Sie nun den Befehl query aus, um die zehn besten Spieler der Woche abzufragen. Geben Sie dazu einen „timespan“ (Zeitraum) an, der der Anzahl der Stunden in einer Woche entspricht, also 168.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 168
Sie sollten eine Antwort mit den zehn besten Spielern der Woche sehen, die so aussieht:
PlayerId: 3869829195 PlayerName: Player 69 Score: 949,686 Timestamp: 2019-02-19
PlayerId: 1981654448 PlayerName: Player 35 Score: 929,003 Timestamp: 2019-02-22
PlayerId: 6968576389 PlayerName: Player 40 Score: 898,041 Timestamp: 2019-02-21
PlayerId: 9395039625 PlayerName: Player 59 Score: 849,955 Timestamp: 2019-02-21
PlayerId: 5954045812 PlayerName: Player 8 Score: 795,639 Timestamp: 2019-02-22
PlayerId: 3889939638 PlayerName: Player 71 Score: 775,252 Timestamp: 2019-02-21
PlayerId: 5529627211 PlayerName: Player 16 Score: 604,695 Timestamp: 2019-02-19
PlayerId: 9006728426 PlayerName: Player 3 Score: 457,208 Timestamp: 2019-02-22
PlayerId: 8289497066 PlayerName: Player 58 Score: 227,697 Timestamp: 2019-02-20
PlayerId: 8065482904 PlayerName: Player 99 Score: 198,429 Timestamp: 2019-02-24
Super!
Wenn Sie jetzt Datensätze hinzufügen, skaliert Cloud Spanner Ihre Datenbank auf die benötigte Größe. Ganz gleich, wie groß Ihre Datenbank wird, die Bestenliste Ihres Spiels kann mit Cloud Spanner und der Truetime-Technologie weiterhin genau skaliert werden.
7. Bereinigen
Nachdem wir uns mit Spanner ausgetobt haben, müssen wir unseren Spielplatz aufräumen, um wertvolle Ressourcen und Geld zu sparen. Glücklicherweise ist das ein einfacher Schritt. Rufen Sie einfach den Cloud Spanner-Bereich der Cloud Console auf und löschen Sie die Instanz, die wir im Codelab-Schritt „Cloud Spanner-Instanz einrichten“ erstellt haben.
8. Glückwunsch!
Behandelte Themen:
- Google Cloud Spanner-Instanzen, -Datenbanken und Tabellenschema für eine Bestenliste
- Java-Konsolenanwendung erstellen
- Spanner-Datenbank und -Tabellen mit der Java-Clientbibliothek erstellen
- Daten mit der Java-Clientbibliothek in eine Spanner-Datenbank laden
- Die „Top 10“-Ergebnisse aus Ihren Daten mit Spanner-Commit-Zeitstempeln und der Java-Clientbibliothek abfragen
Vorgehensweise:
- CAP-Whitepaper zu Spanner lesen
- Best Practices für Schemadesign und Abfragen
- Weitere Informationen zu Commit-Zeitstempeln in Cloud Spanner
Feedback geben
- Bitte nehmen Sie sich einen Moment Zeit, um an unserer kurzen Umfrage teilzunehmen.