Cloud Spanner: Membuat papan peringkat game dengan Java

Google Cloud Spanner adalah layanan database relasional skalabel dan terkelola sepenuhnya secara global yang menyediakan transaksi ACID dan semantik SQL tanpa merusak performa dan ketersediaan yang tinggi.

Di lab ini, Anda akan mempelajari cara menyiapkan instance Cloud Spanner. Anda akan menjalani langkah-langkah untuk membuat database dan skema yang dapat digunakan untuk papan peringkat game. Anda akan mulai dengan membuat tabel Pemain untuk menyimpan informasi pemain dan tabel Skor untuk menyimpan skor pemain.

Selanjutnya, Anda akan mengisi tabel dengan data contoh. Kemudian Anda akan mengakhiri lab dengan menjalankan kueri contoh Sepuluh Teratas dan menghapus instance untuk mengosongkan resource.

Yang akan Anda pelajari

  • Cara menyiapkan instance Cloud Spanner.
  • Cara membuat database dan tabel.
  • Cara menggunakan kolom stempel waktu commit.
  • Cara memuat data ke tabel database Cloud Spanner dengan stempel waktu.
  • Cara membuat kueri database Cloud Spanner.
  • Cara menghapus instance Cloud Spanner.

Yang akan Anda butuhkan

Bagaimana Anda akan menggunakan tutorial ini?

Hanya membacanya Membacanya dan menyelesaikan latihan

Bagaimana penilaian Anda terhadap pengalaman dengan Google Cloud Platform?

Pemula Menengah Mahir

Penyiapan lingkungan mandiri

Jika belum memiliki Akun Google (Gmail atau Google Apps), Anda harus membuatnya. Login ke Google Cloud Platform console (console.cloud.google.com) dan buat project baru.

Jika Anda sudah memiliki project, klik menu pull-down pilihan project di kiri atas konsol:

6c9406d9b014760.png

dan klik tombol 'PROJECT BARU' dalam dialog yang dihasilkan untuk membuat project baru:

f708315ae07353d0.png

Jika belum memiliki project, Anda akan melihat dialog seperti ini untuk membuat project pertama:

870a3cbd6541ee86.png

Dialog pembuatan project berikutnya memungkinkan Anda memasukkan detail project baru:

6a92c57d3250a4b3.png

Ingat project ID yang merupakan nama unik di semua project Google Cloud (maaf, nama di atas telah digunakan dan tidak akan berfungsi untuk Anda!) Project ID tersebut selanjutnya akan dirujuk di codelab ini sebagai PROJECT_ID.

Selanjutnya, jika Anda belum melakukannya, Anda harus mengaktifkan penagihan di Developers Console untuk menggunakan resource Google Cloud dan mengaktifkan Cloud Spanner API.

15d0ef27a8fbab27.png

Menjalankan melalui codelab ini tidak akan menghabiskan biaya lebih dari beberapa dolar, tetapi bisa lebih jika Anda memutuskan untuk menggunakan lebih banyak resource atau jika Anda membiarkannya berjalan (lihat bagian "pembersihan" di akhir dokumen ini). Harga Google Cloud Spanner didokumentasikan di sini.

Pengguna baru Google Cloud Platform memenuhi syarat untuk mendapatkan uji coba gratis senilai $300, yang menjadikan codelab ini sepenuhnya gratis.

Penyiapan Google Cloud Shell

Meskipun Google Cloud dan Spanner dapat dioperasikan dari jarak jauh menggunakan laptop Anda, dalam codelab ini, kita akan menggunakan Google Cloud Shell, lingkungan command line yang berjalan di Cloud.

Mesin virtual berbasis Debian ini memuat semua alat pengembangan yang akan Anda perlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Ini berarti bahwa semua yang Anda perlukan untuk codelab ini adalah browser (ya, ini berfungsi di Chromebook).

  1. Untuk mengaktifkan Cloud Shell dari Cloud Console, cukup klik Aktifkan Cloud ShellgcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A (hanya perlu beberapa saat untuk melakukan penyediaan dan terhubung ke lingkungan).

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

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

Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda sudah diautentikasi dan project sudah ditetapkan ke PROJECT_ID.

gcloud auth list

Output perintah

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

Output perintah

[core]
project = <PROJECT_ID>

Jika, untuk beberapa alasan, project belum disetel, cukup jalankan perintah berikut:

gcloud config set project <PROJECT_ID>

