Cloud Spanner: Java ile oyunlar için skor tablosu oluşturma

1. Genel Bakış

Google Cloud Spanner, performanstan ve yüksek kullanılabilirlikten ödün vermeden ACID işlemleri ve SQL semantiği sağlayan, tümüyle yönetilen, yatay olarak ölçeklenebilir, küresel olarak dağıtılmış bir ilişkisel veritabanı hizmetidir.

Bu laboratuvarda, Cloud Spanner örneği oluşturmayı öğreneceksiniz. Oyun skor tablosu için kullanılabilecek bir veritabanı ve şema oluşturma adımlarını inceleyeceksiniz. Oyuncu bilgilerini saklamak için bir Oyuncular tablosu, oyuncu skorlarını saklamak için bir Skor tablosu oluşturarak başlayın.

Ardından tabloları örnek verilerle dolduracaksınız. Ardından, kaynakları boşa çıkarmak için en iyi on örnek sorguyu çalıştırarak ve son olarak da örneği silerek laboratuvarı tamamlayacaksınız.

Neler öğreneceksiniz?

  • Cloud Spanner örneği oluşturma.
  • Veritabanı ve tablo oluşturma
  • Kaydetme zaman damgası sütunu nasıl kullanılır?
  • Cloud Spanner veritabanı tablonuza zaman damgalarıyla veri yükleme.
  • Cloud Spanner veritabanınızı sorgulama.
  • Cloud Spanner örneğinizi silme.

Gerekenler

Bu eğiticiden nasıl yararlanacaksınız?

Yalnızca okuma Okuyun ve alıştırmaları tamamlayın

Google Cloud Platform deneyiminizi nasıl değerlendirirsiniz?

Acemi Orta Yeterli

2. Kurulum ve Gereksinimler

Kendi hızınızda ortam kurulumu

Google Hesabınız (Gmail veya Google Apps) yoksa bir hesap oluşturmanız gerekir. Google Cloud Platform konsolunda ( console.cloud.google.com) oturum açın ve yeni bir proje oluşturun.

Zaten bir projeniz varsa konsolun sol üst köşesindeki proje seçimi açılan menüsünü tıklayın:

6c9406d9b014760.png

Sonra ‘YENİ PROJE’yi tıklayın. düğmesini tıklayın:

f708315ae07353d0.png

Henüz projeniz yoksa ilk projenizi oluşturmak için şuna benzer bir iletişim kutusu görmeniz gerekir:

870a3cbd6541ee86.png

Sonraki proje oluşturma iletişim kutusu yeni projenizin ayrıntılarını girmenize olanak tanır:

6a92c57d3250a4b3.png

Tüm Google Cloud projeleri için benzersiz bir ad olan proje kimliğini unutmayın (yukarıdaki ad daha önce alınmış ve size uygun olmayacaktır!). Bu kod laboratuvarın ilerleyen bölümlerinde PROJECT_ID olarak adlandırılacaktır.

Ardından, henüz yapmadıysanız Developers Console'da faturalandırmayı etkinleştirmeniz ve Google Cloud kaynaklarını kullanmanız ve Cloud Spanner API'yi etkinleştirmeniz gerekir.

15d0ef27a8fbab27.png

Bu codelab'i çalıştırmanın maliyeti birkaç dolardan fazla değildir. Ancak daha fazla kaynak kullanmaya karar verirseniz veya bu kaynakları çalışır durumda bırakırsanız daha yüksek ücret ödemeniz gerekebilir (bu belgenin sonundaki "temizlik" bölümüne bakın). Google Cloud Spanner fiyatlandırması burada açıklanmıştır.

Yeni Google Cloud Platform kullanıcıları, bu codelab'i tamamen ücretsiz hale getirecek 300 ABD doları değerindeki ücretsiz denemeden yararlanabilir.

Google Cloud Shell Kurulumu

Google Cloud ve Spanner, dizüstü bilgisayarınızdan uzaktan çalıştırılabilse de bu codelab'de, Cloud'da çalışan bir komut satırı ortamı olan Google Cloud Shell'i kullanacağız.

