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

เอกสารนี้ครอบคลุมวิธีเขียนข้อมูลไปยังฐานข้อมูลเรียลไทม์ของ Firebase ทั้ง 4 วิธี ได้แก่ การสนับสนุนการตั้งค่า อัปเดต พุช และธุรกรรม

วิธีบันทึกข้อมูล

ตั้งค่า เขียนหรือแทนที่ข้อมูลด้วยเส้นทางที่กำหนด เช่น messages/users/<username>
อัปเดต อัปเดตคีย์บางรายการสำหรับเส้นทางที่กำหนดโดยไม่ต้องแทนที่ข้อมูลทั้งหมด
พุช เพิ่มลงในรายการข้อมูลในฐานข้อมูล ทุกครั้งที่คุณพุชโหนดใหม่ในรายการ ฐานข้อมูลจะสร้างคีย์ที่ไม่ซ้ำกัน เช่น messages/users/<unique-user-id>/<username>
ธุรกรรม ใช้ธุรกรรมเมื่อทำงานกับข้อมูลที่ซับซ้อนซึ่งอาจเสียหายจากการอัปเดตพร้อมกัน

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

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

Java
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("server/saving-data/fireblog");
Node.js
// Import Admin SDK
const { getDatabase } = require('firebase-admin/database');

// Get a database reference to our blog
const db = getDatabase();
const ref = db.ref('server/saving-data/fireblog');
Python
# Import database module.
from firebase_admin import db

# Get a database reference to our blog.
ref = db.reference('server/saving-data/fireblog')
Go
// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our blog.
ref := client.NewRef("server/saving-data/fireblog")

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

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

Java
public static class User {

  public String date_of_birth;
  public String full_name;
  public String nickname;

  public User(String dateOfBirth, String fullName) {
    // ...
  }

  public User(String dateOfBirth, String fullName, String nickname) {
    // ...
  }

}

DatabaseReference usersRef = ref.child("users");

Map<String, User> users = new HashMap<>();
users.put("alanisawesome", new User("June 23, 1912", "Alan Turing"));
users.put("gracehop", new User("December 9, 1906", "Grace Hopper"));

usersRef.setValueAsync(users);
Node.js
const usersRef = ref.child('users');
usersRef.set({
  alanisawesome: {
    date_of_birth: 'June 23, 1912',
    full_name: 'Alan Turing'
  },
  gracehop: {
    date_of_birth: 'December 9, 1906',
    full_name: 'Grace Hopper'
  }
});
Python
users_ref = ref.child('users')
users_ref.set({
    'alanisawesome': {
        'date_of_birth': 'June 23, 1912',
        'full_name': 'Alan Turing'
    },
    'gracehop': {
        'date_of_birth': 'December 9, 1906',
        'full_name': 'Grace Hopper'
    }
})
Go

// User is a json-serializable type.
type User struct {
	DateOfBirth string `json:"date_of_birth,omitempty"`
	FullName    string `json:"full_name,omitempty"`
	Nickname    string `json:"nickname,omitempty"`
}

usersRef := ref.Child("users")
err := usersRef.Set(ctx, map[string]*User{
	"alanisawesome": {
		DateOfBirth: "June 23, 1912",
		FullName:    "Alan Turing",
	},
	"gracehop": {
		DateOfBirth: "December 9, 1906",
		FullName:    "Grace Hopper",
	},
})
if err != nil {
	log.Fatalln("Error setting value:", err)
}

เมื่อบันทึกออบเจ็กต์ JSON ลงในฐานข้อมูลแล้ว พร็อพเพอร์ตี้ของออบเจ็กต์จะแมปกับตำแหน่งของฐานข้อมูลย่อยโดยอัตโนมัติในรูปแบบที่ซ้อนกัน คราวนี้หากไปที่ URL https://docs-examples.firebaseio.com/server/saving-data/fireblog/users/alanisawesome/full_name เราจะเห็นค่า "Alan Turing" นอกจากนี้คุณยังบันทึกข้อมูลลงในตำแหน่งย่อยโดยตรงได้ด้วย โดยทำดังนี้

