۱. مرور کلی
گوگل کلود اسپنر یک سرویس پایگاه داده رابطهای، توزیعشده در سطح جهانی و مقیاسپذیر افقی است که تراکنشهای ACID و مفاهیم SQL را بدون از دست دادن عملکرد و دسترسیپذیری بالا ارائه میدهد.
در این آزمایش، نحوه راهاندازی یک نمونه Cloud Spanner را خواهید آموخت. مراحل ایجاد یک پایگاه داده و طرحوارهای که میتواند برای جدول امتیازات بازی استفاده شود را طی خواهید کرد. با ایجاد یک جدول Players برای ذخیره اطلاعات بازیکنان و یک جدول Scores برای ذخیره امتیازات بازیکنان شروع خواهید کرد.
در مرحله بعد، جداول را با دادههای نمونه پر خواهید کرد. سپس با اجرای چند نمونه کوئری از ده نمونه برتر، این آزمایش را به پایان خواهید رساند و در نهایت نمونه را برای آزادسازی منابع حذف خواهید کرد.
آنچه یاد خواهید گرفت
- نحوه راهاندازی یک نمونه Cloud Spanner.
- نحوه ایجاد پایگاه داده و جداول.
- نحوه استفاده از ستون مهر زمانی کامیت.
- نحوه بارگذاری دادهها در جدول پایگاه داده Cloud Spanner خود با مهرهای زمانی.
- چگونه از پایگاه داده Cloud Spanner خود پرس و جو کنید.
- چگونه نمونه Cloud Spanner خود را حذف کنید.
آنچه نیاز دارید
چگونه از این آموزش استفاده خواهید کرد؟
تجربه خود را با پلتفرم ابری گوگل چگونه ارزیابی میکنید؟
۲. تنظیمات و الزامات
تنظیم محیط خودتنظیم
اگر از قبل حساب گوگل (جیمیل یا برنامههای گوگل) ندارید، باید یکی ایجاد کنید . وارد کنسول پلتفرم ابری گوگل ( console.cloud.google.com ) شوید و یک پروژه جدید ایجاد کنید.
اگر از قبل پروژهای دارید، روی منوی کشویی انتخاب پروژه در سمت چپ بالای کنسول کلیک کنید:

و در پنجرهی باز شده روی دکمهی «پروژهی جدید» کلیک کنید تا یک پروژهی جدید ایجاد شود:

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

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

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

اجرای این آزمایشگاه کد نباید بیش از چند دلار برای شما هزینه داشته باشد، اما اگر تصمیم به استفاده از منابع بیشتر بگیرید یا اگر آنها را در حال اجرا رها کنید (به بخش «پاکسازی» در انتهای این سند مراجعه کنید)، میتواند بیشتر هم بشود. قیمت Google Cloud Spanner در اینجا مستند شده است.
کاربران جدید پلتفرم ابری گوگل واجد شرایط دریافت یک دوره آزمایشی رایگان ۳۰۰ دلاری هستند که این کدلب را کاملاً رایگان میکند.
راهاندازی پوسته ابری گوگل
اگرچه میتوان از راه دور و از طریق لپتاپ، گوگل کلود و اسپنر را کنترل کرد، اما در این آزمایشگاه کد، از گوگل کلود شل ، یک محیط خط فرمان که در فضای ابری اجرا میشود، استفاده خواهیم کرد.
این ماشین مجازی مبتنی بر دبیان، تمام ابزارهای توسعه مورد نیاز شما را در خود جای داده است. این ماشین مجازی یک دایرکتوری خانگی ۵ گیگابایتی دائمی ارائه میدهد و در فضای ابری گوگل اجرا میشود که عملکرد شبکه و احراز هویت را تا حد زیادی بهبود میبخشد. این بدان معناست که تنها چیزی که برای این آزمایشگاه کد نیاز دارید یک مرورگر است (بله، روی کرومبوک هم کار میکند).
- برای فعال کردن Cloud Shell از کنسول Cloud، کافیست روی Activate Cloud Shell کلیک کنید.
(فقط چند لحظه طول میکشد تا آماده شود و به محیط متصل شود).
پس از اتصال به 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 جستجو کنید:
Cloud Shell همچنین برخی از متغیرهای محیطی را به طور پیشفرض تنظیم میکند که ممکن است هنگام اجرای دستورات بعدی مفید باشند.
echo $GOOGLE_CLOUD_PROJECT
خروجی دستور
<PROJECT_ID>
- در نهایت، منطقه پیشفرض و پیکربندی پروژه را تنظیم کنید.
gcloud config set compute/zone us-central1-f
شما میتوانید مناطق مختلفی را انتخاب کنید. برای اطلاعات بیشتر، به بخش مناطق و نواحی مراجعه کنید.
خلاصه
در این مرحله، محیط خود را راهاندازی میکنید.
بعدی
در مرحله بعد، یک نمونه Cloud Spanner راهاندازی خواهید کرد.
۳. راهاندازی یک نمونهی Cloud Spanner
در این مرحله، نمونه Cloud Spanner خود را برای این codelab راهاندازی میکنیم. ورودی Spanner را جستجو کنید.
در منوی همبرگری بالا سمت چپ
یا با فشردن کلید "/" و تایپ "Spanner" عبارت Spanner را جستجو کنید.