Bu Debian tabanlı sanal makine, ihtiyacınız olan tüm geliştirme araçlarıyla yüklüdür. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud'da çalışarak ağ performansını ve kimlik doğrulamasını büyük ölçüde iyileştirir. Yani bu codelab'de ihtiyacınız olan tek şey bir tarayıcıdır (Evet, Chromebook'ta çalışır).

  1. Cloud Shell'i Cloud Console'dan etkinleştirmek için Cloud Shell'i etkinleştir gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A simgesini tıklamanız yeterlidir (sağlanması ve ortama bağlanması yalnızca birkaç dakika sürer).

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

Cloud Shell'e bağlandıktan sonra kimliğinizin doğrulandığını ve projenin PROJECT_ID olarak ayarlanmış olduğunu göreceksiniz.

gcloud auth list

Komut çıkışı

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Komut çıkışı

[core]
project = <PROJECT_ID>

Herhangi bir nedenle proje ayarlanmamışsa şu komutu vermeniz yeterlidir:

gcloud config set project <PROJECT_ID>

PROJECT_ID cihazınızı mı arıyorsunuz? Kurulum adımlarında kullandığınız kimliği kontrol edin veya Cloud Console kontrol panelinden arayın:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell bazı ortam değişkenlerini de varsayılan olarak ayarlar. Bu değişkenler, gelecekte komut çalıştırdığınızda işinize yarayabilir.

echo $GOOGLE_CLOUD_PROJECT

Komut çıkışı

<PROJECT_ID>
  1. Son olarak, varsayılan alt bölgeyi ve proje yapılandırmasını ayarlayın.
gcloud config set compute/zone us-central1-f

Çeşitli farklı alt bölgeler seçebilirsiniz. Daha fazla bilgi için Bölgeler ve Bölgeler.

Özet

Bu adımda ortamınızı ayarlarsınız.

Sonraki bölüm

Şimdi bir Cloud Spanner örneği kuracaksınız.

3. Cloud Spanner Örneği Oluşturma

Bu adımda, bu codelab için Cloud Spanner örneğimizi oluşturduk. Soldaki Hamburger Menüsünde 1a6580bd3d3e6783.png Spanner girişini 3129589f7bc9e5ce.png veya "/" tuşuna basarak Spanner girişini arayın ve "Spanner" yazın.

36e52f8df8e13b99.png

Ardından 95269e75bc8c3e4d.png düğmesini tıklayın ve örneğiniz için cloudspanner-leaderboard örnek adını girip bir yapılandırma seçip (bölgesel bir örnek seçin) ve düğüm sayısını ayarlayın. Bu codelab için yalnızca 1 düğüme ihtiyacımız olacaktır. Üretim örnekleri için ve Cloud Spanner HDS'si (Hizmet Düzeyi Sözleşmesi) şartlarına uygun olmak üzere, Cloud Spanner örneğinizde en az 3 düğüm çalıştırmanız gerekir.

Son olarak, "Oluştur"u tıklayın. ve birkaç saniye içinde Cloud Spanner örneğiniz olur.

dceb68e9ed3801e8.png

Sonraki adımda yeni örneğimizde veritabanı ve şema oluşturmak için Java istemci kitaplığını kullanacağız.

4. Veritabanı ve şema oluşturma

Bu adımda örnek veritabanımızı ve şemamızı oluşturacağız.

Şimdi, Java istemci kitaplığını kullanarak iki tablo oluşturalım: Oyuncu bilgileri için Oyuncular tablosu, oyuncu skorlarını kaydetmek için de Skorlar tablosu. Bunun için Cloud Shell'de Java konsolu uygulaması oluşturma adımlarını inceleyeceğiz.

Öncelikle Cloud Shell'de aşağıdaki komutu yazarak GitHub'dan bu codelab için örnek kodu klonlayın:

git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

Ardından dizini "applications" (uygulamalar) olarak değiştirin uygulamanızı oluşturacağınız dizindir.

cd java-docs-samples/spanner/leaderboard

Bu codelab'de gerekli olan tüm kodlar, codelab'de ilerlerken referans olarak kullanılmak üzere Leaderboard adlı çalıştırılabilir bir C# uygulaması olarak mevcut java-docs-samples/spanner/leaderboard/complete dizininde bulunmaktadır. Yeni bir dizin oluşturup Skor Tablosu uygulamasının bir kopyasını aşamalı olarak hazırlayacağız.

"codelab" adlı yeni bir dizin oluşturun dokunun ve aşağıdaki komutla dizini değiştirin:

mkdir codelab && cd $_

"Leaderboard" adlı yeni bir temel Java uygulaması oluşturun kullanarak aşağıdaki Maven (mvn) komutunu kullanabilirsiniz:

mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.google.codelabs -DartifactId=leaderboard -DarchetypeVersion=1.4

Bu komut, iki birincil dosya (Maven uygulamasının yapılandırma dosyası pom.xml ve Java uygulaması App.java) dosyadan oluşan basit bir konsol uygulaması oluşturur.

Daha sonra, dizini yeni oluşturulan leaderboard diziniyle değiştirin ve içeriğini listeleyin:

cd leaderboard && ls

Listede pom.xml dosyası ve src dizini gösterilir:

pom.xml  src

Şimdi App.java öğesini düzenleyerek iki tablodan oluşan bir leaderboard oluşturmak için Java Spanner istemci kitaplığını kullanarak bu konsol uygulamasını güncelleyelim; Oyuncular ve Skorlar. Bu işlemi doğrudan Cloud Shell Düzenleyici'de yapabilirsiniz:

Aşağıda vurgulanan simgeyi tıklayarak Cloud Shell Düzenleyici'yi açın:

73cf70e05f653ca.png

Leaderboard klasörünün altındaki pom.xml dosyasını açın. java-docs-samples\ spanner\leaderboard\codelab\leaderboard klasöründeki pom.xml dosyasını açın.Bu dosya, maven derleme sistemini, uygulamamızı tüm bağımlılıklarımızı içerecek şekilde bir jar içinde derleyecek şekilde yapılandırır.

Aşağıdaki 1 yeni bağımlılık yönetimi bölümünü mevcut </properties> bölümünün hemen altına ekleyin öğe:

<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>

Ayrıca mevcut <dependencies> içine 1 yeni bağımlılık ekleyin. bölümüne göz atacağız. Bu bölüm, Cloud Spanner Java istemci kitaplığını uygulamaya ekleyecek.

    <dependency>
      <!-- Version auto-managed by BOM -->
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-spanner</artifactId>
    </dependency>  

Sonra pom.xml dosyasının mevcut <build> öğesini değiştirin bölümünü bölüm:

 <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>

pom.xml dosyasında yaptığınız değişiklikleri "Kaydet"i seçerek kaydedin. Cloud Shell Düzenleyici'nin "Dosya" bölümünde tuşuna basarak veya "Ctrl" tuşuna basarak ve "S" klavye tuşlarını basılı tutun.

Ardından, src/main/java/com/google/codelabs/ klasöründe bulunan Cloud Shell Düzenleyici'de App.java dosyasını açın. Aşağıdaki Java kodunu App.java dosyasına yapıştırarak leaderboard veritabanını ve Players ile Scores tablolarını oluşturmak için gerekli olan kodla dosyanın mevcut kodunu değiştirin:

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");
  }
}

