Cloud Spanner: یک تابلوی امتیازات بازی با جاوا ایجاد کنید

1. بررسی اجمالی

Google Cloud Spanner یک سرویس پایگاه داده رابطه‌ای با مقیاس‌پذیر افقی، توزیع‌شده در سطح جهانی و کاملاً مدیریت شده است که تراکنش‌های ACID و معنایی SQL را بدون کاهش عملکرد و در دسترس بودن بالا ارائه می‌کند.

در این آزمایشگاه، نحوه راه اندازی یک نمونه Cloud Spanner را خواهید آموخت. شما مراحل ایجاد یک پایگاه داده و طرحواره ای را طی خواهید کرد که می تواند برای امتیازات بازی استفاده شود. شما با ایجاد یک جدول بازیکنان برای ذخیره اطلاعات بازیکن و یک جدول امتیازات برای ذخیره امتیازات بازیکنان شروع خواهید کرد.

سپس جداول را با داده های نمونه پر می کنید. سپس با اجرای چند پرس و جوی نمونه برتر و در نهایت حذف نمونه برای آزاد کردن منابع، آزمایشگاه را به پایان می رسانید.

چیزی که یاد خواهید گرفت

  • نحوه راه اندازی یک نمونه Cloud Spanner.
  • نحوه ایجاد پایگاه داده و جداول
  • نحوه استفاده از ستون مهر زمان commit
  • نحوه بارگیری داده ها در جدول پایگاه داده Cloud Spanner با مُهر زمانی.
  • چگونه پایگاه داده Cloud Spanner خود را پرس و جو کنیم.
  • چگونه نمونه Cloud Spanner خود را حذف کنیم.

آنچه شما نیاز دارید

چگونه از این آموزش استفاده خواهید کرد؟

فقط از طریق آن را بخوانید آن را بخوانید و تمرینات را کامل کنید

تجربه خود را با Google Cloud Platform چگونه ارزیابی می کنید؟

تازه کار متوسط مسلط

2. راه اندازی و الزامات

تنظیم محیط خود به خود

اگر قبلاً یک حساب Google (Gmail یا Google Apps) ندارید، باید یک حساب ایجاد کنید . به کنسول Google Cloud Platform ( consol.cloud.google.com ) وارد شوید و یک پروژه جدید ایجاد کنید.

اگر قبلاً پروژه ای دارید، روی منوی کشویی انتخاب پروژه در سمت چپ بالای کنسول کلیک کنید:

6c9406d9b014760.png

و روی دکمه "پروژه جدید" در گفتگوی حاصل کلیک کنید تا یک پروژه جدید ایجاد کنید:

f708315ae07353d0.png

اگر قبلاً پروژه ای ندارید، باید یک دیالوگ مانند این را ببینید تا اولین پروژه خود را ایجاد کنید:

870a3cbd6541ee86.png

گفتگوی بعدی ایجاد پروژه به شما امکان می دهد جزئیات پروژه جدید خود را وارد کنید:

6a92c57d3250a4b3.png

شناسه پروژه را به خاطر بسپارید، که یک نام منحصر به فرد در تمام پروژه های Google Cloud است (نام بالا قبلاً گرفته شده است و برای شما کار نخواهد کرد، متأسفیم!). بعداً در این آزمایشگاه کد به عنوان PROJECT_ID نامیده خواهد شد.

در مرحله بعد، اگر قبلاً این کار را انجام نداده‌اید، برای استفاده از منابع Google Cloud و فعال کردن Cloud Spanner API، باید صورت‌حساب را در Developers Console فعال کنید .

15d0ef27a8fbab27.png

گذراندن این کد نباید بیش از چند دلار هزینه داشته باشد، اما اگر تصمیم به استفاده از منابع بیشتری داشته باشید یا آنها را در حال اجرا رها کنید، ممکن است بیشتر باشد (به بخش "پاکسازی" در انتهای این سند مراجعه کنید). قیمت Google Cloud Spanner در اینجا مستند شده است.

کاربران جدید Google Cloud Platform واجد شرایط استفاده آزمایشی رایگان 300 دلاری هستند که باید این نرم افزار کد را کاملاً رایگان کند.

Google Cloud Shell Setup

در حالی که Google Cloud و Spanner را می‌توان از راه دور از لپ‌تاپ شما کار کرد، در این نرم‌افزار از Google Cloud Shell استفاده می‌کنیم، یک محیط خط فرمان که در Cloud اجرا می‌شود.