Mencari PROJECT_ID Anda? Periksa ID yang Anda gunakan di langkah-langkah penyiapan atau cari di dasbor Cloud Console:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell juga menetapkan beberapa variabel lingkungan secara default, yang mungkin berguna saat Anda menjalankan perintah di masa mendatang.

echo $GOOGLE_CLOUD_PROJECT

Output perintah

<PROJECT_ID>
  1. Terakhir, tetapkan zona dan konfigurasi project default.
gcloud config set compute/zone us-central1-f

Anda dapat memilih berbagai zona yang berbeda. Untuk informasi selengkapnya, lihat Region & Zona.

Ringkasan

Pada langkah ini, Anda menyiapkan lingkungan.

Berikutnya

Selanjutnya, Anda akan menyiapkan Instance Cloud Spanner.

Pada langkah ini kita menyiapkan Instance Cloud Spanner untuk codelab ini. Telusuri entri Spanner 1a6580bd3d3e6783.png di kiri atas Menu Tiga Garis 3129589f7bc9e5ce.png atau telusuri Spanner dengan menekan "/" dan ketik "Spanner"

36e52f8df8e13b99.png

Selanjutnya, klik 95269e75bc8c3e4d.png dan isi formulir dengan memasukkan nama instance cloudspanner-leaderboard untuk instance Anda, memilih konfigurasi (pilih instance regional), dan menetapkan jumlah node, untuk codelab ini kita hanya perlu 1 node. Agar instance produksi dan untuk memenuhi syarat untuk SLA Cloud Spanner, Anda harus menjalankan 3 node atau lebih di instance Cloud Spanner.

Terakhir, namun tidak kalah penting, klik "Buat" dan dalam beberapa detik Anda sudah memiliki instance Cloud Spanner.

dceb68e9ed3801e8.png

Pada langkah berikutnya, kita akan menggunakan library klien Java untuk membuat database dan skema dalam instance baru.

Pada langkah ini, kita akan membuat contoh database dan skema.

Mari menggunakan library klien Java untuk membuat dua tabel; tabel Pemain untuk info pemain dan tabel Skor untuk menyimpan skor pemain. Untuk melakukannya, kita akan memandu langkah-langkah pembuatan aplikasi konsol Java di Cloud Shell.

Pertama-tama, clone kode sampel untuk codelab ini dari GitHub dengan mengetik perintah berikut di Cloud Shell:

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

Kemudian, ubah direktori ke direktori "aplikasi" tempat Anda akan membuat aplikasi.

cd java-docs-samples/spanner/leaderboard

Semua kode yang diperlukan untuk codelab ini terletak di direktori java-docs-samples/spanner/leaderboard/complete yang ada sebagai aplikasi Go yaC# dapat dijalankan dengan nama Leaderboard untuk berfungsi sebagai referensi saat Anda melanjutkan codelab. Kami akan membuat direktori baru dan mem-build salinan aplikasi Papan Peringkat secara bertahap.

Buat direktori baru bernama "codelab" untuk aplikasi dan ubah direktori menjadi papan peringkat dengan perintah berikut:

mkdir codelab && cd $_

Buat aplikasi Java dasar baru bernama "Leaderboard" menggunakan perintah Maven (mvn) berikut:

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

Perintah ini membuat aplikasi konsol sederhana yang terdiri dari dua file utama, yakni file konfigurasi aplikasi Maven pom.xml dan file aplikasi Java App.java.

Selanjutnya, ubah direktori menjadi direktori papan peringkat yang baru saja dibuat dan masukkan daftar kontennya:

cd leaderboard && ls

Anda akan melihat file pom.xml dan direktori src tercantum:

pom.xml  src

Sekarang mari kita update aplikasi konsol ini dengan mengedit App.java untuk menggunakan library klien Java Spanner untuk membuat papan peringkat yang terdiri dari dua tabel, yakni tabel Pemain dan tabel Skor. Anda dapat melakukannya langsung di Cloud Shell Editor:

Buka Cloud Shell Editor, dengan mengklik ikon yang disorot di bawah:

73cf70e05f653ca.png

Buka pom.xml di bawah folder papan peringkat. Buka file pom.xml yang ada di folder java-docs-samples\ spanner\leaderboard\codelab\leaderboard. File ini mengonfigurasi sistem build maven untuk membuild aplikasi ke dalam satu jar, termasuk semua dependensi.

Tambahkan 1 bagian pengelolaan dependensi baru tepat di bawah elemen </properties> yang ada:

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

Tambahkan juga 1 dependensi baru di bagian <dependencies> yang ada, yang akan menambahkan library klien Java Cloud Spanner ke aplikasi.

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