App.java dosyasında yaptığınız değişiklikleri "Kaydet"i seçerek kaydedin. Cloud Shell Düzenleyici'nin "Dosya" bölümünde tıklayın.

create komutunu etkinleştirme kodu eklendikten sonra App.java dosyanızın nasıl görünmesi gerektiğine dair bir örnek görmek için java-docs-samples/spanner/leaderboard/step4/src dizinindeki App.java dosyasını kullanabilirsiniz.

Uygulamanızı derlemek için pom.xml uygulamanızın bulunduğu dizinden mvn paketini çalıştırın:

mvn package

Java jar dosyanız başarıyla oluşturulduktan sonra, aşağıdaki komutu girerek gösterilen uygulamayı Cloud Shell'de çalıştırın:

java -jar target/leaderboard.jar

Şuna benzer bir çıkış alırsınız:

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.

Bu yanıttan, şu anda olası bir komuta sahip olan Leaderboard uygulamasının bu olduğunu görüyoruz: create. create komutunun beklenen bağımsız değişkenlerinin Örnek Kimliği ve Veritabanı Kimliği olduğunu görebiliyoruz.

Şimdi aşağıdaki komutu çalıştırın.

java -jar target/leaderboard.jar create cloudspanner-leaderboard leaderboard

Birkaç saniye sonra, aşağıdaki gibi bir yanıt görürsünüz:

Created database [projects/your-project/instances/cloudspanner-leaderboard/databases/leaderboard] 

Cloud Console'un Cloud Spanner bölümünde, soldaki menüde yeni veritabanınızı ve tablolarınızı göreceksiniz.

ba9008bb84cb90b0.png

Sonraki adımda, yeni veritabanınıza bazı veriler yüklemek için uygulamamızı güncelleyeceğiz.

5. Verileri Yükle

