การบันทึกข้อมูลด้วยฐานข้อมูลเรียลไทม์ของ Firebase สำหรับ C++

เริ่มต้นใช้งาน

โปรดดูคําแนะนํา Get Started ก่อนหากยังไม่เคย ตั้งค่าแอปและเข้าถึงฐานข้อมูล

รับ DatabaseReference

ในการเขียนข้อมูลไปยังฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference:

    // Get the root reference location of the database.
    firebase::database::DatabaseReference dbref = database->GetReference();

กำลังบันทึกข้อมูล

การเขียนข้อมูลไปยังฐานข้อมูลเรียลไทม์ของ Firebase มี 4 วิธีดังนี้

วิธีการ การใช้งานทั่วไป
SetValue() เขียนหรือแทนที่ข้อมูลในเส้นทางที่กำหนด เช่น users/<user-id>/<username>.
PushChild() เพิ่มลงในรายการข้อมูล ทุกครั้งที่คุณโทร Push() โดย Firebase จะสร้างคีย์ที่ไม่ซ้ำกันซึ่งใช้ได้ด้วยเช่นกัน เป็นตัวระบุที่ไม่ซ้ำกัน เช่น user-scores/<user-id>/<unique-score-id>.
UpdateChildren() อัปเดตคีย์บางรายการสำหรับเส้นทางที่กำหนดโดยไม่ต้องแทนที่ ข้อมูลดังกล่าว
RunTransaction() อัปเดตข้อมูลที่ซับซ้อนที่อาจเสียหายจากการอัปเดตพร้อมกัน

เขียน อัปเดต หรือลบข้อมูลที่ข้อมูลอ้างอิง

การดำเนินการเขียนพื้นฐาน

สำหรับการดำเนินการเขียนพื้นฐาน คุณสามารถใช้ SetValue() เพื่อบันทึกข้อมูลไปยัง ข้อมูลอ้างอิงที่ระบุ โดยแทนที่ข้อมูลที่มีอยู่ในเส้นทางนั้น คุณใช้ ในการส่งประเภทที่ JSON ยอมรับผ่านประเภทตัวแปรที่รองรับ:

  • Null (การดำเนินการนี้จะลบข้อมูล)
  • จำนวนเต็ม (64 บิต)
  • ตัวเลขทศนิยมแบบความแม่นยำสองเท่า
  • บูลีน
  • สตริง
  • เวกเตอร์ของตัวแปร
  • การแมปสตริงกับตัวแปร

การใช้ SetValue() ด้วยวิธีนี้จะเขียนทับข้อมูลในตำแหน่งที่ระบุ รวมถึงโหนดย่อย อย่างไรก็ตาม คุณยังอัปเดตรายการย่อยได้โดยไม่ต้อง การเขียนออบเจ็กต์ใหม่ทั้งหมด หากต้องการอนุญาตให้ผู้ใช้อัปเดตโปรไฟล์ของตนเอง คุณสามารถอัปเดตชื่อผู้ใช้ได้ดังนี้

dbref.Child("users").Child(userId).Child("username").SetValue(name);

ต่อท้ายรายการข้อมูล

ใช้เมธอด PushChild() เพื่อต่อท้ายรายการในแอปพลิเคชันที่มีผู้ใช้หลายคน เมธอด PushChild() จะสร้างคีย์ที่ไม่ซ้ำกันทุกครั้งที่มีการเรียก ลงในการอ้างอิง Firebase ที่ระบุ ด้วยการใช้สิ่งเหล่านี้ คีย์ที่สร้างขึ้นโดยอัตโนมัติสำหรับองค์ประกอบใหม่แต่ละรายการในรายการ โดยไคลเอ็นต์หลายราย เพิ่มเด็กไปยังตำแหน่งที่ตั้งเดียวกันพร้อมกันโดยไม่มีความขัดแย้งในการเขียน คีย์ที่ไม่ซ้ำกันที่ PushChild() สร้างขึ้นจะอิงตามการประทับเวลา ดังนั้นรายการจึง จะเรียงตามลำดับเวลาโดยอัตโนมัติ

