เอกสารนี้ครอบคลุมสี่วิธีในการเขียนข้อมูลไปยังฐานข้อมูล Firebase Realtime ของคุณ: ตั้งค่า อัปเดต พุช และรองรับธุรกรรม
วิธีบันทึกข้อมูล
ชุด | เขียนหรือแทนที่ข้อมูลไปยัง เส้นทางที่กำหนด เช่น messages/users/<username> |
อัปเดต | อัปเดตคีย์บางส่วนสำหรับเส้นทางที่กำหนดโดยไม่ต้องแทนที่ข้อมูลทั้งหมด |
ดัน | เพิ่มลงในรายการข้อมูล ในฐานข้อมูล ทุกครั้งที่คุณพุชโหนดใหม่ลงในรายการ ฐานข้อมูลของคุณจะสร้างคีย์เฉพาะ เช่น messages/users/<unique-user-id>/<username> |
ธุรกรรม | ใช้ธุรกรรมเมื่อทำงานกับข้อมูลที่ซับซ้อนซึ่งอาจเสียหายจากการอัพเดตพร้อมกัน |
การบันทึกข้อมูล
การดำเนินการเขียนฐานข้อมูลพื้นฐานคือชุดที่จะบันทึกข้อมูลใหม่ไปยังการอ้างอิงฐานข้อมูลที่ระบุ โดยแทนที่ข้อมูลที่มีอยู่ในเส้นทางนั้น เพื่อให้เข้าใจชุดนี้ เราจะสร้างแอปบล็อกง่ายๆ ข้อมูลสำหรับแอปของคุณถูกจัดเก็บไว้ที่การอ้างอิงฐานข้อมูลนี้:
ชวา
final FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("server/saving-data/fireblog");
โหนด 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');
หลาม
# Import database module. from firebase_admin import db # Get a database reference to our blog. ref = db.reference('server/saving-data/fireblog')
ไป
// 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 ที่นี่แทนการใช้วิธีพุช เนื่องจากคุณมีรหัสอยู่แล้วและไม่จำเป็นต้องสร้างใหม่
ขั้นแรก สร้างการอ้างอิงฐานข้อมูลไปยังข้อมูลผู้ใช้ของคุณ จากนั้นใช้ set()
/ setValue()
เพื่อบันทึกอ็อบเจ็กต์ผู้ใช้ลงในฐานข้อมูลด้วยชื่อผู้ใช้ ชื่อเต็ม และวันเกิดของผู้ใช้ คุณสามารถส่งผ่านการตั้งค่าสตริง ตัวเลข บูลีน null
อาร์เรย์ หรือออบเจ็กต์ JSON ใดก็ได้ การผ่านค่า null
จะเป็นการลบข้อมูลในตำแหน่งที่ระบุ ในกรณีนี้คุณจะส่งผ่านวัตถุ:
ชวา
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);
โหนด 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' } });
หลาม
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' } })
ไป
// 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" คุณยังสามารถบันทึกข้อมูลลงในตำแหน่งลูกได้โดยตรง:
ชวา
usersRef.child("alanisawesome").setValueAsync(new User("June 23, 1912", "Alan Turing")); usersRef.child("gracehop").setValueAsync(new User("December 9, 1906", "Grace Hopper"));
โหนด 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' });
หลาม
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' })
ไป
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) }
สองตัวอย่างข้างต้น - การเขียนทั้งสองค่าพร้อมกันกับออบเจ็กต์และเขียนแยกกันไปยังตำแหน่งลูก - จะส่งผลให้ข้อมูลเดียวกันถูกบันทึกลงในฐานข้อมูลของคุณ:
{ "users": { "alanisawesome": { "date_of_birth": "June 23, 1912", "full_name": "Alan Turing" }, "gracehop": { "date_of_birth": "December 9, 1906", "full_name": "Grace Hopper" } } }
ตัวอย่างแรกจะทริกเกอร์เพียงเหตุการณ์เดียวบนไคลเอนต์ที่กำลังดูข้อมูล ในขณะที่ตัวอย่างที่สองจะทริกเกอร์สองเหตุการณ์ สิ่งสำคัญคือต้องทราบว่า หากมีข้อมูลอยู่แล้วที่ usersRef
วิธีแรกจะเขียนทับข้อมูลนั้น แต่วิธีที่สองจะแก้ไขเฉพาะค่าของโหนดย่อยแต่ละโหนดที่แยกจากกัน โดยปล่อยให้รายการย่อยอื่นๆ ของ usersRef
ไม่เปลี่ยนแปลง
กำลังอัปเดตข้อมูลที่บันทึกไว้
หากคุณต้องการเขียนไปยังตำแหน่งฐานข้อมูลหลายรายการพร้อมกันโดยไม่ต้องเขียนทับโหนดย่อยอื่น คุณสามารถใช้วิธีการอัปเดตดังที่แสดงด้านล่าง:
ชวา
DatabaseReference hopperRef = usersRef.child("gracehop"); Map<String, Object> hopperUpdates = new HashMap<>(); hopperUpdates.put("nickname", "Amazing Grace"); hopperRef.updateChildrenAsync(hopperUpdates);
โหนด js
const usersRef = ref.child('users'); const hopperRef = usersRef.child('gracehop'); hopperRef.update({ 'nickname': 'Amazing Grace' });
หลาม
hopper_ref = users_ref.child('gracehop') hopper_ref.update({ 'nickname': 'Amazing Grace' })
ไป
hopperRef := usersRef.Child("gracehop") if err := hopperRef.Update(ctx, map[string]interface{}{ "nickname": "Amazing Grace", }); err != nil { log.Fatalln("Error updating child:", err) }
การดำเนินการนี้จะอัปเดตข้อมูลของเกรซเพื่อรวมชื่อเล่นของเธอด้วย หากคุณใช้ set ที่นี่ แทนการอัปเดต ระบบจะลบทั้ง full_name
และ date_of_birth
ออกจาก hopperRef
ของคุณ
ฐานข้อมูลเรียลไทม์ Firebase ยังรองรับการอัปเดตแบบหลายเส้นทางอีกด้วย ซึ่งหมายความว่าขณะนี้การอัปเดตสามารถอัปเดตค่าต่างๆ ในฐานข้อมูลของคุณได้พร้อมๆ กัน ซึ่งเป็นคุณลักษณะที่มีประสิทธิภาพซึ่งช่วยให้คุณ ทำให้ข้อมูลของคุณเป็นปกติได้ เมื่อใช้การอัปเดตแบบหลายเส้นทาง คุณสามารถเพิ่มชื่อเล่นให้กับทั้ง Grace และ Alan ได้ในเวลาเดียวกัน:
ชวา
Map<String, Object> userUpdates = new HashMap<>(); userUpdates.put("alanisawesome/nickname", "Alan The Machine"); userUpdates.put("gracehop/nickname", "Amazing Grace"); usersRef.updateChildrenAsync(userUpdates);
โหนด js
const usersRef = ref.child('users'); usersRef.update({ 'alanisawesome/nickname': 'Alan The Machine', 'gracehop/nickname': 'Amazing Grace' });
หลาม
users_ref.update({ 'alanisawesome/nickname': 'Alan The Machine', 'gracehop/nickname': 'Amazing Grace' })
ไป
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 ด้วยวิธีนี้แทน:
ชวา
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);
โหนด js
const usersRef = ref.child('users'); usersRef.update({ 'alanisawesome': { 'nickname': 'Alan The Machine' }, 'gracehop': { 'nickname': 'Amazing Grace' } });
หลาม
users_ref.update({ 'alanisawesome': { 'nickname': 'Alan The Machine' }, 'gracehop': { 'nickname': 'Amazing Grace' } })
ไป
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 วิธีการเขียนทั้งหมดถูกบล็อก นั่นคือวิธีการเขียนจะไม่ส่งคืนจนกว่าการเขียนจะถูกส่งไปยังฐานข้อมูล
ชวา
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."); } } });
โหนด js
dataRef.set('I\'m writing data', (error) => { if (error) { console.log('Data could not be saved.' + error); } else { console.log('Data saved successfully.'); } });
การบันทึกรายการข้อมูล
เมื่อสร้างรายการข้อมูล สิ่งสำคัญคือต้องคำนึงถึงลักษณะผู้ใช้หลายรายของแอปพลิเคชันส่วนใหญ่ และปรับโครงสร้างรายการของคุณให้เหมาะสม ขยายจากตัวอย่างด้านบน มาเพิ่มโพสต์บล็อกลงในแอปของคุณกัน สัญชาตญาณแรกของคุณอาจใช้ set เพื่อจัดเก็บลูกด้วยดัชนีจำนวนเต็มที่เพิ่มขึ้นอัตโนมัติ ดังต่อไปนี้:
// 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
วิธีนี้จะได้ผลหากมีผู้เขียนเพียงคนเดียวเพิ่มโพสต์ แต่ในแอปพลิเคชันบล็อกการทำงานร่วมกันของคุณ ผู้ใช้หลายคนอาจเพิ่มโพสต์ในเวลาเดียวกัน หากผู้เขียนสองคนเขียนถึง /posts/2
พร้อมกัน หนึ่งในโพสต์นั้นจะถูกลบโดยอีกคนหนึ่ง
เพื่อแก้ปัญหานี้ ไคลเอนต์ Firebase ได้จัดเตรียมฟังก์ชัน push()
ที่สร้าง คีย์ เฉพาะสำหรับรายการย่อยใหม่แต่ละรายการ ด้วยการใช้คีย์ลูกที่ไม่ซ้ำกัน ไคลเอนต์หลายรายสามารถเพิ่มลูกไปยังตำแหน่งเดียวกันในเวลาเดียวกันได้โดยไม่ต้องกังวลกับข้อขัดแย้งในการเขียน
ชวา
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"));
โหนด 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' });
หลาม
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' })
ไป
// 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 ให้คุณรวมพวกมันเข้าด้วยกันโดยส่งข้อมูลที่จะตั้งค่าโดยตรงไปที่ push()
ดังนี้:
ชวา
// No Java equivalent
โหนด js
// This is equivalent to the calls to push().set(...) above postsRef.push({ author: 'gracehop', title: 'Announcing COBOL, a New Programming Language' });;
หลาม
# This is equivalent to the calls to push().set(...) above posts_ref.push({ 'author': 'gracehop', 'title': 'Announcing COBOL, a New Programming Language' })
ไป
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()
จะส่งคืนการอ้างอิงไปยังเส้นทางข้อมูลใหม่ ซึ่งคุณสามารถใช้เพื่อรับคีย์หรือตั้งค่าข้อมูลได้ รหัสต่อไปนี้จะส่งผลให้มีข้อมูลเหมือนกับตัวอย่างข้างต้น แต่ตอนนี้เราจะสามารถเข้าถึงคีย์เฉพาะที่สร้างขึ้นได้:
ชวา
// 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();
โหนด 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;
หลาม
# 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
ไป
// 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 คุณให้การดำเนินการธุรกรรมโทรกลับสองครั้ง: ฟังก์ชันอัปเดตและตัวเลือกการโทรกลับให้เสร็จสิ้น ใน Python และ Go การดำเนินการธุรกรรมถูกบล็อก ดังนั้นจึงยอมรับเฉพาะฟังก์ชันอัปเดตเท่านั้น
ฟังก์ชันอัพเดตจะใช้สถานะปัจจุบันของข้อมูลเป็นอาร์กิวเมนต์ และควรส่งคืนสถานะใหม่ที่คุณต้องการเขียน ตัวอย่างเช่น หากคุณต้องการเพิ่มจำนวนการโหวตเห็นด้วยในบล็อกโพสต์ใดโพสต์หนึ่ง คุณจะต้องเขียนธุรกรรมดังต่อไปนี้:
ชวา
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"); } });
โหนด js
const upvotesRef = db.ref('server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes'); upvotesRef.transaction((current_value) => { return (current_value || 0) + 1; });
หลาม
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')
ไป
fn := func(t db.TransactionNode) (interface{}, error) { var currentValue int if err := t.Unmarshal(¤tValue); 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
ได้หากไม่มีการเขียนค่าเริ่มต้น
หากโค้ดข้างต้นถูกเรียกใช้โดยไม่มีฟังก์ชันธุรกรรมและไคลเอนต์สองรายพยายามที่จะเพิ่มค่าดังกล่าวพร้อมกัน ทั้งคู่จะเขียน 1
เป็นค่าใหม่ ซึ่งส่งผลให้มีการเพิ่มทีละค่าแทนที่จะเป็นสองค่า
การเชื่อมต่อเครือข่ายและการเขียนแบบออฟไลน์
ไคลเอนต์ Firebase Node.js และ Java จะรักษาเวอร์ชันภายในของข้อมูลที่ใช้งานอยู่ เมื่อมีการเขียนข้อมูล ข้อมูลจะถูกเขียนลงในเวอร์ชันโลคัลนี้ก่อน จากนั้นไคลเอ็นต์จะซิงโครไนซ์ข้อมูลนั้นกับฐานข้อมูลและกับไคลเอ็นต์อื่นๆ บนพื้นฐาน 'ความพยายามอย่างดีที่สุด'
ด้วยเหตุนี้ การเขียนทั้งหมดไปยังฐานข้อมูลจะทริกเกอร์เหตุการณ์ภายในเครื่องทันที ก่อนที่ข้อมูลใดๆ จะถูกเขียนลงในฐานข้อมูลด้วยซ้ำ ซึ่งหมายความว่าเมื่อคุณเขียนแอปพลิเคชันโดยใช้ Firebase แอปของคุณจะยังคงตอบสนองโดยไม่คำนึงถึงเวลาแฝงของเครือข่ายหรือการเชื่อมต่ออินเทอร์เน็ต
เมื่อการเชื่อมต่อถูกสร้างขึ้นใหม่แล้ว เราจะได้รับชุดเหตุการณ์ที่เหมาะสมเพื่อให้ไคลเอนต์ "ตามทัน" กับสถานะเซิร์ฟเวอร์ปัจจุบัน โดยไม่ต้องเขียนโค้ดที่กำหนดเองใดๆ
การรักษาความปลอดภัยข้อมูลของคุณ
ฐานข้อมูลเรียลไทม์ของ Firebase มีภาษาความปลอดภัยที่ให้คุณกำหนดว่าผู้ใช้รายใดมีสิทธิ์อ่านและเขียนการเข้าถึงโหนดต่างๆ ของข้อมูลของคุณ คุณสามารถอ่านเพิ่มเติมได้ใน รักษาความปลอดภัยข้อมูลของคุณ