Artık leaderboard adında iki tablo içeren bir veritabanımız var; Players ve Scores. Şimdi Players tablomuzu oyuncularla, Scores tablomuzu da her oyuncu için rastgele skorlarla doldurmak için Java istemci kitaplığını kullanalım.

Açık değilse Cloud Shell Düzenleyici'yi aşağıda vurgulanan simgeyi tıklayarak açın:

ef49fcbaaed19024.png

Ardından, Players tablosuna 100 oyuncu eklemek üzere veya Players tablosundaki her oyuncu için Scores tablosuna 4 rastgele puan eklemek üzere kullanılabilecek bir insert komutu eklemek için Cloud Shell Düzenleyici'de App.java dosyasını düzenleyin.

Öncelikle uygulama dosyasının üst kısmındaki imports bölümünü güncelleyin. Mevcut bölümü, işiniz bittiğinde aşağıdaki gibi görünecek şekilde değiştirin:

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;

Daha sonra, mevcut create() yönteminin altına ve mevcut printUsageAndExit() yönteminin üzerine aşağıdaki Insert, insertPlayers ve insertScores yöntemlerini ekleyin:

  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...");
    }
  }

Ardından, insert komutunun çalışması için aşağıdaki kodu uygulamanızın "ana" koduna ekleyin yöntemini switch (command) ifadesinde bulabilirsiniz :

        case "insert":
          String insertType;
          try {
            insertType = args[3];
          } catch (ArrayIndexOutOfBoundsException exception) {
            insertType = "";
          }
          insert(dbClient, insertType);
          break;

İşiniz bittiğinde switch (command) ifadesi aşağıdaki gibi görünmelidir:

      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();
      }

"Ekle" eklemeyi tamamlamak için son adım işlevi, "insert" için yardım metni eklemektir. komutunu printUsageAndExit()yöntemine ekleyin. Insert komutuna ilişkin yardım metnini dahil etmek için printUsageAndExit() yöntemine aşağıdaki kod satırlarını ekleyin:

    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");

App.java dosyasında yaptığınız değişiklikleri "Kaydet"i seçerek kaydedin. Cloud Shell Düzenleyici'nin "Dosya" bölümünde tıklayın.

insert komutunu etkinleştirme kodu eklendikten sonra App.java dosyanızın nasıl görünmesi gerektiğine dair bir örnek görmek için java-docs-samples/spanner/leaderboard/step5/src dizinindeki App.java dosyasını kullanabilirsiniz.

Şimdi yeni insert komutunun, uygulamanın olası komutlar listesine eklendiğini onaylamak için uygulamayı yeniden oluşturup çalıştıralım.

Uygulamanızı derlemek için pom.xml dosyanızın bulunduğu dizinden mvn package komutunu çalıştırın:

mvn package

Java jar dosyanız başarıyla oluşturulduktan sonra, aşağıdaki komutu çalıştırın:

java -jar target/leaderboard.jar

insert komutunun artık uygulamanın varsayılan çıkışına dahil olduğunu göreceksiniz:

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.

Yanıttan, Örnek Kimliği ve Veritabanı Kimliği'ne ek olarak, "oynatıcılar" değerine sahip olabilecek başka bir bağımsız değişkenin daha olduğunu görebilirsiniz. kullanabilirsiniz.

Şimdi insert komutunu, create komutunu çağırdığımızda kullandığımız bağımsız değişken değerleriyle çalıştırıp "oynatıcılar" ifadesini ekleyerek çalıştıralım ek "ek türü" olarak bağımsız değişkeninin önüne geçer.

java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard players

Birkaç saniye sonra, aşağıdaki gibi bir yanıt görürsünüz:

Done inserting player records...

Şimdi Scores tablomuzu Players tablodaki her oynatıcıya ait zaman damgalarıyla birlikte dört rastgele puanla doldurmak için Java istemci kitaplığını kullanalım.

Scores tablosunun Timestamp sütunu, "taahhüt zaman damgası" olarak tanımlandı sütununu kontrol edin: daha önce create komutunu çalıştırdığımızda yürütülen aşağıdaki SQL deyimi:

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

OPTIONS(allow_commit_timestamp=true) özelliğine dikkat edin. Bu durumda Timestamp, "taahhüt zaman damgası" haline gelir. sütununu görüntüler ve belirli bir tablo satırındaki INSERT ve UPDATE işlemleri için tam işlem zaman damgasıyla otomatik olarak doldurulmasını sağlar.