این ماشین مجازی مبتنی بر دبیان با تمام ابزارهای توسعه که شما نیاز دارید بارگذاری شده است. این دایرکتوری اصلی 5 گیگابایتی دائمی را ارائه می دهد و در Google Cloud اجرا می شود و عملکرد شبکه و احراز هویت را بسیار افزایش می دهد. این بدان معنی است که تمام چیزی که برای این کد لبه نیاز دارید یک مرورگر است (بله، روی کروم بوک کار می کند).

  1. برای فعال کردن Cloud Shell از Cloud Console، کافی است روی Activate Cloud Shell کلیک کنید. gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK_OgjogdOgdOg3ZHK39gdOg 2A (تهیه و اتصال به محیط فقط چند لحظه طول می کشد).

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjkWkWE2C w

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

پس از اتصال به Cloud Shell، باید ببینید که قبلاً احراز هویت شده اید و پروژه قبلاً روی PROJECT_ID شما تنظیم شده است.

gcloud auth list

خروجی فرمان

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

خروجی فرمان

[core]
project = <PROJECT_ID>

اگر به دلایلی پروژه تنظیم نشد، به سادگی دستور زیر را صادر کنید:

gcloud config set project <PROJECT_ID>

به دنبال PROJECT_ID خود هستید؟ بررسی کنید از چه شناسه ای در مراحل راه اندازی استفاده کرده اید یا آن را در داشبورد Cloud Console جستجو کنید:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvOZwZw51

Cloud Shell همچنین برخی از متغیرهای محیطی را به صورت پیش‌فرض تنظیم می‌کند که ممکن است هنگام اجرای دستورات آینده مفید باشند.

echo $GOOGLE_CLOUD_PROJECT

خروجی فرمان

<PROJECT_ID>
  1. در نهایت، منطقه پیش فرض و پیکربندی پروژه را تنظیم کنید.
gcloud config set compute/zone us-central1-f

شما می توانید مناطق مختلفی را انتخاب کنید. برای اطلاعات بیشتر، به مناطق و مناطق مراجعه کنید.

خلاصه

در این مرحله محیط خود را راه اندازی می کنید.

بعدی

در مرحله بعد، یک نمونه Cloud Spanner را تنظیم خواهید کرد.

3. یک نمونه Cloud Spanner راه اندازی کنید

در این مرحله ما نمونه Cloud Spanner خود را برای این Codelab راه اندازی می کنیم. ورودی آچار را جستجو کنید 1a6580bd3d3e6783.png در سمت چپ منوی همبرگر بالا 3129589f7bc9e5ce.png یا با فشار دادن "/" عبارت "Spanner" را جستجو کنید و "Spanner" را تایپ کنید.

36e52f8df8e13b99.png

بعد، بر روی کلیک کنید 95269e75bc8c3e4d.png و با وارد کردن نام نمونه cloudspanner-leaderboard برای نمونه خود، انتخاب یک پیکربندی (انتخاب یک نمونه منطقه ای) و تنظیم تعداد گره ها، فرم را پر کنید، برای این کد لبه تنها به 1 گره نیاز داریم. برای نمونه های تولید و برای واجد شرایط بودن برای Cloud Spanner SLA، باید 3 یا بیشتر گره را در نمونه Cloud Spanner خود اجرا کنید.

آخرین، اما نه کم اهمیت، روی "ایجاد" کلیک کنید و در عرض چند ثانیه یک نمونه Cloud Spanner در اختیار دارید.

dceb68e9ed3801e8.png

در مرحله بعدی ما از کتابخانه کلاینت جاوا برای ایجاد یک پایگاه داده و طرحواره در نمونه جدید خود استفاده می کنیم.

4. یک پایگاه داده و طرحواره ایجاد کنید

در این مرحله می‌خواهیم پایگاه داده و طرحواره خود را بسازیم.

بیایید از کتابخانه مشتری جاوا برای ایجاد دو جدول استفاده کنیم. یک جدول بازیکنان برای اطلاعات بازیکن و یک جدول امتیازات برای ذخیره امتیازات بازیکنان. برای انجام این کار، مراحل ایجاد یک برنامه کنسول جاوا در Cloud Shell را طی می کنیم.

ابتدا با تایپ دستور زیر در Cloud Shell، کد نمونه این کد لبه را از Github کلون کنید:

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

سپس دایرکتوری را به دایرکتوری "applications" تغییر دهید که در آن برنامه خود را ایجاد خواهید کرد.

cd java-docs-samples/spanner/leaderboard