คุณสามารถใช้การอ้างอิงข้อมูลใหม่ที่แสดงผลโดยเมธอด PushChild() ได้ เพื่อรับค่าของคีย์ที่สร้างขึ้นโดยอัตโนมัติของบุตรหลานหรือตั้งค่าข้อมูลสำหรับบุตรหลาน การเรียกใช้ GetKey() ในการอ้างอิง PushChild() จะแสดงค่าของแอตทริบิวต์ ที่สร้างโดยอัตโนมัติ

อัปเดตช่องข้อมูลที่เฉพาะเจาะจง

เพื่อเขียนไปยังโหนดย่อยที่ต้องการพร้อมกันโดยไม่เขียนทับโหนดอื่น โหนดย่อย ให้ใช้เมธอด UpdateChildren()

เมื่อโทรหา UpdateChildren() คุณจะอัปเดตค่าย่อยระดับล่างได้โดย โดยระบุเส้นทางของคีย์นี้ หากมีการจัดเก็บข้อมูลในหลายตำแหน่งเพื่อปรับขนาด คุณจะสามารถอัปเดตอินสแตนซ์ทั้งหมดของข้อมูลนั้นได้โดยใช้ การขยายข้อมูล ตัวอย่างเช่น เกมอาจมีคลาส LeaderboardEntry ในลักษณะนี้

class LeaderboardEntry {
  std::string uid;
  int score = 0;

 public:
  LeaderboardEntry() {
  }

  LeaderboardEntry(std::string uid, int score) {
    this->uid = uid;
    this->score = score;
  }

  std::map<std::string, Object> ToMap() {
    std::map<string, Variant> result = new std::map<string, Variant>();
    result["uid"] = Variant(uid);
    result["score"] = Variant(score);

    return result;
  }
}

เพื่อสร้าง LeaderboardEntry และอัปเดตให้เป็นคะแนนล่าสุดพร้อมกัน และรายการคะแนนของผู้ใช้เอง เกมจะใช้โค้ดต่อไปนี้