Ayrıca, bir "kaydetme zaman damgası"na kendi zaman damgası değerlerinizi de ekleyebilirsiniz. geçmiş bir değere sahip bir zaman damgası eklemeniz gerekir. Bu codelab'in amacı budur.

Şimdi insert komutunu, "scores" ekleyerek create komutunu çağırdığımızda kullandığımız bağımsız değişken değerleriyle çalıştıralım. ek "ek türü" olarak bağımsız değişkeninin önüne geçer.

java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard scores

Birkaç saniye sonra, aşağıdaki gibi bir yanıt görürsünüz:

Done inserting score records...

insert, "type of insert" (ekleme türü) ile çalıştırılıyor belirtilen scores yöntemi, rastgele oluşturulmuş bir zaman damgası (tarih/saat geçmişte olmak üzere) eklemek için aşağıdaki kod snippet'lerini kullanan insertScores yöntemini çağırır:

          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())

Timestamp sütununun tam olarak "Insert" öğesinin zaman damgasıyla otomatik olarak doldurulması için işlemi gerçekleşirse bunun yerine Java sabitini Value.COMMIT_TIMESTAMP ekleyebilirsiniz. Örneğin, aşağıdaki kod snippet'ini kullanabilirsiniz:

               .bind("Timestamp")
               .to(Value.COMMIT_TIMESTAMP)

Veri yüklemeyi tamamladığımıza göre artık yeni tablolarımıza yazdığımız değerleri doğrulayalım. Öncelikle leaderboard veritabanını ve ardından Players tablosunu seçin. Data sekmesini tıklayın. Tablonun PlayerId ve PlayerName sütunlarında veri bulunduğunu göreceksiniz.

7bc2c96293c31c49.png

Şimdi de Scores tablosunu tıklayıp Data sekmesini seçerek Puanlar tablosunun veri içerdiğini doğrulayalım. Tablonun PlayerId, Timestamp ve Score sütunlarında verileriniz olduğunu göreceksiniz.

d8a4ee4f13244c19.png

Tebrikler! Oyun skor tablosu oluşturmak için kullanabileceğimiz bazı sorgular çalıştırmak için uygulamamızı güncelleyelim.

6. Skor tablosu sorgularını çalıştırma

Veritabanımızı kurup bilgileri tablolarımıza yüklediğimize göre şimdi bu verileri kullanarak bir skor tablosu oluşturalım. Bunu yapmak için aşağıdaki dört soruyu cevaplamamız gerekiyor:

  1. "En İyi On" Oyuncular var mı?
  2. "En İyi On" Oyuncular ne durumda?
  3. "En İyi On" Oyuncular hakkında bilgi edindiniz?
  4. "En İyi On" Oyuncular merak ediyor musunuz?

Şimdi uygulamamızı, bu soruları cevaplayacak SQL sorgularını çalıştıracak şekilde güncelleyelim.

Skor tablosu için gereken bilgileri üretecek soruları yanıtlamak üzere, sorguların çalıştırılmasını sağlayacak bir query komutu ekleyeceğiz.

Uygulamayı query komutu ekleyerek güncellemek için Cloud Shell Düzenleyici'de App.java dosyasını düzenleyin. query komutu, iki query yönteminden oluşur. Biri yalnızca bir DatabaseClient bağımsız değişkeni alır, diğeri ise sonuçların saat cinsinden belirtilen bir zaman aralığına göre filtrelenmesini kolaylaştırmak için ek bir timespan bağımsız değişkeni alır.

Mevcut insertScores() yönteminin altında ve mevcut printUsageAndExit() yönteminin üzerinde aşağıdaki iki query yöntemini ekleyin:

  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));
    }
  }

Ardından, query komutunun çalışması için aşağıdaki kodu uygulamanızın "ana" komutundaki switch(command) ifadesine ekleyin yöntem:

        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;

"Sorgu" eklemeyi tamamlamak için son adım işlevi, "sorgu" sorgusu için yardım metni eklemektir. komutunu printUsageAndExit()yöntemine ekleyin. "Sorgu" için yardım metni dahil etmek üzere printUsageAndExit() yöntemine aşağıdaki kod satırlarını ekleyin komut:

    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");

App.java dosyasında yaptığınız değişiklikleri "Kaydet"i seçerek kaydedin. Cloud Shell Düzenleyici'nin "Dosya" bölümünde tıklayın.