تمام کدهای مورد نیاز برای این Codelab در دایرکتوری java-docs-samples/spanner/leaderboard/complete موجود به عنوان یک برنامه C# قابل اجرا با نام Leaderboard قرار دارد تا در حین پیشروی از طریق Codelab به عنوان مرجع عمل کند. ما یک دایرکتوری جدید ایجاد می کنیم و یک کپی از برنامه Leaderboard در مراحل ایجاد می کنیم.

یک دایرکتوری جدید با نام "codelab" برای برنامه ایجاد کنید و دایرکتوری را با دستور زیر به آن تغییر دهید:

mkdir codelab && cd $_

با استفاده از دستور Maven (mvn) زیر یک برنامه جاوا پایه جدید با نام "Leaderboard" ایجاد کنید:

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

این دستور یک برنامه کنسول ساده متشکل از دو فایل اصلی فایل، فایل پیکربندی برنامه Maven pom.xml و فایل برنامه Java App.java ایجاد می کند.

سپس، دایرکتوری را به فهرست راهنمای رهبران که به تازگی ایجاد شده است تغییر دهید و محتوای آن را فهرست کنید:

cd leaderboard && ls

باید فایل pom.xml و فهرست src را مشاهده کنید:

pom.xml  src

اکنون اجازه دهید این برنامه کنسول را با ویرایش App.java به روز کنیم تا از کتابخانه سرویس گیرنده Java Spanner برای ایجاد یک تابلوی امتیاز متشکل از دو جدول استفاده کنیم. بازیکنان و امتیازات می توانید این کار را درست در ویرایشگر پوسته ابری انجام دهید:

ویرایشگر پوسته ابری را با کلیک بر روی نماد مشخص شده در زیر باز کنید:

73cf70e05f653ca.png

pom.xml را در زیر پوشه تابلوی امتیازات باز کنید. فایل pom.xml را که در پوشه java-docs-samples\ spanner\leaderboard\codelab\leaderboard قرار دارد را باز کنید. این فایل سیستم ساخت maven را برای ساخت برنامه ما در یک jar پیکربندی می‌کند که شامل همه وابستگی‌های ما می‌شود.

1 بخش مدیریت وابستگی جدید زیر را درست زیر عنصر </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>

همچنین 1 وابستگی جدید در بخش <وابستگی> موجود اضافه کنید که کتابخانه کلاینت Cloud Spanner Java را به برنامه اضافه می کند.

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

سپس بخش <build> موجود فایل pom.xml را با بخش <build> زیر جایگزین کنید:

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

با انتخاب «ذخیره» در منوی «فایل» ویرایشگر Cloud Shell یا با فشار دادن کلیدهای صفحه کلید «Ctrl» و «S» با هم، تغییراتی را که در فایل pom.xml ایجاد کرده‌اید، ذخیره کنید.

سپس فایل App.java را در Cloud Shell Editor واقع در پوشه src/main/java/com/google/codelabs/ باز کنید. با قرار دادن کد جاوا زیر در فایل App.java ، کد موجود فایل را با کد مورد نیاز برای ایجاد پایگاه داده leaderboard و جداول Players و Scores جایگزین کنید:

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 ایجاد کرده‌اید، با انتخاب «ذخیره» در منوی «فایل» Cloud Shell Editor ذخیره کنید.

می‌توانید از فایل App.java در فهرست راهنمای java-docs-samples/spanner/leaderboard/step4/src استفاده کنید تا نمونه‌ای از نحوه نگاه کردن فایل App.java خود را پس از افزودن کد برای فعال کردن فرمان create ببینید.

برای ساختن برنامه خود بسته mvn را از دایرکتوری که pom.xml شما در آن قرار دارد اجرا کنید:

mvn package

هنگامی که فایل jar جاوا شما با موفقیت ساخته شد، با وارد کردن دستور زیر برنامه به دست آمده را در پوسته ابری اجرا کنید:

java -jar target/leaderboard.jar

شما باید خروجی را مانند زیر ببینید:

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.

از این پاسخ می بینیم که این برنامه Leaderboard است که در حال حاضر یک دستور ممکن دارد: create . می‌توانیم ببینیم که آرگومان‌های مورد انتظار دستور create Instance ID و Database ID هستند.

حالا دستور زیر را اجرا کنید.

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

پس از چند ثانیه باید پاسخی مانند زیر را مشاهده کنید:

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