Kemudian ganti bagian <build> yang sudah ada pada file pom.xml dengan bagian <build> berikut:

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

Simpan perubahan yang Anda buat pada file pom.xml dengan memilih "Simpan" di bawah menu "File" pada Cloud Shell Editor atau dengan menekan tombol keyboard "Ctrl" dan "S" secara bersamaan.

Selanjutnya, buka file App.java di Cloud Shell Editor yang ada di folder src/main/java/com/google/codelabs/. Ganti kode yang ada pada file dengan kode yang diperlukan untuk membuat database leaderboard serta tabel Players dan Scores dengan menempelkan kode Java berikut ke dalam file 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");
  }
}

Simpan perubahan yang Anda buat ke file App.java dengan memilih "Simpan" di bagian menu "File" pada Cloud Shell Editor.

Anda dapat menggunakan file App.java di direktori java-docs-samples/spanner/leaderboard/step4/src untuk melihat contoh tampilan file App.java setelah menambahkan kode untuk mengaktifkan perintah create.

Untuk membuild aplikasi Anda agar menjalankan paket mvn dari direktori tempat pom.xml Anda berada:

mvn package

Setelah file Java jar berhasil dibuat, jalankan aplikasi yang dihasilkan dalam Cloud Shell dengan memasukkan perintah berikut:

java -jar target/leaderboard.jar

Anda akan melihat output seperti berikut:

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.

Dari respons ini, kita dapat melihat bahwa ini adalah aplikasi Leaderboard yang saat ini memiliki satu kemungkinan perintah: create. Kita dapat melihat bahwa argumen yang diharapkan dari perintah create adalah ID Instance dan ID Database.

Sekarang jalankan perintah berikut.

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

Setelah beberapa detik, Anda akan melihat respons seperti berikut:

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

Di bagian Cloud Spanner pada Cloud Console, Anda akan melihat database dan tabel baru muncul di menu sebelah kiri.

ba9008bb84cb90b0.png

Pada langkah berikutnya, kita akan mengupdate aplikasi untuk memuat beberapa data ke database baru Anda.

Sekarang kita memiliki database yang disebut leaderboard yang berisi dua tabel; Players dan Scores Sekarang mari kita gunakan library klien Java untuk mengisi tabel Players dengan pemain dan tabel Scores dengan skor acak untuk setiap pemain.

Jika belum terbuka, buka Cloud Shell Editor dengan mengklik ikon yang ditandai di bawah:

ef49fcbaaed19024.png

Selanjutnya, edit file App.java di Cloud Shell Editor untuk menambahkan perintah insert yang dapat digunakan untuk menyisipkan 100 pemain ke dalam tabel Players atau dapat digunakan untuk menyisipkan 4 skor acak di tabel Scores untuk setiap pemain di tabel Players.

Pertama-tama update bagian imports di bagian atas file aplikasi, menggantikan apa yang saat ini ada, sehingga setelah selesai akan terlihat seperti berikut:

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;

Selanjutnya, tambahkan metode insert, insertPlayers, dan insertScores berikut di bawah metode create() yang ada dan di atas metode printUsageAndExit() yang ada:

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

Kemudian, untuk membuat perintah insert berfungsi, tambahkan kode berikut ke metode "utama" aplikasi Anda dalam pernyataan switch (command) :

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

Setelah Anda selesai, pernyataan switch (command) akan terlihat seperti berikut:

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

Langkah terakhir untuk menyelesaikan penambahan fungsi "insert" ke aplikasi Anda adalah menambahkan teks bantuan untuk perintah "insert" ke metode printUsageAndExit(). Tambahkan baris kode berikut ke metode printUsageAndExit() untuk menyertakan teks bantuan untuk perintah 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");

Simpan perubahan yang Anda buat ke file App.java dengan memilih "Simpan" di bagian menu "File" pada Cloud Shell Editor.

Anda dapat menggunakan file App.java di direktori java-docs-samples/spanner/leaderboard/step5/src untuk melihat contoh tampilan file App.java setelah menambahkan kode untuk mengaktifkan perintah insert.

Sekarang mari kita buat ulang dan jalankan aplikasi untuk mengonfirmasi bahwa perintah insert baru disertakan dalam daftar kemungkinan perintah aplikasi.

Untuk membuat aplikasi Anda, jalankan mvn package dari direktori tempat pom.xml berada:

mvn package

Setelah file Java jar Anda berhasil dibuat, jalankan perintah berikut:

java -jar target/leaderboard.jar

Anda akan melihat perintah insert sekarang disertakan dalam output default aplikasi:

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.

Anda bisa melihat dari respons bahwa selain ID Instance dan ID Database, ada argumen lain yang bisa memiliki nilai 'pemain' atau 'skor'.

Sekarang, mari kita jalankan perintah insert dengan nilai argumen yang sama dengan yang digunakan saat kita memanggil perintah create, menambahkan "pemain" sebagai argumen "type of insert" tambahan.

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

Setelah beberapa detik, Anda akan melihat respons seperti berikut:

Done inserting player records...

Sekarang mari kita gunakan library klien Java untuk mengisi tabel Scores dengan empat skor acak beserta stempel waktu untuk setiap pemain di tabel Players.

Kolom Timestamp pada tabel Scores ditetapkan sebagai kolom "stempel waktu commit" melalui pernyataan SQL berikut yang dijalankan saat sebelumnya menjalankan perintah 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

Perhatikan atribut OPTIONS(allow_commit_timestamp=true). Ini membuat Timestamp menjadi kolom "stempel waktu commit" dan mengaktifkannya untuk diisi secara otomatis dengan stempel waktu transaksi yang tepat untuk operasi INSERT dan UPDATE pada baris tabel tertentu.

Anda juga dapat menyisipkan nilai stempel waktu Anda sendiri ke dalam kolom "stempel waktu commit" selama Anda menyisipkan stempel waktu dengan nilai yang sudah berlalu, yang akan kita lakukan untuk tujuan codelab ini.

Sekarang, mari kita jalankan perintah insert dengan nilai argumen yang sama dengan yang digunakan saat kita memanggil perintah create, menambahkan "skor" sebagai argumen "type of insert" tambahan.

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

Setelah beberapa detik, Anda akan melihat respons seperti berikut:

Done inserting score records...

Menjalankan insert dengan "type of insert" yang ditentukan sebagai scores memanggil metode insertScores yang menggunakan cuplikan kode berikut untuk menyisipkan stempel waktu yang dibuat secara acak dengan tanggal-waktu yang terjadi di waktu lampau:

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

Untuk mengisi kolom Timestamp secara otomatis dengan stempel waktu tepat saat transaksi "Insert" terjadi, Anda dapat menyisipkan konstanta Java Value.COMMIT_TIMESTAMP seperti dalam cuplikan kode berikut:

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

Setelah menyelesaikan pemuatan data, mari kita verifikasi nilai yang baru saja kita tulis ke tabel baru. Pertama-tama, pilih database leaderboard, lalu pilih tabel Players. Klik tab Data. Anda akan melihat bahwa Anda memiliki data di kolom PlayerId dan PlayerName tabel.

7bc2c96293c31c49.png

Selanjutnya, pastikan tabel Skor juga memiliki data dengan mengklik tabel Scores dan memilih tab Data. Anda akan melihat bahwa Anda memiliki data di kolom PlayerId, Timestamp, dan Score tabel.

d8a4ee4f13244c19.png

Bagus! Mari mengupdate aplikasi untuk menjalankan beberapa kueri yang dapat kita gunakan untuk membuat papan peringkat game.

Setelah menyiapkan database dan memuat informasi ke dalam tabel, mari kita buat papan peringkat menggunakan data ini. Untuk melakukannya, kita harus menjawab empat pertanyaan berikut:

  1. Pemain mana yang masuk peringkat "Sepuluh Teratas" sepanjang waktu?
  2. Pemain mana yang masuk peringkat "Sepuluh Teratas" tahun ini?
  3. Pemain mana yang masuk peringkat "Sepuluh Teratas" bulan ini?
  4. Pemain mana yang masuk peringkat "Sepuluh Teratas" minggu ini?

Mari mengupdate aplikasi untuk menjalankan kueri SQL yang akan menjawab pertanyaan ini.

Kita akan menambahkan perintah query yang akan memberikan cara untuk menjalankan kueri guna menjawab pertanyaan yang akan menghasilkan informasi yang diperlukan untuk papan peringkat.

Edit file App.java dalam Cloud Shell Editor untuk mengupdate aplikasi guna menambahkan perintah query. Perintah query terdiri dari dua metode query, salah satunya hanya memerlukan argumen DatabaseClient dan satu lainnya membutuhkan argumen timespan tambahan untuk memfasilitasi pemfilteran hasil berdasarkan rentang waktu yang ditentukan dalam jam.