query komutunu etkinleştirme kodu eklendikten sonra App.java dosyanızın nasıl görünmesi gerektiğine dair bir örnek görmek için dotnet-docs-samples/applications/leaderboard/step6/src dizinindeki App.java dosyasını kullanabilirsiniz.

Uygulamanızı derlemek için pom.xml dosyanızın bulunduğu dizinden mvn package komutunu çalıştırın:

mvn package

Şimdi yeni query komutunun, uygulamanın olası komutlar listesine eklendiğini onaylamak için uygulamayı çalıştıralım. Aşağıdaki komutu çalıştırın:

java -jar target/leaderboard.jar

Artık query komutunun, uygulamanın varsayılan çıkışına yeni bir komut seçeneği olarak dahil edildiğini göreceksiniz:

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.

Yanıttan, Örnek Kimliği ve Veritabanı Kimliği bağımsız değişkenlerine ek olarak, query komutunun, Scores tablosunun Timestamp sütunundaki değerlerine göre kayıtları filtrelemek için kullanılacak, saat cinsinden isteğe bağlı bir zaman aralığı belirtmemize olanak tanıdığını görebilirsiniz. İsteğe bağlı zaman aralığı bağımsız değişkeni, bir zaman aralığı bağımsız değişkeni dahil edilmediğinde hiçbir kaydın zaman damgalarına göre filtrelenmeyeceği anlamına gelir. Böylece query komutunu "zaman aralığı" olmadan kullanabiliriz. "En İyi On" listemizi almak için "En İyi On" değerini tüm zamanların en iyi oyuncuları.

query komutunu, create komutunu çalıştırdığımızda kullandığımız bağımsız değişken değerlerini kullanarak, bir "zaman aralığı" belirtmeden çalıştıralım.

java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard

"İlk On" bilgisini içeren bir yanıt göreceksiniz oyuncuları beğenmişsinizdir:

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

Şimdi "İlk On"u sorgulamak için gerekli bağımsız değişkenlerle birlikte query komutunu çalıştıralım. yılın oyuncuları için bir "zaman aralığı" 8.760 olan bir yıldaki saat sayısına eşittir.

java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 8760

"İlk On" bilgisini içeren bir yanıt göreceksiniz en iyi performans gösteren oyuncuları şunlar:

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

Şimdi "İlk On"u sorgulamak için query komutunu çalıştıralım bir "zaman aralığı" belirleyerek ayın oyuncuları bir aydaki saat sayısına (730) eşittir.

java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 730

"İlk On" bilgisini içeren bir yanıt göreceksiniz şunlar gibi ayın oyuncuları:

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

Şimdi "İlk On"u sorgulamak için query komutunu çalıştıralım "zaman aralığı" belirleyerek haftanın oyuncuları bir haftadaki 168 saat olan saat sayısına eşittir.

java -jar target/leaderboard.jar query cloudspanner-leaderboard leaderboard 168

"İlk On" bilgisini içeren bir yanıt göreceksiniz şunlar gibi haftanın oyuncuları:

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

Harika!

Artık kayıt ekledikçe Cloud Spanner, veritabanınızı ihtiyacınız ne kadar büyük olacak şekilde ölçeklendirir. Veritabanınız ne kadar büyürse yükselsin, oyununuzun skor tablosu, Cloud Spanner ve Truetime teknolojisiyle doğrulukla ölçeklendirilmeye devam edebilir.

7. Temizleme

Spanner ile oynadığımız eğlencenin ardından, oyun alanını temizleyerek değerli kaynaklardan ve paradan tasarruf etmemiz gerekiyor. Neyse ki bu, kolay bir adımdır. Cloud Console'un Cloud Spanner bölümüne gidip "Cloud Spanner Örneği Oluşturma" adlı codelab adımında oluşturduğumuz örneği silmeniz yeterlidir.

8. Tebrikler!

İşlediğimiz konular:

  • Leaderboard için Google Cloud Spanner Örnekleri, Veritabanları ve Tablo Şeması
  • Java konsolu uygulaması oluşturma
  • Java istemci kitaplığını kullanarak Spanner Veritabanı ve Tabloları oluşturma
  • Java istemci kitaplığını kullanarak bir Spanner Veritabanına veri yükleme
  • "İlk On"u sorgulama Spanner kaydetme zaman damgalarını ve Java istemci kitaplığını kullanarak verilerinizden sonuçlar

Sonraki Adımlar:

Geri bildiriminizi paylaşın

  • Lütfen çok kısa bir süre ayırarak anketimizi doldurun.