void WriteNewScore(std::string userId, int score) {
  // Create new entry at /user-scores/$userid/$scoreid and at
  // /leaderboard/$scoreid simultaneously
  std::string key = dbref.Child("scores").PushChild().GetKey();
  LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
  std::map<std::string, Variant> entryValues = entry.ToMap();

  std::map<string, Variant> childUpdates = new std::map<string, Variant>();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

ตัวอย่างนี้ใช้ PushChild() เพื่อสร้างรายการในโหนดที่มี รายการสำหรับผู้ใช้ทั้งหมดที่ /scores/$key และเรียกข้อมูลคีย์พร้อมกันด้วย key() จากนั้นสามารถใช้คีย์เพื่อสร้างรายการที่ 2 ในส่วน คะแนนในนาทีที่ /user-scores/$userid/$key

การใช้เส้นทางเหล่านี้จะทำให้คุณสามารถอัปเดตสถานที่หลายแห่งพร้อมกันใน โครงสร้าง JSON ที่มีการเรียกไปยัง UpdateChildren() ครั้งเดียว เช่น สร้างรายการใหม่ในทั้ง 2 ตำแหน่ง การอัปเดตในเวลาเดียวกันทำให้รายการนี้ ต่างกันตรงที่ว่าการอัปเดตทั้งหมดสำเร็จหรือล้มเหลว

ลบข้อมูล

วิธีที่ง่ายที่สุดในการลบข้อมูลคือการเรียกใช้ RemoveValue() ตามการอ้างอิง ตำแหน่งของข้อมูลนั้น

ก็ลบได้ด้วยการระบุ null Variant เป็นค่าสำหรับการเขียนอื่น เช่น SetValue() หรือ UpdateChildren() คุณใช้ เทคนิคที่มี UpdateChildren() ในการลบรายการย่อยหลายรายการใน API เดียว การโทร

รับข้อมูลเมื่อมีการคอมมิตข้อมูลของคุณ

หากต้องการทราบเมื่อข้อมูลของคุณผูกอยู่กับเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ของ Firebase ให้ตรวจสอบ ผลลัพธ์อนาคตสำหรับความสำเร็จ

บันทึกข้อมูลเป็นธุรกรรม

เมื่อทำงานกับข้อมูลที่อาจเสียหายจากการเชื่อมต่อพร้อมกัน เช่น ตัวนับที่เพิ่มขึ้น คุณสามารถใช้ การดำเนินการธุรกรรม คุณกำหนดให้การดำเนินการนี้มีฟังก์ชัน DoTransaction ฟังก์ชันอัปเดตนี้ใช้เวลา สถานะปัจจุบันของข้อมูลเป็นอาร์กิวเมนต์ และแสดงผลสถานะที่ต้องการใหม่ ที่คุณต้องการเขียน ถ้าไคลเอ็นต์อื่นเขียนไปยังตำแหน่งก่อนหน้า เขียนค่าใหม่เรียบร้อยแล้ว ฟังก์ชันอัปเดตจะถูกเรียกใช้อีกครั้งพร้อมด้วย ค่าปัจจุบันใหม่ และจะพยายามเขียนอีกครั้ง

เช่น ในเกมที่คุณอนุญาตให้ผู้ใช้อัปเดตลีดเดอร์บอร์ด คะแนนสูงสุด 5 อันดับ ได้แก่

void AddScoreToLeaders(std::string email,
                       long score,
                       DatabaseReference leaderBoardRef) {
  leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) {
    if (mutableData.children_count() >= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector<MutableData> children = mutableData.children();
      std::vector<MutableData>::iterator it;
      for (it = children.begin(); it != children.end(); ++it) {
        if (!it->value().is_map())
          continue;
        long childScore = (long)it->Child("score").value().int64_value();
        if (childScore < minScore) {
          minScore = childScore;
          minVal = &*it;
        }
      }
      if (minScore > score) {
        // The new score is lower than the existing 5 scores, abort.
        return kTransactionResultAbort;
      }

      // Remove the lowest score.
      children.Remove(minVal);
    }

    // Add the new high score.
    std::map<std::string, Variant> newScoreMap =
      new std::map<std::string, Variant>();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

การใช้ธุรกรรมจะป้องกันไม่ให้ลีดเดอร์บอร์ดไม่ถูกต้องหากมีหลายรายการ ผู้ใช้บันทึกคะแนนในเวลาเดียวกันหรือไคลเอ็นต์มีข้อมูลเก่า หาก ปฏิเสธธุรกรรม เซิร์ฟเวอร์จะส่งคืนมูลค่าปัจจุบันไปยังไคลเอ็นต์ ซึ่งจะเรียกใช้ธุรกรรมอีกครั้งด้วยมูลค่าที่อัปเดตแล้ว ซึ่งจะเกิดซ้ำจนถึง ยอมรับธุรกรรมหรือพยายามดำเนินการหลายครั้งเกินไป

เขียนข้อมูลแบบออฟไลน์

หากลูกค้าสูญเสียการเชื่อมต่อเครือข่าย แอปจะยังคงทำงานต่อไป อย่างถูกต้อง

ลูกค้าทุกรายที่เชื่อมต่อกับฐานข้อมูล Firebase จะมีเวอร์ชันภายในของตนเอง ของข้อมูลที่ใช้งานอยู่ เมื่อเขียนข้อมูล ระบบจะเขียนลงในเวอร์ชันนี้ในเครื่อง ก่อน จากนั้นไคลเอ็นต์ Firebase จะซิงค์ข้อมูลนั้นกับฐานข้อมูลระยะไกล เซิร์ฟเวอร์และไคลเอ็นต์อื่นๆ อย่าง "สุดความสามารถ" พื้นฐาน

ด้วยเหตุนี้ การเขียนทั้งหมดไปยังฐานข้อมูลจะทริกเกอร์เหตุการณ์ในระบบทันที ข้อมูลใดก็ตามที่เขียนไปยังเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปของคุณจะยัง ตอบสนองตามอุปกรณ์โดยไม่คำนึงถึงเวลาในการตอบสนองหรือการเชื่อมต่อของเครือข่าย

เมื่อเชื่อมต่ออินเทอร์เน็ตอีกครั้งแล้ว แอปจะได้รับชุด เหตุการณ์เพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบันโดยไม่ต้อง เขียนโค้ดที่กำหนดเอง

ขั้นตอนถัดไป