Java
usersRef.child("alanisawesome").setValueAsync(new User("June 23, 1912", "Alan Turing"));
usersRef.child("gracehop").setValueAsync(new User("December 9, 1906", "Grace Hopper"));
Node.js
const usersRef = ref.child('users');
usersRef.child('alanisawesome').set({
  date_of_birth: 'June 23, 1912',
  full_name: 'Alan Turing'
});
usersRef.child('gracehop').set({
  date_of_birth: 'December 9, 1906',
  full_name: 'Grace Hopper'
});
Python
users_ref.child('alanisawesome').set({
    'date_of_birth': 'June 23, 1912',
    'full_name': 'Alan Turing'
})
users_ref.child('gracehop').set({
    'date_of_birth': 'December 9, 1906',
    'full_name': 'Grace Hopper'
})
Go
if err := usersRef.Child("alanisawesome").Set(ctx, &User{
	DateOfBirth: "June 23, 1912",
	FullName:    "Alan Turing",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

if err := usersRef.Child("gracehop").Set(ctx, &User{
	DateOfBirth: "December 9, 1906",
	FullName:    "Grace Hopper",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

ตัวอย่าง 2 ตัวอย่างข้างต้นคือการเขียนทั้ง 2 ค่าพร้อมกันเป็นออบเจ็กต์ และเขียนแยกกันไปยังตำแหน่งย่อยจะส่งผลให้มีการบันทึกข้อมูลเดียวกันลงในฐานข้อมูล

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper"
    }
  }
}

ตัวอย่างที่ 1 จะทริกเกอร์เพียง 1 เหตุการณ์ในไคลเอ็นต์ที่ดูข้อมูลอยู่ ส่วนตัวอย่างที่ 2 จะทริกเกอร์ 2 เหตุการณ์ โปรดทราบว่าหากมีข้อมูลอยู่แล้วที่ usersRef วิธีการแรกจะเขียนทับข้อมูลนั้น แต่วิธีที่ 2 จะแก้ไขเฉพาะค่าของแต่ละโหนดย่อยที่แยกกันโดยไม่เปลี่ยนแปลงค่าย่อยอื่นๆ ของ usersRef

การอัปเดตข้อมูลที่บันทึกไว้

หากต้องการเขียนไปยังระดับล่างของตำแหน่งฐานข้อมูลหลายรายการพร้อมกันโดยไม่เขียนทับโหนดย่อยอื่นๆ ให้ใช้เมธอดการอัปเดตดังที่แสดงด้านล่าง

Java
DatabaseReference hopperRef = usersRef.child("gracehop");
Map<String, Object> hopperUpdates = new HashMap<>();
hopperUpdates.put("nickname", "Amazing Grace");

hopperRef.updateChildrenAsync(hopperUpdates);
Node.js
const usersRef = ref.child('users');
const hopperRef = usersRef.child('gracehop');
hopperRef.update({
  'nickname': 'Amazing Grace'
});
Python
hopper_ref = users_ref.child('gracehop')
hopper_ref.update({
    'nickname': 'Amazing Grace'
})
Go
hopperRef := usersRef.Child("gracehop")
if err := hopperRef.Update(ctx, map[string]interface{}{
	"nickname": "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating child:", err)
}

การดำเนินการนี้จะอัปเดตข้อมูลของเกสรเพื่อรวมชื่อเล่นของเธอ หากคุณเคยใช้การตั้งค่าที่นี่แทนการอัปเดต ระบบจะลบทั้ง full_name และ date_of_birth ออกจาก hopperRef

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

Java
Map<String, Object> userUpdates = new HashMap<>();
userUpdates.put("alanisawesome/nickname", "Alan The Machine");
userUpdates.put("gracehop/nickname", "Amazing Grace");

usersRef.updateChildrenAsync(userUpdates);
Node.js
const usersRef = ref.child('users');
usersRef.update({
  'alanisawesome/nickname': 'Alan The Machine',
  'gracehop/nickname': 'Amazing Grace'
});
Python
users_ref.update({
    'alanisawesome/nickname': 'Alan The Machine',
    'gracehop/nickname': 'Amazing Grace'
})
Go
if err := usersRef.Update(ctx, map[string]interface{}{
	"alanisawesome/nickname": "Alan The Machine",
	"gracehop/nickname":      "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating children:", err)
}

หลังจากการอัปเดตครั้งนี้ ทั้ง Alan และ Grace ได้เพิ่มชื่อเล่นเป็น:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

โปรดทราบว่าการพยายามอัปเดตออบเจ็กต์ด้วยการเขียนออบเจ็กต์ด้วยเส้นทางที่รวมอยู่จะส่งผลให้มีลักษณะการทำงานที่ต่างกัน เรามาดูกันว่าจะเกิดอะไรขึ้นหากคุณลองอัปเดต Grace และ Alan แบบนี้แทน

Java
Map<String, Object> userNicknameUpdates = new HashMap<>();
userNicknameUpdates.put("alanisawesome", new User(null, null, "Alan The Machine"));
userNicknameUpdates.put("gracehop", new User(null, null, "Amazing Grace"));

usersRef.updateChildrenAsync(userNicknameUpdates);
Node.js
const usersRef = ref.child('users');
usersRef.update({
  'alanisawesome': {
    'nickname': 'Alan The Machine'
  },
  'gracehop': {
    'nickname': 'Amazing Grace'
  }
});
Python
users_ref.update({
    'alanisawesome': {
        'nickname': 'Alan The Machine'
    },
    'gracehop': {
        'nickname': 'Amazing Grace'
    }
})
Go
if err := usersRef.Update(ctx, map[string]interface{}{
	"alanisawesome": &User{Nickname: "Alan The Machine"},
	"gracehop":      &User{Nickname: "Amazing Grace"},
}); err != nil {
	log.Fatalln("Error updating children:", err)
}

ซึ่งส่งผลให้เกิดลักษณะการทำงานที่ต่างกัน ซึ่งก็คือการเขียนทับ /users โหนดทั้งหมด:

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

การเพิ่มโค้ดเรียกกลับสำหรับการเสร็จสมบูรณ์

ใน Node.js และ Java Admin SDK หากต้องการดูเวลาที่ข้อมูลได้รับการคอมมิต ให้เพิ่มโค้ดเรียกกลับที่สมบูรณ์ ทั้งเมธอดการตั้งค่าและการอัปเดตใน SDK เหล่านี้จะใช้โค้ดเรียกกลับความสมบูรณ์แบบไม่บังคับ ซึ่งจะเรียกใช้เมื่อการเขียนคอมมิตกับฐานข้อมูล หากเรียกไม่สำเร็จด้วยเหตุผลบางอย่าง ระบบจะส่งออบเจ็กต์ข้อผิดพลาดเพื่อระบุสาเหตุของความล้มเหลว ใน Python และ Go Admin SDK วิธีการเขียนทั้งหมดจะบล็อกอยู่ กล่าวคือ เมธอดการเขียนจะไม่แสดงผลจนกว่าจะมีการยืนยันการเขียนกับฐานข้อมูล

Java
DatabaseReference dataRef = ref.child("data");
dataRef.setValue("I'm writing data", new DatabaseReference.CompletionListener() {
  @Override
  public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
    if (databaseError != null) {
      System.out.println("Data could not be saved " + databaseError.getMessage());
    } else {
      System.out.println("Data saved successfully.");
    }
  }
});
Node.js
dataRef.set('I\'m writing data', (error) => {
  if (error) {
    console.log('Data could not be saved.' + error);
  } else {
    console.log('Data saved successfully.');
  }
});

