Google Cloud Spanner est un service de base de données relationnelle entièrement géré, distribué à l'échelle mondiale et évolutif horizontalement qui fournit des transactions ACID et une sémantique SQL sans compromis sur les performances et le niveau de disponibilité.
Dans cet atelier, vous allez apprendre à configurer une instance Cloud Spanner. Au fil des étapes, vous allez créer une base de données et un schéma qui peuvent être utilisés pour un classement de jeu. Vous allez commencer par créer une table "Players" (Joueurs) pour stocker les informations concernant les joueurs et un tableau "Scores" pour stocker les scores des joueurs.
Vous remplirez ensuite ces tables avec des exemples de données. Vous finirez l'atelier en exécutant quelques exemples de requêtes de "Top 10" avant de supprimer l'instance pour libérer les ressources.
Points abordés
- Comment configurer une instance Cloud Spanner.
- Comment créer une base de données et des tables.
- Comment utiliser une colonne d'horodatage de commit.
- Comment charger des données dans une table de base de données Cloud Spanner avec des horodatages.
- Comment interroger votre base de données Cloud Spanner.
- Comment supprimer une instance Cloud Spanner.
Prérequis
Comment allez-vous utiliser ce tutoriel ?
Quel est votre niveau d'expérience avec Google Cloud Platform ?
Configuration de l'environnement au rythme de chacun
Si vous ne possédez pas encore de compte Google (Gmail ou Google Apps), vous devez en créer un. Connectez-vous à la console Google Cloud Platform (console.cloud.google.com) et créez un projet.
Si vous avez déjà un projet, cliquez sur le menu déroulant de sélection du projet dans l'angle supérieur gauche de la console :
Cliquez ensuite sur le bouton "NEW PROJECT" (NOUVEAU PROJET) dans la boîte de dialogue qui s'affiche pour créer un projet :
Si vous n'avez pas encore de projet, une boîte de dialogue semblable à celle-ci apparaîtra pour vous permettre d'en créer un :
La boîte de dialogue de création de projet suivante vous permet de saisir les détails de votre nouveau projet :
Notez l'ID du projet. Il s'agit d'un nom unique pour tous les projets Google Cloud, ce qui implique que le nom ci-dessus n'est plus disponible pour vous… Désolé ! Tout au long de cet atelier de programmation, nous utiliserons PROJECT_ID
pour faire référence à cet ID.
Ensuite, si ce n'est pas déjà fait, vous devez activer la facturation dans Developers Console afin de pouvoir utiliser les ressources Google Cloud puis activer l'API Cloud Spanner.
Suivre cet atelier de programmation ne devrait pas vous coûter plus d'un euro. Cependant, cela peut s'avérer plus coûteux si vous décidez d'utiliser davantage de ressources ou si vous n'interrompez pas les ressources (voir la section "Effectuer un nettoyage" à la fin du présent document). Les tarifs de Google Cloud Spanner sont décrits sur cette page.
Les nouveaux utilisateurs de Google Cloud Platform peuvent bénéficier d'un Essai gratuit avec 300 $ de crédits afin de suivre gratuitement le présent atelier.
Configuration de Google Cloud Shell
Bien que Google Cloud et Spanner puissent être utilisés à distance depuis votre ordinateur portable, nous allons utiliser Google Cloud Shell pour cet atelier de programmation, un environnement de ligne de commande exécuté dans le cloud.
Cette machine virtuelle basée sur Debian contient tous les outils de développement dont vous aurez besoin. Elle intègre un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Cela signifie que tout ce dont vous avez besoin pour cet atelier de programmation est un navigateur (oui, tout fonctionne sur un Chromebook).
- Pour activer Cloud Shell à partir de Cloud Console, cliquez simplement sur Activer Cloud Shell (l'opération de provisionnement et la connexion à l'environnement ne devraient prendre que quelques minutes).
Une fois connecté à Cloud Shell, vous êtes normalement déjà authentifié et le projet PROJECT_ID
est sélectionné :
gcloud auth list
Résultat de la commande
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Résultat de la commande
[core] project = <PROJECT_ID>
Si, pour une raison quelconque, le projet n'est pas défini, exécutez simplement la commande suivante :
gcloud config set project <PROJECT_ID>
Vous recherchez votre PROJECT_ID
? Vérifiez l'ID que vous avez utilisé pendant les étapes de configuration ou recherchez-le dans le tableau de bord Cloud Console :
Par défaut, Cloud Shell définit certaines variables d'environnement qui pourront s'avérer utiles pour exécuter certaines commandes dans le futur.
echo $GOOGLE_CLOUD_PROJECT
Résultat de la commande
<PROJECT_ID>
- Pour finir, définissez la configuration du projet et de la zone par défaut :
gcloud config set compute/zone us-central1-f
Vous pouvez choisir parmi différentes zones. Pour en savoir plus, consultez la page Régions et zones.
Résumé
Cette étape consiste à configurer votre environnement.
Étapes suivantes
Vous allez maintenant configurer une instance Cloud Spanner.
Dans cette étape, nous allons configurer notre instance Cloud Spanner pour cet atelier de programmation. Recherchez l'entrée Spanner dans le menu principal en haut à gauche ou recherchez Spanner en appuyant sur "/" et saisissez "Spanner"
Cliquez ensuite sur et remplissez le formulaire en renseignant le nom d'instance cloudspanner-leaderboard, en choisissant une configuration (sélectionnez une instance régionale), puis en définissant le nombre de nœuds (pour le présent atelier, nous n'aurons besoin que d'un seul nœud). Pour les instances en production et pour bénéficier du contrat de niveau de service de Cloud Spanner, vous devez exécuter au moins trois nœuds dans votre instance Cloud Spanner.
Dernière étape mais pas des moindres, cliquez sur "Créer". L'instance Cloud Spanner sera prête en quelques secondes.
Pour l'étape suivante, nous allons utiliser la bibliothèque cliente Java afin de créer une base de données et un schéma dans notre nouvelle instance.
Au cours de cette étape, nous allons créer notre exemple de base de données et notre schéma.
Utilisons la bibliothèque cliente Java pour créer deux tables : une table "Players" (Joueurs) pour stocker les informations concernant les joueurs et une table "Scores" pour stocker les scores des joueurs. Pour ce faire, nous allons vous indiquer comment créer une application de console Java dans Cloud Shell.
Commencez par cloner l'exemple de code du présent atelier de programmation à partir de GitHub en saisissant la commande suivante dans Cloud Shell :
git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
Accédez ensuite au répertoire "applications" dans lequel vous allez créer votre application.
cd java-docs-samples/spanner/leaderboard
L'ensemble du code requis pour cet atelier de programmation se trouve dans le répertoire java-docs-samples/spanner/leaderboard/complete
existant en tant qu'application C# exécutable Leaderboard
, pour vous servir de référence tout au long de cet atelier. Nous allons créer un répertoire et créer une copie de l'application de classement étape par étape.
Créez un répertoire nommé "codelab" pour l'application et accédez-y avec la commande suivante :
mkdir codelab && cd $_
Créez une application Java de base nommée "Leaderboard" à l'aide de la commande Maven (mvn) suivante :
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.google.codelabs -DartifactId=leaderboard -DarchetypeVersion=1.4
Cette commande crée une application de console simple composée de deux fichiers principaux : le fichier de configuration de l'application Maven pom.xml
et le fichier d'application Java App.java
.
Accédez ensuite au répertoire "leaderboard" qui vient d'être créé et répertoriez-en le contenu :
cd leaderboard && ls
Vous devriez voir le fichier pom.xml
et le répertoire src
:
pom.xml src
Vous allez maintenant mettre à jour cette application de console en modifiant App.java
pour utiliser la bibliothèque cliente Java Spanner afin de créer un classement composé de deux tables, "Players" (Joueurs) et "Scores". Vous pouvez faire tout cela directement depuis l'éditeur Cloud Shell :
Ouvrez l'éditeur Cloud Shell en cliquant sur l'icône en surbrillance ci-dessous :
Ouvrez le fichier pom.xml dans le dossier "leaderboard". Ouvrez le fichier pom.xml situé dans le dossier java-docs-samples\ spanner\leaderboard\codelab\leaderboard
. Ce fichier configure le système de compilation Maven afin de compiler notre application dans un fichier jar en incluant toutes nos dépendances.
Ajoutez la nouvelle section de gestion des dépendances ci-dessous directement sous l'élément </properties> existant :
<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>
Ajoutez également une nouvelle dépendance dans la section <dependencies> existante afin d'ajouter la bibliothèque cliente Java Cloud Spanner à l'application.
<dependency>
<!-- Version auto-managed by BOM -->
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner</artifactId>
</dependency>
Remplacez ensuite la section <build> existante du fichier pom.xml
par la section <build> ci-dessous :
<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>
Enregistrez les modifications apportées au fichier pom.xml en sélectionnant "Enregistrer" dans le menu "Fichier" de l'éditeur Cloud Shell ou en appuyant simultanément sur les touches "Ctrl" et "S".
Ensuite, ouvrez le fichier App.java
situé dans le dossier src/main/java/com/google/codelabs/
avec l'éditeur Cloud Shell. Remplacez le code existant du fichier par le code requis pour créer la base de données leaderboard
et les tables Players
et Scores
en collant le code Java suivant dans le fichier App.java
:
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");
}
}
Enregistrez les modifications apportées au fichier App.java
en sélectionnant "Enregistrer" dans le menu "Fichier " de l'éditeur Cloud Shell.
Vous pouvez utiliser le fichier App.java
situé dans le répertoire java-docs-samples/spanner/leaderboard/step4/src
comme référence de ce à quoi doit ressembler votre fichier App.java
après l'ajout du code pour activer la commande create
.
Pour créer votre application, exécutez le package mvn à partir du répertoire où se trouve votre fichier pom.xml
:
mvn package
Une fois votre fichier JAR Java créé, exécutez l'application obtenue dans Cloud Shell à l'aide de la commande suivante :
java -jar target/leaderboard.jar
Vous devriez voir une sortie semblable à ce qui suit.
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.
À partir de cette réponse, nous pouvons voir qu'il s'agit bien de l'application Leaderboard
qui possède actuellement une seule commande possible : create
. Nous constatons que les arguments attendus de la commande create
sont les suivants : ID d'instance et ID de base de données.
Exécutez maintenant la commande suivante :
java -jar target/leaderboard.jar create cloudspanner-leaderboard leaderboard
Après quelques secondes, une réponse de ce type doit s'afficher :
Created database [projects/your-project/instances/cloudspanner-leaderboard/databases/leaderboard]
Dans la section Cloud Spanner de Cloud Console, vous devriez voir votre nouvelle base de données et vos tables dans le menu de gauche.
À l'étape suivante, nous mettrons à jour notre application afin de charger des données dans votre nouvelle base de données.
Nous disposons à présent d'une base de données appelée leaderboard
et contenant deux tables, Players
et Scores
. Utilisons maintenant la bibliothèque cliente Java pour remplir la table Players
avec des joueurs et la table Scores
avec des scores aléatoires pour chaque joueur.
S'il n'est pas déjà ouvert, ouvrez l'éditeur Cloud Shell en cliquant sur l'icône en surbrillance ci-dessous :
Ensuite, modifiez le fichier App.java
dans l'éditeur Cloud Shell afin d'ajouter une commande insert
pouvant être utilisée pour insérer 100 joueurs dans la table Players
, ou pour insérer quatre scores aléatoires dans le fichier Scores
pour chaque joueur dans la table Players
.
Commencez par mettre à jour la section imports
en haut du fichier en la remplaçant de sorte à obtenir le résultat ci-dessous :
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;
Ajoutez ensuite les méthodes "insert", "insertPlayers" et "insertScores" ci-dessous sous la méthode create()
existante et au-dessus de la méthode printUsageAndExit()
existante :
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...");
}
}
Pour que la commande insert
fonctionne, ajoutez le code suivant à la méthode "main" de votre application dans l'instruction switch (command)
:
case "insert":
String insertType;
try {
insertType = args[3];
} catch (ArrayIndexOutOfBoundsException exception) {
insertType = "";
}
insert(dbClient, insertType);
break;
Une fois l'opération terminée, l'instruction switch (command)
doit ressembler à ceci :
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();
}
Pour finaliser l'ajout de la fonctionnalité "insert" à votre application, la dernière étape consiste à ajouter un texte d'aide pour la commande "insert" dans la méthode printUsageAndExit()
. Ajoutez le texte d'aide suivant à la méthode printUsageAndExit()
afin d'inclure le texte d'aide pour les commandes d'insertion :
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");
Enregistrez les modifications apportées au fichier App.java
en sélectionnant "Enregistrer" dans le menu "Fichier " de l'éditeur Cloud Shell.
Vous pouvez utiliser le fichier App.java
du répertoire java-docs-samples/spanner/leaderboard/step5/src
comme référence de ce à quoi doit ressembler votre fichier App.java
après l'ajout du code pour activer la commande insert
.
Recompilons et exécutez l'application pour vérifier que la nouvelle commande insert
est bien incluse dans la liste des commandes possibles.
Pour compiler votre application, exécutez mvn package
à partir du répertoire où se trouve le fichier pom.xml :
mvn package
Une fois votre fichier JAR Java créé, exécutez la commande suivante :
java -jar target/leaderboard.jar
La commande insert
devrait maintenant être incluse dans la sortie par défaut de l'application :
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.
Vous pouvez voir dans la réponse qu'en plus de l'ID d'instance et de l'ID de base de données, un autre argument dont la valeur peut être "players" ou "scores" est apparu.
Nous allons maintenant exécuter la commande insert
avec les mêmes valeurs d'argument que celles utilisées lors de l'appel de la commande create
en ajoutant "players" comme argument "type of insert" supplémentaire.
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard players
Après quelques secondes, une réponse de ce type doit s'afficher :
Done inserting player records...
Utilisons maintenant la bibliothèque cliente Java pour remplir notre table Scores
avec quatre scores et horodatages aléatoires pour chaque joueur dans la table Players
.
La colonne Timestamp
de la table Scores
a été définie comme colonne d'horodatage de commit ("commit timestamp") via l'instruction SQL suivante, exécutée lors de l'exécution de la commande create
:
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
Notez l'attribut OPTIONS(allow_commit_timestamp=true)
. Cela fait de Timestamp
une colonne d'horodatage de commit qu'il est possible de renseigner avec l'horodatage de transaction exact des opérations INSERT et UPDATE sur une ligne de table donnée.
Vous pouvez également insérer vos propres valeurs d'horodatage dans une colonne "commit timestamp" à condition d'utiliser un horodatage situé dans le passé, ce qui est exactement ce que nous avons fait dans le présent atelier de programmation.
Nous allons maintenant exécuter la commande insert
avec les mêmes valeurs d'argument que celles utilisées lors de l'appel de la commande create
en ajoutant "scores" comme argument "type of insert" supplémentaire.
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard scores
Après quelques secondes, une réponse de ce type doit s'afficher :
Done inserting score records...
L'exécution de insert
avec scores
spécifié comme argument "type of insert" supplémentaire appelle la méthode insertScores
. Celle-ci utilise les extraits de code ci-dessous pour insérer un horodatage généré de manière aléatoire avec une date et une heure dans le passé :
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())
Pour automatiquement remplir la colonne Timestamp
avec l'horodatage correspondant à l'heure exacte de la transaction "Insert", vous pouvez insérer la constante Java Value.COMMIT_TIMESTAMP
comme dans l'extrait de code suivant :
.bind("Timestamp")
.to(Value.COMMIT_TIMESTAMP)
Maintenant que le chargement des données est terminé, vérifions les valeurs que nous venons d'écrire dans les nouvelles tables. Sélectionnez d'abord la base de données leaderboard
, puis la table Players
. Cliquez sur l'onglet Data
. Vous devriez voir des données dans les colonnes PlayerId
et PlayerName
de la table.
Vérifions maintenant le tableau des scores contient bien des données en cliquant sur le tableau Scores
et en sélectionnant l'onglet Data
. Vous devriez voir des données dans les colonnes PlayerId
, Timestamp
et Score
de la table.
Bravo ! Mettons à jour notre application pour exécuter des requêtes qui nous permettront de créer un classement de jeu.
Maintenant que nous avons configuré notre base de données et chargé des informations dans nos tables, nous allons créer un classement à l'aide de ces données. Pour ce faire, nous devons répondre aux quatre questions suivantes :
- Quels joueurs sont les "Top 10" joueurs de tous les temps ?
- Quels joueurs sont les "Top 10" joueurs de l'année ?
- Quels joueurs sont les "Top 10" joueurs du mois ?
- Quels joueurs sont les "Top 10" joueurs de la semaine ?
Mettons à jour notre application pour exécuter les requêtes SQL qui répondent à ces questions.
Nous allons ajouter une commande query
afin d'exécuter les requêtes permettant de répondre à ces questions en générant les informations requises pour notre classement.
Modifiez le fichier App.java
dans l'éditeur Cloud Shell afin de mettre à jour l'application et d'ajouter une commande query
. La commande query
est composée de deux méthodes query
, l'une n'utilisant qu'un argument DatabaseClient
et l'autre acceptant un argument timespan
supplémentaire pour faciliter le filtrage des résultats en fonction d'une période spécifiée en nombre d'heures.
Ajoutez les deux méthodes query
suivantes sous la méthode insertScores()
existante et au-dessus de la méthode printUsageAndExit()
existante :
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));
}
}
Pour que la commande query
fonctionne, ajoutez le code suivant à l'instruction switch(command)
dans la méthode "main" de votre application :
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;
La dernière étape pour finaliser l'ajout de la fonctionnalité "query" à votre application consiste à ajouter du texte d'aide pour la commande "query" dans la méthode printUsageAndExit()
. Ajoutez les lignes de code suivantes à la méthode printUsageAndExit()
afin d'inclure du texte d'aide pour la commande "query :
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");
Enregistrez les modifications apportées au fichier App.java
en sélectionnant "Enregistrer" dans le menu "Fichier " de l'éditeur Cloud Shell.
Vous pouvez utiliser le fichier App.java
du répertoire dotnet-docs-samples/applications/leaderboard/step6/src
comme référence de ce à quoi doit ressembler votre fichier App.java
après l'ajout du code pour activer la commande query
.
Pour compiler votre application, exécutez mvn package
à partir du répertoire où se trouve le fichier pom.xml :
mvn package
Nous allons maintenant exécuter l'application pour confirmer que la nouvelle commande query
est bien incluse dans la liste des commandes possibles de l'application. Exécutez la commande suivante :
java -jar target/leaderboard.jar
La commande query
devrait maintenant être incluse dans la sortie par défaut de l'application en tant que nouvelle option :
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.
Vous pouvez voir dans la réponse qu'en plus des arguments d'ID d'instance et d'ID de base de données, la commande query
accepte un argument facultatif "timespan" (exprimé en heures) pour filtrer les enregistrements en fonction de leur valeur dans la colonne Timestamp
du tableau Scores
. L'argument "timespan" étant facultatif, aucun filtrage par horodatage ne sera appliqué si l'argument n'est pas inclus. Nous pouvons donc utiliser la commande query
sans argument "timespan" pour obtenir la liste des joueurs du "Top 10" sur l'intégralité des données disponibles.
Exécutons la commande query
sans spécifier de valeur "timespan", en utilisant les mêmes valeurs d'argument que celles utilisées lors de l'exécution de la commande create
.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard
Vous devriez voir une réponse similaire à celle ci-dessous et incluant les joueurs du "Top 10" :
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
Exécutez maintenant la commande query
avec les arguments nécessaires pour obtenir les joueurs du "Top 10" de l'année en spécifiant une valeur "timespan" égale au nombre d'heures dans une année (8760).
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 8760
Vous devriez voir une réponse similaire à celle ci-dessous et incluant les joueurs du "Top 10" de l'année :
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
À présent, vous allez exécuter la commande query
pour obtenir les joueurs du "Top 10 " du mois en spécifiant une valeur "timespan" égale au nombre d'heures dans un mois (730).
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 730
Vous devriez voir une réponse similaire à celle ci-dessous et incluant les joueurs du "Top 10" du mois :
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
À présent, exécutez la commande query
pour obtenir les joueurs du "Top 10 " de la semaine en spécifiant une valeur "timespan" égale au nombre d'heures dans une semaine (168).
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 168
Vous devriez voir une réponse similaire à celle ci-dessous et incluant les joueurs du "Top 10" de la semaine :
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
Beau travail !
À mesure que vous ajoutez des enregistrements, Cloud Spanner fait évoluer votre base de données en fonction de vos besoins. Quel que soit le volume de votre base de données, le classement de votre jeu peut continuer d'évoluer avec précision grâce à Cloud Spanner et sa technologie Truetime.
Après s'être amusé avec Spanner, il faut maintenant nettoyer notre aire de jeux pour ne gaspiller de préciseuses ressources et donc d'argent. Heureusement, il s'agit d'une étape très facile puisqu'il vous suffit d'accéder à la section Cloud Spanner de Cloud Console et de supprimer l'instance que nous avons créée à l'étape "Configurer une instance Cloud Spanner".
Points abordés :
- Instances, bases de données et schéma de table Google Cloud Spanner pour un classement.
- Comment créer une application de console Java.
- Comment créer une base de données et des tables Spanner à l'aide de la bibliothèque cliente Java.
- Comment charger des données dans une base de données Spanner à l'aide de la bibliothèque cliente Java.
- Comment interroger les résultats du "Top 10" à partir de vos données à l'aide des horodatages de commit Spanner et de la bibliothèque cliente Java.
Prochaines étapes :
- Lisez le Livre blanc de Spanner CAP
- Découvrez la Conception de schémas et les bonnes pratiques relatives aux requêtes.
- Découvrez plus en détail les horodatages de commit de Cloud Spanner
Votre avis nous intéresse !
- Veuillez prendre quelques minutes pour répondre à notre courte enquête.