1. نظرة عامة
Google Cloud Spanner هي خدمة قاعدة بيانات ارتباطية وقابلة للتطوير أفقيًا وموزعة عالميًا، وتوفِّر معاملات ACID ودلالات SQL بدون التخلّي عن الأداء والتوفّر العالي.
سوف تتعلم في هذا التمرين المعملي كيفية إعداد مثيل Cloud Spanner. ستتعرف على خطوات إنشاء قاعدة بيانات ومخطط يمكن استخدامهما لقائمة الصدارة في الألعاب. ستبدأ بإنشاء جدول "اللاعبين" لتخزين معلومات اللاعب وجدول النتائج لتخزين نتائج اللاعبين.
بعد ذلك، ستقوم بتعبئة الجداول بنماذج بيانات. ثم ستختتم التمرين المعملي بتشغيل بعض أهم عشرة استعلامات وأخيرًا حذف المثيل لتحرير الموارد.
المعلومات التي ستطّلع عليها
- كيفية إعداد مثيل Cloud Spanner
- كيفية إنشاء قاعدة بيانات وجداول
- كيفية استخدام عمود الطابع الزمني للتنفيذ
- كيفية تحميل البيانات إلى جدول قاعدة بيانات Cloud Spanner مع الطوابع الزمنية
- كيفية طلب بحث في قاعدة بيانات Cloud Spanner
- كيفية حذف مثيل Cloud Spanner
المتطلبات
كيف ستستخدم هذا البرنامج التعليمي؟
ما هو تقييمك لتجربتك مع Google Cloud Platform؟
2. الإعداد والمتطلبات
إعداد بيئة ذاتية
إذا لم يكن لديك حساب Google (Gmail أو Google Apps)، يجب عليك إنشاء حساب. سجِّل الدخول إلى وحدة تحكُّم Google Cloud Platform ( console.cloud.google.com) وأنشئ مشروعًا جديدًا.
إذا كان لديك مشروع بالفعل، فانقر فوق القائمة المنسدلة لاختيار المشروع في أعلى يسار وحدة التحكم:
وانقر على "مشروع جديد" في مربع الحوار الناتج لإنشاء مشروع جديد:
إذا لم يكن لديك مشروع، من المفترض أن يظهر لك مربع حوار مثل هذا لإنشاء مشروعك الأول:
يتيح لك مربع الحوار اللاحق لإنشاء المشروع إدخال تفاصيل مشروعك الجديد:
يُرجى تذكُّر رقم تعريف المشروع، وهو اسم فريد في جميع مشاريع Google Cloud (سبق أن تم استخدام الاسم أعلاه ولن يكون مناسبًا لك). ستتم الإشارة إليها لاحقًا في هذا الدرس التطبيقي حول الترميز باسم PROJECT_ID
.
بعد ذلك، عليك تفعيل الفوترة في Developers Console لاستخدام موارد Google Cloud وتفعيل Cloud Spanner API إذا لم يسبق لك إجراء ذلك.
لن يكلفك تنفيذ هذا الدرس التطبيقي أكثر من بضعة دولارات، ولكن قد تزيد التكاليف إذا قررت استخدام المزيد من الموارد أو إذا تركتها قيد التشغيل (يُرجى الاطّلاع على قسم "التنظيف" في نهاية هذا المستند). يمكن الاطّلاع على أسعار خدمة Google Cloud Spanner هنا.
إنّ مستخدمي Google Cloud Platform الجدد مؤهّلون للاستفادة من فترة تجريبية مجانية بقيمة 300 دولار أمريكي، ما يجعل هذا الدرس التطبيقي حول الترميز بدون أي تكلفة.
إعداد Google Cloud Shell
يمكن إدارة Google Cloud وSpanner عن بُعد من الكمبيوتر المحمول، ولكن في هذا الدرس التطبيقي حول الترميز، سنستخدم Google Cloud Shell، وهي بيئة سطر أوامر يتم تشغيلها في السحابة الإلكترونية.
هذا الجهاز الافتراضي المستند إلى نظام دبيان محمل بكل أدوات التطوير التي ستحتاج إليها. وتوفّر هذه الشبكة دليلاً رئيسيًا دائمًا بسعة 5 غيغابايت ويتم تشغيله في Google Cloud، ما يحسّن بشكل كبير من أداء الشبكة والمصادقة. وهذا يعني أنّ كل ما ستحتاجه في هذا الدرس التطبيقي حول الترميز هو متصفّح (نعم، يعمل على جهاز Chromebook).
- لتفعيل Cloud Shell من Cloud Console، ما عليك سوى النقر على تفعيل 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.
3- إعداد مثيل Cloud Spanner
في هذه الخطوة، نُعِدّ مثيل Cloud Spanner لهذا الدرس التطبيقي حول الترميز. ابحث عن إدخال Spanner في قائمة همبرغر أعلى اليمين أو ابحث عن Spanner عن طريق الضغط على "/" واكتب "Spanner"
بعد ذلك، انقر على واملأ النموذج عن طريق إدخال اسم المثيل cloudspanner-leaderboard للمثيل، واختيار الإعدادات (اختيار مثيل إقليمي)، وضبط عدد العُقد. سنحتاج إلى عقدة واحدة فقط في هذا الدرس التطبيقي حول الترميز. بالنسبة إلى مثيلات الإنتاج وللتأهل لاتفاقية مستوى الخدمة في Cloud Spanner، ستحتاج إلى تشغيل 3 عُقد أو أكثر في مثيل Cloud Spanner.
أخيرًا وليس آخرًا، انقر على "إنشاء" وستكون لديك مثيل Cloud Spanner تحت تصرفك خلال ثوانٍ.
في الخطوة التالية، سنستخدم مكتبة برامج Java لإنشاء قاعدة بيانات ومخطط في المثيل الجديد.
4. إنشاء قاعدة بيانات ومخطط
في هذه الخطوة، سننشئ نموذجًا لقاعدة البيانات والمخطط.
لنستخدم مكتبة برامج Java لإنشاء جدولين؛ جدول "اللاعبين" للحصول على معلومات عن اللاعب وجدول النتائج لتخزين نتائج اللاعبين ولتنفيذ ذلك، سنستعرض خطوات إنشاء تطبيق وحدة تحكُّم Java في Cloud Shell.
استنسِخ أولاً نموذج الرمز البرمجي لهذا الدرس التطبيقي من GitHub من خلال كتابة الأمر التالي في Cloud Shell:
git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
ثم غير الدليل إلى "التطبيقات" الدليل الذي ستنشئ فيه التطبيق.
cd java-docs-samples/spanner/leaderboard
تتوفّر كلّ الرموز المطلوبة لهذا الدرس التطبيقي حول الترميز في دليل java-docs-samples/spanner/leaderboard/complete
الحالي كتطبيق C# قابل للتشغيل واسمه Leaderboard
، لاستخدامه كمرجع أثناء تقدّمك في الدرس التطبيقي حول الترميز. سننشئ دليلاً جديدًا وننشئ نسخة من تطبيق "ليدربورد" على مراحل.
إنشاء دليل جديد باسم "درس تطبيقي حول الترميز" للتطبيق وتغيير الدليل إليه باستخدام الأمر التالي:
mkdir codelab && cd $_
إنشاء تطبيق Java أساسي جديد باسم "Leaderboard" باستخدام أمر Maven (mvn) التالي:
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 لإنشاء لوحة صدارة تتألف من جدولَين. اللاعبون والنتائج ويمكنك إجراء ذلك مباشرةً في "محرِّر Cloud Shell" باتّباع الخطوات التالية:
افتح محرِّر Cloud Shell، من خلال النقر على الرمز الموضح أدناه:
افتح pom.xml ضمن مجلد "ليدربورد". افتح ملف pom.xml المتوفّر في مجلد "java-docs-samples\ spanner\leaderboard\codelab\leaderboard
".
أضِف قسم إدارة التبعية الجديد التالي مباشرةً ضمن المواقع </property> الحالية. العنصر:
<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 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>
احفظ التغييرات التي أجريتها على ملف pom.xml من خلال اختيار "حفظ". ضمن الزر "File" في محرر Cloud Shell القائمة أو بالضغط على "Ctrl" و"S" مفاتيح لوحة المفاتيح معًا.
بعد ذلك، افتح ملف App.java
في محرِّر Cloud Shell المتوفّر في مجلد src/main/java/com/google/codelabs/
. استبدِل الرمز الحالي للملف بالرمز المطلوب لإنشاء قاعدة بيانات leaderboard
والجدولَين Players
وScores
من خلال لصق رمز Java التالي في ملف 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");
}
}
احفظ التغييرات التي أجريتها على ملف App.java
من خلال اختيار "حفظ". ضمن الزر "File" في محرر Cloud Shell القائمة.
يمكنك استخدام ملف App.java
في دليل java-docs-samples/spanner/leaderboard/step4/src
للاطّلاع على مثال حول الشكل الذي يجب أن يظهر به ملف App.java
بعد إضافة الرمز لتفعيل الأمر create
.
لإنشاء تطبيقك، عليك تشغيل حزمة mvn من الدليل الذي يتضمّن pom.xml
:
mvn package
بعد إنشاء ملف Java jar بنجاح، شغِّل التطبيق الناتج في cloud shell من خلال إدخال الأمر التالي:
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
هي معرّف المثيل ومعرّف قاعدة البيانات.
قم الآن بتشغيل الأمر التالي.
java -jar target/leaderboard.jar create cloudspanner-leaderboard leaderboard
بعد بضع ثوانٍ، من المفترض أن يظهر لك رد كالتالي:
Created database [projects/your-project/instances/cloudspanner-leaderboard/databases/leaderboard]
في قسم Cloud Spanner من Cloud Console، من المفترض أن تظهر لك قاعدة البيانات والجداول الجديدة في القائمة الجانبية اليمنى.
في الخطوة التالية سنحدث تطبيقنا لتحميل بعض البيانات إلى قاعدة بياناتك الجديدة.
5- تحميل البيانات
لدينا الآن قاعدة بيانات تسمى leaderboard
تحتوي على جدولين؛ Players
وScores
لنستخدم الآن مكتبة برامج Java لملء جدول Players
باللاعبين وجدول Scores
بنتائج عشوائية لكل لاعب.
إذا لم يكن التطبيق مفتوحًا، افتح محرِّر Cloud Shell بالنقر على الرمز المميّز أدناه:
بعد ذلك، عدِّل ملف App.java
في "محرِّر Cloud Shell" لإضافة الأمر 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;
بعد ذلك، أضِف طرق الإدراج التالية و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
، أضِف الرمز التالي إلى "الرئيسي" في تطبيقك. في عبارة 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()
لتضمين نص المساعدة لأمر الإدراج:
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
من خلال اختيار "حفظ". ضمن الزر "File" في محرر Cloud Shell القائمة.
يمكنك استخدام ملف App.java
في دليل java-docs-samples/spanner/leaderboard/step5/src
للاطّلاع على مثال حول الشكل الذي يجب أن يظهر به ملف App.java
بعد إضافة الرمز لتفعيل الأمر insert
.
والآن، لنعيد إنشاء التطبيق ونشغِّله للتأكّد من تضمين أمر insert
الجديد في قائمة الأوامر المحتملة في التطبيق.
لإنشاء تطبيقك، شغِّل mvn package
من الدليل الذي يتوفّر فيه ملف pom.xml:
mvn package
بعد إنشاء ملف Java 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.
يمكنك أن ترى من الرد أنه بالإضافة إلى معرّف المثيل ومعرّف قاعدة البيانات، هناك وسيطة أخرى يمكن أن تحتوي على قيمة "players" أو "النتائج".
لنقم الآن بتشغيل الأمر insert
باستخدام نفس قيم الوسيطة التي استخدمناها عند استدعينا الأمر create
، مع إضافة "players" باعتباره "نوع الإدخال" الإضافي الوسيطة.
java -jar target/leaderboard.jar insert cloudspanner-leaderboard leaderboard players
بعد بضع ثوانٍ، من المفترض أن يظهر لك رد كالتالي:
Done inserting player records...
لنستخدم الآن مكتبة برامج Java لتعبئة جدول Scores
بأربع نتائج عشوائية بالإضافة إلى طوابع زمنية لكل لاعب في جدول Players
.
تم تعريف عمود Timestamp
في جدول Scores
على أنّه "طابع زمني للالتزام". عبر عبارة 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
"طابعًا زمنيًا للالتزام". وتمكين ملؤه تلقائيًا بالطابع الزمني الدقيق للمعاملة لعمليات 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
تلقائيًا بالطابع الزمني عند ضبط الزر "إدراج" بالضبط تحدث المعاملات، يمكنك بدلاً من ذلك إدراج Value.COMMIT_TIMESTAMP
ثابت لـ Java كما في مقتطف الرمز التالي:
.bind("Timestamp")
.to(Value.COMMIT_TIMESTAMP)
الآن وبعد أن أكملنا تحميل البيانات، لنتحقق من القيم التي كتبناها للتو إلى جداولنا الجديدة. اختَر أولاً قاعدة بيانات leaderboard
ثم اختَر الجدول Players
. انقر على علامة التبويب Data
. من المفترض أن يظهر لك أنّ لديك بيانات في العمودَين PlayerId
وPlayerName
في الجدول.
بعد ذلك، لنتحقق من أن جدول "النتائج" يحتوي أيضًا على بيانات من خلال النقر على جدول Scores
واختيار علامة التبويب Data
. من المفترض أن يظهر لك أنّ لديك بيانات في الأعمدة PlayerId
وTimestamp
وScore
في الجدول.
أحسنت! لنعدّل تطبيقنا لتشغيل بعض طلبات البحث التي يمكننا استخدامها لإنشاء قائمة الصدارة للألعاب.
6- تنفيذ طلبات بحث في قائمة الصدارة
الآن بعد أن أعددت قاعدة البيانات وتحميل المعلومات إلى جداولنا، لنقم بإنشاء قائمة صدارة باستخدام هذه البيانات. ولتنفيذ ذلك، يجب الإجابة عن الأسئلة الأربعة التالية:
- مَن هم اللاعبين ضمن "أفضل عشرة" من جميع الأوقات؟
- مَن هم اللاعبين ضمن "أفضل عشرة" من العام؟
- مَن هم اللاعبين ضمن "أفضل عشرة" من الشهر؟
- مَن هم اللاعبين ضمن "أفضل عشرة" من الأسبوع؟
لنحدث تطبيقنا لتشغيل استعلامات 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)
في الحقل "الرئيسي" لتطبيقك. :
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;
الخطوة الأخيرة لإكمال إضافة "طلب البحث" إلى تطبيقك، هي إضافة نص مساعدة
لطلب البحث إلى الطريقة printUsageAndExit()
. أضِف سطور الرمز التالية إلى طريقة printUsageAndExit()
لتضمين نص المساعدة الخاص بـ "طلب البحث". :
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
من خلال اختيار "حفظ". ضمن الزر "File" في محرر Cloud Shell القائمة.
يمكنك استخدام ملف 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.
يمكنك الاطّلاع من الردّ على أنّه بالإضافة إلى وسيطات معرّف المثيل ومعرّف قاعدة البيانات، يتيح لنا الأمر query
تحديد فترة زمنية اختيارية بعدد الساعات لاستخدامها في فلترة السجلّات استنادًا إلى قيمتها في العمود Timestamp
في الجدول Scores
. بما أنّ وسيطة النطاق الزمني اختيارية، يعني هذا أنّه في حال عدم تضمين وسيطة فترة زمنية، لن تتم فلترة أي سجلّات باستخدام الطوابع الزمنية. إذًا، يمكننا استخدام الأمر 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 وحذف المثيل الذي أنشأناه في خطوة الدرس التطبيقي حول الترميز المسماة "إعداد مثيل Cloud Spanner".
8. تهانينا!
المواضيع التي تناولناها:
- مثيلات Google Cloud Spanner وقواعد البيانات ومخطط الجدول لقائمة الصدارة
- كيفية إنشاء تطبيق وحدة تحكم Java
- كيفية إنشاء قاعدة بيانات وجدول Spanner باستخدام مكتبة برامج Java
- كيفية تحميل البيانات إلى قاعدة بيانات Spanner باستخدام مكتبة برامج Java
- كيفية الاستعلام عن "أعلى عشرة" نتائج من بياناتك باستخدام الطوابع الزمنية لتنفيذ Spanner ومكتبة برامج Java
الخطوات التالية:
- الاطّلاع على التقرير الموجز حول Spanner CAP
- تعرَّف على أفضل ممارسات طلب البحث وتصميم المخطط.
- مزيد من المعلومات حول الطوابع الزمنية للالتزام في Cloud Spanner
يُرجى إرسال ملاحظاتك إلينا
- يُرجى تخصيص بعض الوقت لإكمال الاستطلاع القصير جدًا.