การบันทึกรายการข้อมูล

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

// NOT RECOMMENDED - use push() instead!
{
  "posts": {
    "0": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
    },
    "1": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

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

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

Java
public static class Post {

  public String author;
  public String title;

  public Post(String author, String title) {
    // ...
  }

}

DatabaseReference postsRef = ref.child("posts");

DatabaseReference newPostRef = postsRef.push();
newPostRef.setValueAsync(new Post("gracehop", "Announcing COBOL, a New Programming Language"));

// We can also chain the two calls together
postsRef.push().setValueAsync(new Post("alanisawesome", "The Turing Machine"));
Node.js
const newPostRef = postsRef.push();
newPostRef.set({
  author: 'gracehop',
  title: 'Announcing COBOL, a New Programming Language'
});

// we can also chain the two calls together
postsRef.push().set({
  author: 'alanisawesome',
  title: 'The Turing Machine'
});
Python
posts_ref = ref.child('posts')

new_post_ref = posts_ref.push()
new_post_ref.set({
    'author': 'gracehop',
    'title': 'Announcing COBOL, a New Programming Language'
})

# We can also chain the two calls together
posts_ref.push().set({
    'author': 'alanisawesome',
    'title': 'The Turing Machine'
})
Go

// Post is a json-serializable type.
type Post struct {
	Author string `json:"author,omitempty"`
	Title  string `json:"title,omitempty"`
}

postsRef := ref.Child("posts")

newPostRef, err := postsRef.Push(ctx, nil)
if err != nil {
	log.Fatalln("Error pushing child node:", err)
}

if err := newPostRef.Set(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

// We can also chain the two calls together
if _, err := postsRef.Push(ctx, &Post{
	Author: "alanisawesome",
	Title:  "The Turing Machine",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

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

{
  "posts": {
    "-JRHTHaIs-jNPLXOQivY": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
    },
    "-JRHTHaKuITFIhnj02kE": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

ใน JavaScript, Python และ Go รูปแบบการเรียกใช้ push() แล้วเรียก set() ในทันทีนั้นเป็นเรื่องธรรมดามาก ดังนั้น Firebase SDK จะให้คุณรวม Firebase SDK ได้โดยส่งข้อมูลที่ตั้งค่าเป็น push() โดยตรงดังนี้

Java
// No Java equivalent
Node.js
// This is equivalent to the calls to push().set(...) above
postsRef.push({
  author: 'gracehop',
  title: 'Announcing COBOL, a New Programming Language'
});;
Python
# This is equivalent to the calls to push().set(...) above
posts_ref.push({
    'author': 'gracehop',
    'title': 'Announcing COBOL, a New Programming Language'
})
Go
if _, err := postsRef.Push(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

การรับคีย์ที่ไม่ซ้ำกันที่สร้างโดย Push()

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

Java
// Generate a reference to a new location and add some data using push()
DatabaseReference pushedPostRef = postsRef.push();

// Get the unique ID generated by a push()
String postId = pushedPostRef.getKey();
Node.js
// Generate a reference to a new location and add some data using push()
const newPostRef = postsRef.push();

// Get the unique key generated by push()
const postId = newPostRef.key;
Python
# Generate a reference to a new location and add some data using push()
new_post_ref = posts_ref.push()

# Get the unique key generated by push()
post_id = new_post_ref.key
Go
// Generate a reference to a new location and add some data using Push()
newPostRef, err := postsRef.Push(ctx, nil)
if err != nil {
	log.Fatalln("Error pushing child node:", err)
}

// Get the unique key generated by Push()
postID := newPostRef.Key

จะเห็นได้ว่า คุณสามารถดูค่าของคีย์ที่ไม่ซ้ำกันจากข้อมูลอ้างอิงของ push()

ในส่วนถัดไปเกี่ยวกับการดึงข้อมูล เราจะเรียนรู้วิธีอ่านข้อมูลนี้จากฐานข้อมูล Firebase

การบันทึกข้อมูลธุรกรรม

เมื่อทำงานกับข้อมูลที่ซับซ้อนซึ่งอาจเสียหายจากการแก้ไขพร้อมกัน เช่น ตัวนับที่เพิ่มขึ้น SDK จะมีการดำเนินการด้านธุรกรรม

ใน Java และ Node.js คุณจะให้โค้ดเรียกกลับสำหรับการดำเนินการธุรกรรม 2 แบบ ได้แก่ ฟังก์ชันอัปเดตและโค้ดเรียกกลับเสร็จสมบูรณ์ (ไม่บังคับ) ใน Python และ Go การดำเนินการธุรกรรมกำลังบล็อกอยู่ ด้วยเหตุนี้จึงยอมรับเฉพาะฟังก์ชันการอัปเดตเท่านั้น

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

Java
DatabaseReference upvotesRef = ref.child("server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes");
upvotesRef.runTransaction(new Transaction.Handler() {
  @Override
  public Transaction.Result doTransaction(MutableData mutableData) {
    Integer currentValue = mutableData.getValue(Integer.class);
    if (currentValue == null) {
      mutableData.setValue(1);
    } else {
      mutableData.setValue(currentValue + 1);
    }

    return Transaction.success(mutableData);
  }

  @Override
  public void onComplete(
      DatabaseError databaseError, boolean committed, DataSnapshot dataSnapshot) {
    System.out.println("Transaction completed");
  }
});
Node.js
const upvotesRef = db.ref('server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction((current_value) => {
  return (current_value || 0) + 1;
});
Python
def increment_votes(current_value):
    return current_value + 1 if current_value else 1

upvotes_ref = db.reference('server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes')
try:
    new_vote_count = upvotes_ref.transaction(increment_votes)
    print('Transaction completed')
except db.TransactionAbortedError:
    print('Transaction failed to commit')
Go
fn := func(t db.TransactionNode) (interface{}, error) {
	var currentValue int
	if err := t.Unmarshal(&currentValue); err != nil {
		return nil, err
	}
	return currentValue + 1, nil
}

ref := client.NewRef("server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes")
if err := ref.Transaction(ctx, fn); err != nil {
	log.Fatalln("Transaction failed to commit:", err)
}

ตัวอย่างด้านบนจะตรวจสอบว่าตัวนับ null หรือยังไม่มีการเพิ่มขึ้น เนื่องจากเรียกใช้ธุรกรรมได้ด้วย null หากไม่ได้เขียนมูลค่าเริ่มต้นไว้

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

การเชื่อมต่อเครือข่ายและการเขียนแบบออฟไลน์

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

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

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

การรักษาความปลอดภัยข้อมูลของคุณ

ฐานข้อมูลเรียลไทม์ของ Firebase มีภาษาความปลอดภัยที่ช่วยให้คุณกำหนดได้ว่าจะให้ผู้ใช้รายใดมีสิทธิ์อ่านและเขียนในโหนดต่างๆ ของข้อมูล อ่านข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ในรักษาความปลอดภัยข้อมูลของคุณ