Tambahkan dua metode query berikut di bawah metode insertScores() yang ada dan di atas metode printUsageAndExit() yang ada:

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

Kemudian, untuk membuat perintah query berfungsi, tambahkan kode berikut ke pernyataan switch(command) dalam metode "utama" aplikasi:

        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;

Langkah terakhir untuk menyelesaikan penambahan fungsi "kueri" ke aplikasi Anda adalah menambahkan teks bantuan untuk perintah "kueri" ke metode printUsageAndExit(). Tambahkan baris kode berikut ke metode printUsageAndExit() untuk menyertakan teks bantuan untuk perintah "kueri":

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

Simpan perubahan yang Anda buat ke file App.java dengan memilih "Simpan" di bagian menu "File" pada Cloud Shell Editor.

Anda dapat menggunakan file App.java di direktori dotnet-docs-samples/applications/leaderboard/step6/src untuk melihat contoh tampilan file App.java setelah menambahkan kode untuk mengaktifkan perintah query.

Untuk membuat aplikasi Anda, jalankan mvn package dari direktori tempat pom.xml berada:

mvn package

Sekarang mari kita jalankan aplikasi untuk mengonfirmasi bahwa perintah query baru disertakan dalam daftar kemungkinan perintah aplikasi. Jalankan perintah berikut:

java -jar target/leaderboard.jar

Anda akan melihat perintah query kini disertakan dalam output default aplikasi sebagai opsi perintah baru:

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.

Anda dapat melihat dari respons bahwa selain argumen ID Instance dan ID Database, perintah query memungkinkan kita menentukan rentang waktu opsional dalam jumlah jam yang akan digunakan untuk memfilter data berdasarkan nilainya di kolom Timestamp pada tabel Scores. Karena argumen rentang waktu bersifat opsional, ini berarti bahwa jika argumen rentang waktu tersebut tidak disertakan, tidak ada data yang akan difilter menurut stempel waktu. Jadi kita bisa menggunakan perintah query tanpa nilai "timespan" untuk mendapatkan daftar pemain "Sepuluh Teratas" sepanjang masa.

Mari kita jalankan perintah query tanpa menetapkan "timespan", menggunakan nilai argumen yang sama dengan yang digunakan saat menjalankan perintah create.

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

Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" sepanjang masa seperti berikut:

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

Sekarang mari kita jalankan perintah query dengan argumen yang diperlukan untuk membuat kueri pemain "Sepuluh Teratas" tahun ini dengan menentukan "timespan" yang sama dengan jumlah jam dalam setahun, yakni 8.760.

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

Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" tahun ini seperti berikut:

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

Sekarang, mari kita jalankan perintah query untuk membuat kueri pemain "Sepuluh Teratas" bulan ini dengan menentukan "timespan" yang sama dengan jumlah jam dalam sebulan, yaitu 730.

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

Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" bulan ini seperti berikut:

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

Sekarang mari kita jalankan perintah query untuk membuat kueri pemain "Sepuluh Teratas" minggu ini dengan menetapkan "timespan" yang sama dengan jumlah jam dalam seminggu, yaitu 168.

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

Anda akan melihat respons yang menyertakan pemain "Sepuluh Teratas" minggu ini seperti berikut:

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

Bagus!

Kini setelah Anda menambahkan data, Cloud Spanner akan mengubah skala database Anda menjadi sebesar apa pun kebutuhan Anda. Berapa pun pertumbuhan database Anda, papan peringkat game dapat terus diskalakan dengan akurat menggunakan Cloud Spanner dan teknologi Truetime-nya.

Setelah asyik bermain dengan Spanner, kita perlu membersihkan tempat bermain kita, menghemat resource dan uang yang berharga. Untungnya ini adalah langkah yang mudah. Cukup buka bagian Cloud Spanner dari Cloud Console dan hapus instance yang kita buat di langkah codelab bernama "Siapkan Instance Cloud Spanner".

Yang telah kita bahas:

  • Instance, Database, dan Skema Tabel Google Cloud Spanner untuk papan peringkat
  • Cara membuat aplikasi konsol Java
  • Cara membuat Database Spanner dan Tabel menggunakan library klien Java
  • Cara memuat data ke dalam Database Spanner menggunakan library klien Java
  • Cara membuat kueri hasil "Sepuluh Teratas" dari data Anda menggunakan stempel waktu commit Spanner dan library klien Java

Langkah Berikutnya:

Kirimkan masukan Anda

  • Luangkan waktu Anda untuk menyelesaikan survei singkat kami