در مرحله بعد، روی کلیک کنید
و فرم را با وارد کردن نام نمونه cloudspanner-leaderboard برای نمونه خود پر کنید، یک پیکربندی انتخاب کنید (یک نمونه منطقهای را انتخاب کنید) و تعداد گرهها را تنظیم کنید، برای این codelab ما فقط به ۱ گره نیاز داریم. برای نمونههای تولیدی و برای واجد شرایط بودن برای SLA Cloud Spanner، باید ۳ یا بیشتر گره را در نمونه Cloud Spanner خود اجرا کنید.
در آخر، اما نه کماهمیتتر، روی «ایجاد» کلیک کنید و ظرف چند ثانیه یک نمونه Cloud Spanner در اختیار شما قرار میگیرد.

در مرحله بعدی، ما قصد داریم از کتابخانه کلاینت جاوا برای ایجاد یک پایگاه داده و طرحواره در نمونه جدید خود استفاده کنیم.
۴. ایجاد پایگاه داده و طرحواره
در این مرحله، ما قصد داریم پایگاه داده و طرحواره نمونه خود را ایجاد کنیم.
بیایید از کتابخانه کلاینت جاوا برای ایجاد دو جدول استفاده کنیم؛ یک جدول Players برای اطلاعات بازیکنان و یک جدول Scores برای ذخیره امتیازات بازیکنان. برای انجام این کار، مراحل ایجاد یک برنامه کنسول جاوا در Cloud Shell را بررسی خواهیم کرد.
ابتدا با تایپ دستور زیر در Cloud Shell، کد نمونه این codelab را از Github کپی کنید:
git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
سپس دایرکتوری را به دایرکتوری "applications" تغییر دهید، جایی که برنامه خود را ایجاد خواهید کرد.
cd java-docs-samples/spanner/leaderboard
تمام کدهای مورد نیاز برای این آزمایشگاه کد، در دایرکتوری موجود java-docs-samples/spanner/leaderboard/complete به عنوان یک برنامه C# قابل اجرا با نام Leaderboard قرار دارد تا در حین پیشرفت در آزمایشگاه کد، به عنوان مرجع در اختیار شما قرار گیرد. ما یک دایرکتوری جدید ایجاد خواهیم کرد و یک کپی از برنامه 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 و فایل برنامه جاوا App.java .
سپس، دایرکتوری را به دایرکتوری leaderboard که تازه ایجاد شده تغییر دهید و محتویات آن را فهرست کنید:
cd leaderboard && ls
شما باید فایل pom.xml و دایرکتوری src را در لیست ببینید:
pom.xml src
حالا بیایید این برنامه کنسول را با ویرایش App.java بهروزرسانی کنیم تا از کتابخانه کلاینت Java Spanner برای ایجاد یک جدول امتیازات شامل دو جدول بازیکنان و امتیازات استفاده کنیم. میتوانید این کار را مستقیماً در ویرایشگر Cloud Shell انجام دهید:
با کلیک روی آیکون مشخص شده در زیر، ویرایشگر Cloud Shell را باز کنید:

فایل pom.xml را در پوشه leaderboard باز کنید. فایل pom.xml که در پوشه java-docs-samples\ spanner\leaderboard\codelab\leaderboard قرار دارد را باز کنید. این فایل سیستم ساخت maven را طوری پیکربندی میکند که برنامه ما را در یک jar، شامل تمام وابستگیهای ما، بسازد.
بخش مدیریت وابستگی جدید زیر را درست زیر عنصر </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>
همچنین یک وابستگی جدید در بخش <dependencies> موجود اضافه کنید که کتابخانه کلاینت جاوای Cloud Spanner را به برنامه اضافه میکند.
<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 واقع در پوشه src/main/java/com/google/codelabs/ باز کنید. کد موجود در فایل را با کد مورد نیاز برای ایجاد پایگاه داده جدول leaderboard و جداول Players و Scores با قرار دادن کد جاوا زیر در فایل 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");
}
}
با انتخاب گزینه «ذخیره» در منوی «فایل» ویرایشگر Cloud Shell، تغییراتی را که در فایل App.java ایجاد کردهاید، ذخیره کنید.
میتوانید از فایل App.java در دایرکتوری java-docs-samples/spanner/leaderboard/step4/src استفاده کنید تا نمونهای از نحوهی عملکرد فایل App.java پس از افزودن کد فعالسازی دستور create را مشاهده کنید.
برای ساخت برنامه خود، بسته mvn را از دایرکتوری که pom.xml شما در آن قرار دارد، اجرا کنید:
mvn package
پس از اینکه فایل جاوا جار شما با موفقیت ساخته شد، برنامهی حاصل را با وارد کردن دستور زیر در پوستهی ابری اجرا کنید:
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، باید پایگاه داده و جداول جدید خود را که در منوی سمت چپ ظاهر میشوند، ببینید.

در مرحله بعدی، برنامه خود را بهروزرسانی خواهیم کرد تا برخی دادهها را در پایگاه داده جدید شما بارگذاری کند.
۵. بارگذاری دادهها
اکنون یک پایگاه داده به نام leaderboard داریم که شامل دو جدول است: Players و Scores . حال بیایید از کتابخانه کلاینت جاوا برای پر کردن جدول Players با بازیکنان و جدول Scores با نمرات تصادفی برای هر بازیکن استفاده کنیم.
اگر هنوز باز نشده است، ویرایشگر Cloud Shell را با کلیک روی آیکون مشخص شده در زیر باز کنید:

در مرحله بعد، فایل App.java را در ویرایشگر Cloud Shell ویرایش کنید تا یک دستور insert اضافه شود که میتواند برای درج ۱۰۰ بازیکن در جدول Players استفاده شود یا میتواند برای درج ۴ امتیاز تصادفی در جدول 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");
با انتخاب گزینه «ذخیره» در منوی «فایل» ویرایشگر Cloud Shell، تغییراتی را که در فایل App.java ایجاد کردهاید، ذخیره کنید.
شما میتوانید از فایل 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 از طریق دستور SQL زیر که هنگام اجرای دستور create اجرا شد، به عنوان یک ستون "commit timestamp" تعریف شد:
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 در یک ردیف جدول مشخص را فراهم میکند.
همچنین میتوانید مقادیر timestamp خودتان را در ستون "commit timestamp" وارد کنید، به شرطی که یک timestamp با مقداری که در گذشته است وارد کنید، که ما برای هدف این codelab همین کار را انجام خواهیم داد.
حالا بیایید دستور 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 جدول، داده دارید.

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

