O Google Cloud Spanner é um serviço de banco de dados relacional horizontalmente escalonável e distribuído globalmente que fornece transações ACID e semântica SQL sem deixar de oferecer desempenho e alta disponibilidade.
Neste laboratório, você aprenderá a configurar uma instância do Cloud Spanner. Você conhecerá as etapas para criar um banco de dados e um esquema que possam ser usados em um placar de jogos. Primeiro, crie uma tabela de jogadores para armazenar informações sobre o jogador e uma tabela de pontuações para armazenar as pontuações dos jogadores.
Em seguida, preencha as tabelas com dados de amostra. Para concluir o laboratório, execute algumas consultas de exemplo do Top 10 e, por fim, exclua a instância para liberar recursos.
O que você aprenderá
- Como configurar uma instância do Cloud Spanner.
- Como criar um banco de dados e tabelas.
- Como usar uma coluna de carimbo de data/hora de confirmação.
- Como carregar dados em uma tabela de banco de dados do Cloud Spanner com carimbos de data/hora.
- Como consultar o banco de dados do Cloud Spanner.
- Como excluir uma instância do Cloud Spanner.
Pré-requisitos
Como você usará este tutorial?
Como você classificaria sua experiência com o Google Cloud Platform?
Configuração de ambiente personalizada
Se você ainda não tem uma Conta do Google (Gmail ou Google Apps), crie uma. Faça login no Console do Google Cloud Platform ( console.cloud.google.com) e crie um novo projeto.
Se você já tiver um projeto, clique no menu suspenso de seleção no canto superior esquerdo do console:
e clique no botão "NEW PROJECT" na caixa de diálogo exibida para criar um novo projeto:
Se você ainda não tiver um projeto, uma caixa de diálogo como esta será exibida para criar seu primeiro:
A caixa de diálogo de criação de projeto subsequente permite que você insira os detalhes do novo projeto:
Lembre-se do código do projeto, um nome exclusivo em todos os projetos do Google Cloud. O nome acima já foi escolhido e não servirá para você. Faremos referência a ele mais adiante neste codelab como PROJECT_ID
.
Em seguida, será preciso ativar o faturamento no Developers Console para usar os recursos do Google Cloud e ativar a API Cloud Spanner, caso ainda não tenha feito isso.
A execução por meio deste codelab terá um custo baixo, mas poderá ser mais se você decidir usar mais recursos ou se deixá-los em execução. Consulte a seção "limpeza" no final deste documento. Os preços do Google Cloud Spanner estão documentados neste link.
Novos usuários do Google Cloud Platform estão qualificados para uma avaliação gratuita de US$ 300, o que torna este codelab totalmente gratuito.
Configuração do Google Cloud Shell
Embora o Google Cloud e o Spanner possam ser operados remotamente do seu laptop, neste codelab usaremos o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.
O Cloud Shell é uma máquina virtual com base em Debian que contém todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Isso significa que tudo que você precisa para este codelab é um navegador (sim, funciona em um Chromebook).
- Para ativar o Cloud Shell no Console do Cloud, basta clicar em Ativar o Cloud Shell . Leva apenas alguns instantes para provisionar e se conectar ao ambiente.
Depois de se conectar ao Cloud Shell, você já estará autenticado e o projeto estará configurado com seu PROJECT_ID
.
gcloud auth list
Resposta ao comando
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Resposta ao comando
[core] project = <PROJECT_ID>
Se, por algum motivo, o projeto não estiver definido, basta emitir o seguinte comando:
gcloud config set project <PROJECT_ID>
Quer encontrar seu PROJECT_ID
? Veja qual ID você usou nas etapas de configuração ou procure-o no painel do Console do Cloud:
O Cloud Shell também define algumas variáveis de ambiente por padrão, o que pode ser útil ao executar comandos futuros.
echo $GOOGLE_CLOUD_PROJECT
Resposta ao comando
<PROJECT_ID>
- Defina a zona padrão e a configuração do projeto:
gcloud config set compute/zone us-central1-f
É possível escolher uma variedade de zonas diferentes. Para mais informações, consulte Regiões e zonas.
Resumo
Nesta etapa, você configurará seu ambiente.
A seguir
Agora configure uma instância do Cloud Spanner.
Nesta etapa, configuramos a instância do Cloud Spanner para este codelab. Pesquise a entrada do Spanner no menu de navegação superior esquerdo ou procure o Spanner pressionando "/" e digite "Spanner".
Depois, clique em e preencha o formulário inserindo o nome da instância cloudspanner-leaderboard da sua instância, escolhendo uma configuração (selecione uma instância regional) e defina o número de nós para este codelab só precisaremos de um nó. Para instâncias de produção e se qualificar para o SLA do Cloud Spanner, você precisará executar três ou mais nós na sua instância do Cloud Spanner.
Por fim, mas não menos importante, clique em "Criar" e, em segundos, uma instância do Cloud Spanner está à sua disposição.
Na próxima etapa, usaremos a biblioteca do cliente Java para criar um banco de dados e esquema em nossa nova instância.
Nesta etapa, criaremos nosso banco de dados e esquema de amostra.
Vamos usar a biblioteca de cliente Java para criar duas tabelas, uma tabela de jogadores para informações do jogador e uma tabela de pontuações para armazenar pontuações de jogadores. Para isso, mostraremos as etapas para criar um aplicativo de console Java no Cloud Shell.
Primeiro, clone o código de amostra do codelab digitando o seguinte comando no Cloud Shell:
git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
Em seguida, mude para o diretório "applications" onde você criará o aplicativo.
cd java-docs-samples/spanner/leaderboard
Todo o código necessário para este codelab está localizado no diretório java-docs-samples/spanner/leaderboard/complete
existente como um aplicativo C# executável como Leaderboard
para servir como referência enquanto você avança no codelab. Criaremos um novo diretório e criaremos uma cópia do aplicativo Leaderboard em etapas.
Crie um novo diretório chamado "codelab" para o aplicativo e altere o diretório nele para ele usando o seguinte comando:
mkdir codelab && cd $_
Crie um novo aplicativo Java básico chamado "Leaderboard" usando o seguinte comando do Maven (mvn):
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.google.codelabs -DartifactId=leaderboard -DarchetypeVersion=1.4
Esse comando cria um aplicativo de console simples com dois arquivos principais, o arquivo de configuração do aplicativo Maven pom.xml
e o arquivo de aplicativo Java App.java
.
Depois, altere o diretório para o diretório do placar que foi criado e liste o conteúdo:
cd leaderboard && ls
Você verá o arquivo pom.xml
e o diretório src
listados:
pom.xml src
Agora, vamos atualizar este aplicativo de console editando o App.java
para usar a biblioteca de cliente Java Spanner para criar um placar de formação de duas tabelas; Jogadores e pontuações. É possível fazer isso diretamente no editor do Cloud Shell:
Abra o editor do Cloud Shell clicando no ícone destacado abaixo:
Abra o pom.xml na pasta do placar. Abra o arquivo pom.xml localizado na pasta java-docs-samples\ spanner\leaderboard\codelab\leaderboard
. Esse arquivo configura o sistema de compilação do Maven para criar nosso aplicativo em um jar, incluindo todas as nossas dependências.
Adicione a nova seção de gerenciamento de dependências a seguir logo abaixo do elemento </properties>:
<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>
Adicione também uma nova dependência na seção <dependencies> existente, que adicionará a biblioteca de cliente Java do Cloud Spanner ao aplicativo.
<dependency>
<!-- Version auto-managed by BOM -->
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-spanner</artifactId>
</dependency>
Em seguida, substitua a seção <build> do arquivo pom.xml
pela seção <build> a seguir:
<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>
Salve as alterações feitas no arquivo pom.xml selecionando "Salvar" no menu "Arquivo" do editor do Cloud Shell ou pressionando as teclas de atalho "Ctrl" e "S" juntas.
Em seguida, abra o arquivo App.java
no editor do Cloud Shell localizado na pasta src/main/java/com/google/codelabs/
. Substitua o código existente do arquivo pelo código necessário para criar o banco de dados leaderboard
e as tabelas Players
e Scores
colando o seguinte código Java no arquivo 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");
}
}
Salve as alterações feitas no arquivo App.java
selecionando "Salvar" no menu "Arquivo" do editor do Cloud Shell.
Use o arquivo App.java
no diretório java-docs-samples/spanner/leaderboard/step4/src
para ver um exemplo de como seu arquivo App.java
deve ficar depois de você adicionar o código para ativar o comando create
.
Para criar seu app, execute o pacote mvn no diretório em que seu pom.xml
está localizado:
mvn package
Após criar o arquivo jar Java, execute o aplicativo resultante no Cloud Shell inserindo o seguinte comando:
java -jar target/leaderboard.jar
O resultado será assim:
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.
Nesta resposta, vemos que este é o aplicativo Leaderboard
que atualmente tem um comando possível: create
. Os argumentos esperados do comando create
são o código da instância e do banco de dados.
Agora, execute o seguinte comando.
java -jar target/leaderboard.jar create cloudspanner-leaderboard leaderboard
Após alguns segundos, será exibida uma resposta semelhante a esta:
Created database [projects/your-project/instances/cloudspanner-leaderboard/databases/leaderboard]
Na seção Cloud Spanner do Console do Cloud, você verá o novo banco de dados e as tabelas que aparecem no menu do lado esquerdo.
Na próxima etapa, atualizaremos o nosso aplicativo para carregar alguns dados no novo banco de dados.
Agora temos um banco de dados chamado leaderboard
contendo duas tabelas; Players
e Scores
. Agora, usaremos a biblioteca de cliente Java para preencher a tabela Players
com os jogadores e nossa tabela Scores
com pontuações aleatórias para cada jogador.
Se ele ainda não estiver aberto, clique no ícone destacado abaixo para abrir o editor do Cloud Shell:
Em seguida, edite o arquivo App.java
no editor do Cloud Shell para adicionar um comando insert
que possa ser usado para inserir 100 jogadores na tabela Players
ou para inserir quatro pontuações aleatórias em Scores
tabela para cada jogador na tabela Players
.
Primeiro, atualize a seção imports
na parte superior do arquivo do app, substituindo o que está lá, de modo que, quando você terminar, o resultado será semelhante a este:
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;
Em seguida, adicione os seguintes métodos insert, insertPlayers e insertScores abaixo do método create()
existente e acima do método printUsageAndExit()
existente:
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...");
}
}
Em seguida, para fazer o comando insert
funcionar, adicione o seguinte código ao método "main" do app na instrução switch (command)
:
case "insert":
String insertType;
try {
insertType = args[3];
} catch (ArrayIndexOutOfBoundsException exception) {
insertType = "";
}
insert(dbClient, insertType);
break;
Depois de terminar, a instrução switch (command)
será semelhante a esta:
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();
}
A etapa final para concluir a adição da funcionalidade "inserir" ao seu aplicativo é adicionar o texto de ajuda ao comando "inserir" ao método printUsageAndExit()
. Adicione as seguintes linhas de código ao método printUsageAndExit()
para incluir o texto de ajuda para o comando "insert":
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");
Salve as alterações feitas no arquivo App.java
selecionando "Salvar" no menu "Arquivo" do editor do Cloud Shell.
Use o arquivo App.java
no diretório java-docs-samples/spanner/leaderboard/step5/src
para ver um exemplo de como seu arquivo App.java
deve ficar depois de você adicionar o código para ativar o comando insert
.
Agora, vamos recriar e executar o app para confirmar que o novo comando insert
está incluído na lista de comandos possíveis do app.
Para criar seu aplicativo, execute mvn package
no diretório em que seu pom.xml está localizado:
mvn package
Depois de criar o arquivo jar Java, execute o seguinte comando:
java -jar target/leaderboard.jar
Você verá o comando insert
agora incluído na saída padrão do app:
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.
É possível ver da resposta que, além do ID da instância e do ID do banco de dados, há outro argumento que pode ter um valor "players" ou "scores".
Agora, vamos executar o comando insert
com os mesmos valores de argumento que usamos quando chamamos o comando create
, adicionando "players" como o argumento adicional "tipo de inserção".
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard players
Após alguns segundos, será exibida uma resposta semelhante a esta:
Done inserting player records...
Agora, vamos usar a biblioteca de cliente Java para preencher nossa tabela Scores
com quatro pontuações aleatórias com carimbos de data/hora de cada jogador na tabela Players
.
A coluna Timestamp
da tabela Scores
foi definida como uma coluna de "carimbo de data/hora de confirmação" por meio da seguinte instrução SQL que foi executada quando executamos o comando create
anteriormente:
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
Observe o atributo OPTIONS(allow_commit_timestamp=true)
. Isso torna Timestamp
uma coluna de "timestamp de confirmação" e permite que ela seja preenchida automaticamente com o carimbo de data/hora exato da transação para operações INSERT e UPDATE em uma determinada linha da tabela.
Também é possível inserir seus próprios valores de carimbo de data/hora em uma coluna de "carimbo de data/hora de confirmação", desde que você insira um carimbo de data/hora com um valor que esteja no passado, que é o que vamos fazer para este fim de codelab.
Agora, vamos executar o comando insert
com os mesmos valores de argumento que usamos quando chamamos o comando create
adicionando "scores" como o argumento adicional "type of insert".
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard scores
Após alguns segundos, será exibida uma resposta semelhante a esta:
Done inserting score records...
Executar insert
com o "tipo de inserção" especificado como scores
chama o método insertScores
, que usa os seguintes snippets de código para inserir um carimbo de data/hora gerado aleatoriamente com uma data e hora no passado:
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())
Para preencher automaticamente a coluna Timestamp
com o carimbo de data/hora do momento exato em que a transação "Inserir" ocorre, insira a constante Java Value.COMMIT_TIMESTAMP
como no snippet de código a seguir:
.bind("Timestamp")
.to(Value.COMMIT_TIMESTAMP)
Agora que concluímos o carregamento de dados, vamos verificar os valores que acabamos de gravar nas nossas novas tabelas. Primeiro, selecione o banco de dados leaderboard
e depois a tabela Players
. Clique na guia Data
. Você verá dados nas colunas PlayerId
e PlayerName
da tabela.
Em seguida, para verificar se a tabela "Scores" também tem dados, clique na tabela Scores
e selecione a guia Data
. Você deve ter dados nas colunas PlayerId
, Timestamp
e Score
da tabela.
Parabéns! Vamos atualizar nosso app para executar algumas consultas que podemos usar para criar um placar de jogos.
Agora que configuramos o banco de dados e carregamos as informações nas tabelas, vamos criar um placar usando esses dados. Para fazer isso, precisamos responder a estas quatro perguntas:
- Quais jogadores são o "Top 10" de todos os tempos?
- Quais jogadores são os "Top 10" do ano?
- Quais jogadores são o "Top 10" do mês?
- Quais jogadores são os "Top 10" da semana?
Vamos atualizar nosso aplicativo para executar as consultas SQL que responderão a essas perguntas.
Adicionaremos um comando query
que fornecerá uma forma de executar as consultas de forma a responder às perguntas que produzirão as informações necessárias para nosso placar.
Edite o arquivo App.java
no editor do Cloud Shell para atualizar o app e adicionar um comando query
. O comando query
é composto por dois métodos query
, um que usa apenas um argumento DatabaseClient
e outro que usa um argumento timespan
extra para facilitar a filtragem de resultados por um período especificado em horas.
Adicione os dois métodos query
a seguir abaixo do método insertScores()
existente e acima do método printUsageAndExit()
atual:
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));
}
}
Em seguida, para fazer o comando query
funcionar, adicione o seguinte código à instrução switch(command)
no método "main" do seu app:
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;
A etapa final para concluir a adição da funcionalidade "query" ao seu aplicativo é adicionar o texto de ajuda ao comando "query" ao método printUsageAndExit()
. Adicione as seguintes linhas de código ao método printUsageAndExit()
para incluir o texto de ajuda no comando "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");
Salve as alterações feitas no arquivo App.java
selecionando "Salvar" no menu "Arquivo" do editor do Cloud Shell.
Use o arquivo App.java
no diretório dotnet-docs-samples/applications/leaderboard/step6/src
para ver um exemplo de como seu arquivo App.java
deve ficar depois de você adicionar o código para ativar o comando query
.
Para criar seu aplicativo, execute mvn package
no diretório em que seu pom.xml está localizado:
mvn package
Agora, vamos executar o aplicativo para confirmar se o novo comando query
está incluído na lista de comandos possíveis do aplicativo. Execute este comando:
java -jar target/leaderboard.jar
Você verá o comando query
agora incluído na saída padrão do app como uma nova opção de comando:
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.
É possível ver na resposta que, além dos argumentos do código da instância e do banco de dados, o comando query
nos permite especificar um período opcional em número de horas a ser usado para filtrar registros com base no valor deles na tabela Scores
Timestamp
. Como o argumento de tempo é opcional, significa que, se um argumento de tempo não for incluído, nenhum registro será filtrado por carimbos de data/hora. Assim, podemos usar o comando query
sem um valor de "período" para acessar uma lista dos nossos "Top 10" todos os tempos.
Execute o comando query
sem especificar um "timespan", usando os mesmos valores de argumento que usamos quando executamos o comando create
.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard
Você deverá ver uma resposta que inclui os players "Top 10" de todos os tempos, como a seguinte:
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
Agora, vamos executar o comando query
com os argumentos necessários para consultar os "Top 10" do ano especificando um "timespan" igual ao número de horas em um ano que é 8760.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 8760
Você deverá ver uma resposta que inclui os "Top 10" do ano, como a seguir:
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
Agora, vamos executar o comando query
para consultar os players "Top 10" do mês, especificando um "timespan" igual ao número de horas em um mês que é 730.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 730
Você verá uma resposta que inclui os "Top 10" do mês, como a seguinte:
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
Agora execute o comando query
para consultar os "Top 10" da semana, especificando um "timespan" igual ao número de horas em uma semana de 168.
java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 168
Você verá uma resposta que inclui os jogadores "Top 10" da semana, como a seguinte:
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
Ótimo trabalho!
Agora, conforme você adicionar registros, o Cloud Spanner fará o escalonamento do seu banco de dados para o tamanho que for necessário. Não importa o quanto seu banco de dados aumente, o placar do jogo pode continuar a ser dimensionado com a precisão do Cloud Spanner e da tecnologia Truetime.
Depois de se divertir com o Spanner, precisamos limpar nosso playground, economizando recursos preciosos e dinheiro. Felizmente, esta é uma etapa fácil, basta acessar a seção Cloud Spanner do Console do Cloud e excluir a instância que criamos na etapa do codelab chamada "Setup a Cloud Spanner Instance".
O que vimos:
- Instâncias, bancos de dados e esquema de tabelas do Google Cloud Spanner para um placar
- Como criar um aplicativo do console Java
- Como criar um banco de dados do Spanner e tabelas usando a biblioteca de cliente do Java
- Como carregar dados em um banco de dados do Spanner usando a biblioteca de cliente Java
- Como consultar os "Top 10" dos seus dados usando carimbos de data/hora de confirmação do Spanner e a biblioteca de cliente Java
Próximas etapas:
- Leia o whitepaper sobre CAPs do Spanner
- Saiba mais sobre o Design de esquema e as práticas recomendadas de consulta.
- Saiba mais sobre os carimbos de data/hora de confirmação do Cloud Spanner
Envie um feedback
- Reserve um momento para completar nossa pesquisa curta