در بخش Cloud Spanner در Cloud Console، باید پایگاه داده و جداول جدید خود را در منوی سمت چپ مشاهده کنید.

ba9008bb84cb90b0.png

در مرحله بعدی برنامه خود را برای بارگیری برخی از داده ها در پایگاه داده جدید شما به روز می کنیم.

5. بارگذاری داده ها

ما اکنون یک پایگاه داده به نام leaderboard داریم که شامل دو جدول است. Players و Scores اکنون بیایید از کتابخانه مشتری جاوا برای پر کردن جدول Players با بازیکنان و جدول Scores خود با امتیازهای تصادفی برای هر بازیکن استفاده کنیم.

اگر قبلاً باز نشده است، ویرایشگر پوسته ابری را با کلیک بر روی نماد برجسته شده در زیر باز کنید:

ef49fcbaaed19024.png

در مرحله بعد، فایل App.java را در ویرایشگر پوسته ابری ویرایش کنید تا یک دستور insert اضافه کنید که می تواند برای وارد کردن 100 بازیکن در جدول Players استفاده شود یا می توان از آن برای درج 4 امتیاز تصادفی در جدول Scores برای هر بازیکن در Players استفاده کرد. جدول

ابتدا بخش imports را در بالای فایل برنامه به‌روزرسانی کنید و آنچه را که در حال حاضر وجود دارد جایگزین کنید تا پس از اتمام کار به شکل زیر باشد:

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;

سپس متدهای insert، insertPlayers و insertScores زیر را در زیر متد create() موجود و بالای متد printUsageAndExit() موجود اضافه کنید:

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

سپس، برای کاربردی کردن دستور insert ، کد زیر را به متد "main" برنامه خود در switch (command) اضافه کنید:

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

هنگامی که کارتان تمام شد، دستور switch (command) باید به شکل زیر باشد:

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

آخرین مرحله برای تکمیل افزودن قابلیت "insert" به برنامه شما، افزودن متن راهنما برای دستور "insert" به متد printUsageAndExit() است. خطوط کد زیر را به متد printUsageAndExit() اضافه کنید تا متن راهنما برای دستور 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");

تغییراتی را که در فایل App.java ایجاد کرده‌اید، با انتخاب «ذخیره» در منوی «فایل» Cloud Shell Editor ذخیره کنید.

می‌توانید از فایل App.java در فهرست راهنمای java-docs-samples/spanner/leaderboard/step5/src استفاده کنید تا نمونه‌ای از نحوه نگاه فایل App.java خود را پس از افزودن کد برای فعال کردن دستور insert ببینید.

حالا بیایید برنامه را بازسازی و اجرا کنیم تا تأیید کنیم که دستور insert جدید در لیست دستورات ممکن برنامه گنجانده شده است.

برای ساختن برنامه خود mvn package از دایرکتوری که pom.xml شما در آن قرار دارد اجرا کنید:

mvn package

هنگامی که فایل jar جاوا شما با موفقیت ساخته شد، دستور زیر را اجرا کنید:

java -jar target/leaderboard.jar

اکنون باید دستور insert را در خروجی پیش فرض برنامه مشاهده کنید:

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.

می‌توانید از پاسخ ببینید که علاوه بر شناسه نمونه و شناسه پایگاه داده، آرگومان دیگری نیز وجود دارد که می‌تواند مقدار «بازیکنان» یا «امتیازها» را داشته باشد.

حالا بیایید دستور insert را با همان مقادیر آرگومان اجرا کنیم که هنگام فراخوانی دستور create استفاده کردیم و "players" را به عنوان آرگومان اضافی "نوع درج" اضافه کنیم.

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

پس از چند ثانیه باید پاسخی مانند زیر را مشاهده کنید:

Done inserting player records...

اکنون بیایید از کتابخانه مشتری جاوا استفاده کنیم تا جدول Scores خود را با چهار امتیاز تصادفی همراه با مهرهای زمانی برای هر بازیکن در جدول Players پر کنیم.

ستون Timestamp جدول Scores به عنوان یک ستون "commit timestamp" از طریق عبارت SQL زیر تعریف شد که زمانی که قبلاً دستور 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

به ویژگی OPTIONS(allow_commit_timestamp=true) توجه کنید. این موضوع Timestamp به یک ستون "Commit timestamp" تبدیل می‌کند و آن را قادر می‌سازد تا به طور خودکار با مهر زمانی دقیق تراکنش برای عملیات INSERT و UPDATE در یک ردیف جدول مشخص شود.