آفرین! بیایید برنامهمان را بهروزرسانی کنیم تا چند کوئری اجرا کنیم که میتوانیم از آنها برای ایجاد جدول امتیازات بازی استفاده کنیم.
۶. جستجوهای جدول امتیازات را اجرا کنید
حالا که پایگاه داده خود را راهاندازی و اطلاعات را در جداول بارگذاری کردهایم، بیایید با استفاده از این دادهها یک جدول امتیازات ایجاد کنیم. برای انجام این کار باید به چهار سوال زیر پاسخ دهیم:
- کدام بازیکنان جزو "ده بازیکن برتر" تمام دوران هستند؟
- کدام بازیکنان جزو «ده بازیکن برتر» سال هستند؟
- کدام بازیکنان جزو «ده بازیکن برتر» ماه هستند؟
- کدام بازیکنان جزو «ده بازیکن برتر» هفته هستند؟
بیایید برنامه خود را بهروزرسانی کنیم تا کوئریهای SQL را که به این سؤالات پاسخ میدهند، اجرا کند.
ما یک دستور query اضافه خواهیم کرد که روشی برای اجرای پرسوجوها برای پاسخ به سؤالاتی که اطلاعات مورد نیاز برای جدول امتیازات ما را تولید میکنند، فراهم میکند.
فایل App.java را در ویرایشگر Cloud Shell ویرایش کنید تا برنامه را برای افزودن یک دستور 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");
با انتخاب گزینه «ذخیره» در منوی «فایل» ویرایشگر Cloud Shell، تغییراتی را که در فایل App.java ایجاد کردهاید، ذخیره کنید.
شما میتوانید از فایل 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 استفاده کنیم. از آنجایی که آرگومان timespan اختیاری است، به این معنی است که اگر آن آرگومان timespan گنجانده نشود، هیچ رکوردی بر اساس timestamp فیلتر نخواهد شد. بنابراین میتوانیم از دستور 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 را با آرگومانهای لازم اجرا کنیم تا با مشخص کردن یک "timespan" برابر با تعداد ساعات در یک سال که ۸۷۶۰ است، "ده بازیکن برتر" سال را جستجو کنیم.
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 را اجرا کنیم تا با مشخص کردن یک "timespan" برابر با تعداد ساعات در یک ماه که 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 را اجرا کنیم تا با مشخص کردن یک "timespan" برابر با تعداد ساعات یک هفته که ۱۶۸ است، "ده بازیکن برتر" هفته را جستجو کنیم.
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
کار عالی!
حالا که رکورد اضافه میکنید، کلود اسپنر پایگاه داده شما را تا هر اندازهای که نیاز دارید، بزرگ میکند. مهم نیست پایگاه داده شما چقدر بزرگ شود، جدول امتیازات بازی شما میتواند با کلود اسپنر و فناوری Truetime آن، با دقت بیشتری بزرگ شود.
۷. پاکسازی
بعد از کلی بازی سرگرمکننده با Spanner، باید زمین بازیمان را تمیز کنیم و در منابع و پول ارزشمندمان صرفهجویی کنیم. خوشبختانه این مرحله آسانی است، فقط کافی است به بخش Cloud Spanner در Cloud Console بروید و نمونهای را که در مرحله codelab با عنوان «راهاندازی یک نمونه Cloud Spanner» ایجاد کردیم، حذف کنید.
۸. تبریک میگویم!
آنچه ما پوشش دادهایم:
- نمونههای گوگل کلود اسپنر، پایگاههای داده و طرح جدول برای کسب رتبه برتر
- نحوه ایجاد برنامه کنسول جاوا
- نحوه ایجاد پایگاه داده و جداول Spanner با استفاده از کتابخانه کلاینت جاوا
- نحوه بارگذاری دادهها در پایگاه داده Spanner با استفاده از کتابخانه کلاینت جاوا
- چگونه با استفاده از مهرهای زمانی کامیت Spanner و کتابخانه کلاینت جاوا، نتایج «ده مورد برتر» را از دادههای خود پرسوجو کنیم؟
مراحل بعدی:
- گزارش رسمی Spanner CAP را بخوانید.
- آشنایی با طراحی Schema و بهترین شیوههای پرسوجو
- درباره مهرهای زمانی کامیت Cloud Spanner بیشتر بدانید
نظرات خود را با ما در میان بگذارید
- لطفا کمی وقت بگذارید و نظرسنجی بسیار کوتاه ما را تکمیل کنید.