همچنین می‌توانید مقادیر مُهر زمانی خود را در ستون «مهر زمانی تعهد» درج کنید تا زمانی که یک مهر زمانی با مقداری که در گذشته است وارد کنید، این همان کاری است که ما برای هدف این آزمایشگاه انجام خواهیم داد.

حالا بیایید دستور insert را با همان مقادیر آرگومان اجرا کنیم که هنگام فراخوانی دستور create و اضافه کردن "scores" به عنوان آرگومان اضافی "نوع درج" استفاده کردیم.

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

پس از چند ثانیه باید پاسخی مانند زیر را مشاهده کنید:

Done inserting score records...

insert در حال اجرا با "نوع درج" مشخص شده به عنوان scores ، روش insertScores را فراخوانی می کند که از تکه های کد زیر برای درج مهر زمانی تولید شده به طور تصادفی با تاریخ-زمان در گذشته استفاده می کند:

          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 با مهر زمانی دقیقاً زمانی که تراکنش «Insert» انجام می‌شود، می‌توانید در عوض مقدار ثابت جاوا Value.COMMIT_TIMESTAMP را مانند قطعه کد زیر وارد کنید:

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

اکنون که بارگیری داده ها را کامل کردیم، بیایید مقادیری را که به تازگی در جداول جدید خود نوشته ایم بررسی کنیم. ابتدا پایگاه داده leaderboard را انتخاب کنید و سپس جدول Players را انتخاب کنید. روی تب Data کلیک کنید. باید ببینید که داده‌هایی در ستون‌های PlayerId و PlayerName جدول دارید.

7bc2c96293c31c49.png

سپس اجازه دهید با کلیک بر روی جدول Scores و انتخاب زبانه Data ، بررسی کنیم که جدول امتیازات نیز دارای داده است. باید ببینید که داده‌هایی در ستون‌های PlayerId ، Timestamp و Score جدول دارید.

d8a4ee4f13244c19.png

آفرین! بیایید برنامه خود را به روز کنیم تا برخی از پرس و جوها را اجرا کنیم تا بتوانیم از آنها برای ایجاد تابلوی امتیازات بازی استفاده کنیم.

6. کوئری های تابلوی امتیازات را اجرا کنید

اکنون که پایگاه داده خود را راه اندازی کرده ایم و اطلاعات را در جداول خود بارگذاری کرده ایم، بیایید با استفاده از این داده ها یک تابلوی امتیازات ایجاد کنیم. برای این کار باید به چهار سوال زیر پاسخ دهیم:

  1. کدام بازیکنان "ده" برتر تمام دوران هستند؟
  2. کدام بازیکنان "ده" برتر سال هستند؟
  3. کدام بازیکنان "ده" برتر ماه هستند؟
  4. کدام بازیکنان "ده" برتر هفته هستند؟

بیایید برنامه خود را به‌روزرسانی کنیم تا سؤالات SQL را اجرا کنیم که به این سؤالات پاسخ می‌دهد.

ما یک دستور query اضافه می کنیم که راهی برای اجرای پرس و جوها برای پاسخ به سوالاتی که اطلاعات مورد نیاز برای تابلوی امتیازات ما را تولید می کند، ارائه می دهد.

فایل App.java را در Cloud Shell Editor ویرایش کنید تا برنامه به‌روزرسانی شود تا دستور query اضافه شود. دستور query از دو روش query تشکیل شده است، یکی که فقط آرگومان DatabaseClient را می گیرد و دیگری که یک آرگومان timespan اضافی برای تسهیل فیلتر کردن نتایج با بازه زمانی مشخص شده در ساعت می گیرد.

دو روش query زیر را در زیر متد insertScores() موجود و بالای متد printUsageAndExit() موجود اضافه کنید:

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

سپس، برای اینکه دستور query کاربردی شود، کد زیر را به switch(command) در متد "main" برنامه خود اضافه کنید:

        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;

آخرین مرحله برای تکمیل افزودن قابلیت "query" به برنامه شما، افزودن متن راهنما برای دستور "query" به متد printUsageAndExit() است. خطوط کد زیر را به متد printUsageAndExit() اضافه کنید تا متن راهنما برای دستور 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");

تغییراتی را که در فایل App.java ایجاد کرده‌اید، با انتخاب «ذخیره» در منوی «فایل» Cloud Shell Editor ذخیره کنید.

می توانید از فایل App.java در دایرکتوری dotnet-docs-samples/applications/leaderboard/step6/src استفاده کنید تا نمونه ای از نحوه نگاه کردن فایل App.java خود را پس از افزودن کد برای فعال کردن دستور query ببینید.

برای ساختن برنامه خود mvn package از دایرکتوری که pom.xml شما در آن قرار دارد اجرا کنید:

mvn package

حالا اجازه دهید برنامه را اجرا کنیم تا تأیید کنیم که دستور query جدید در لیست دستورات ممکن برنامه گنجانده شده است. دستور زیر را اجرا کنید:

java -jar target/leaderboard.jar

اکنون باید دستور query را به عنوان یک گزینه دستور جدید در خروجی پیش‌فرض برنامه مشاهده کنید:

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.

از پاسخ می‌بینید که علاوه بر آرگومان‌های Instance ID و Database ID، دستور query به ما اجازه می‌دهد تا یک بازه زمانی اختیاری را بر حسب ساعت تعیین کنیم تا از آن برای فیلتر کردن رکوردها بر اساس مقدار آنها در ستون Timestamp جدول Scores استفاده کنیم. از آنجایی که آرگومان Timesp در اختیاری است به این معنی است که اگر آرگومان بازه زمانی در آن گنجانده نشود، هیچ رکوردی توسط مهر زمانی فیلتر نخواهد شد. بنابراین می‌توانیم از دستور query بدون مقدار «timespan» برای دریافت لیستی از «ده بازیکن برتر» خود در تمام دوران استفاده کنیم.

بیایید دستور query را بدون تعیین "timespan" اجرا کنیم، با استفاده از همان مقادیر آرگومان که هنگام اجرای دستور create استفاده کردیم.

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

شما باید پاسخی را ببینید که شامل "ده بازیکن برتر" تمام دوران است مانند موارد زیر:

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

حالا بیایید دستور query را با آرگومان های لازم برای پرس و جو از "ده بازیکن برتر سال" با تعیین "زمان زمانی" برابر تعداد ساعت های یک سال که 8760 ساعت است، اجرا کنیم.

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

شما باید پاسخی را ببینید که شامل «ده بازیکن برتر» سال مانند زیر است:

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

حالا بیایید دستور query را برای پرس و جو از "ده بازیکن برتر" ماه با تعیین یک "زمان زمانی" برابر با تعداد ساعت های یک ماه که 730 ساعت است، اجرا کنیم.

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

شما باید پاسخی را ببینید که شامل "ده بازیکن برتر" ماه است مانند زیر:

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

حالا بیایید دستور query را برای پرس و جو از "ده بازیکن برتر" هفته با تعیین یک "زمان زمانی" برابر تعداد ساعت در هفته که 168 ساعت است، اجرا کنیم.

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

شما باید پاسخی را ببینید که شامل "ده بازیکن برتر" هفته مانند زیر است:

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

کار عالی!

اکنون همانطور که رکوردها را اضافه می کنید، Cloud Spanner پایگاه داده شما را به هر اندازه که نیاز دارید مقیاس می دهد. مهم نیست که پایگاه داده شما چقدر رشد می کند، تابلوی امتیازات بازی شما می تواند با استفاده از Cloud Spanner و فناوری Truetime آن با دقت به مقیاس خود ادامه دهد.

7. پاکسازی

پس از همه سرگرمی‌های بازی با Spanner، باید زمین بازی خود را تمیز کنیم و منابع و پول گرانبها را ذخیره کنیم. خوشبختانه این یک مرحله آسان است، کافی است به بخش Cloud Spanner در Cloud Console بروید و نمونه‌ای را که در مرحله Codelab ایجاد کردیم به نام "Setup a Cloud Spanner Instance" را حذف کنید.

8. تبریک می گویم!

آنچه ما پوشش داده ایم:

  • نمونه‌های Google Cloud Spanner، پایگاه‌های داده و جدول جدول برای تابلوی امتیازات
  • نحوه ایجاد اپلیکیشن کنسول جاوا
  • نحوه ایجاد یک پایگاه داده و جداول Spanner با استفاده از کتابخانه مشتری جاوا
  • نحوه بارگذاری داده ها در پایگاه داده Spanner با استفاده از کتابخانه مشتری جاوا
  • نحوه پرس و جو کردن نتایج "Top Ten" از داده های خود با استفاده از مهرهای زمانی commit Spanner و کتابخانه مشتری Java

مراحل بعدی:

نظرات خود را با ما در میان بگذارید

  • لطفا یک لحظه برای تکمیل نظرسنجی بسیار کوتاه ما وقت بگذارید