เอกสารนี้ครอบคลุมข้อมูลเบื้องต้นเกี่ยวกับการอ่านและการเขียนข้อมูล Firebase
ระบบจะเขียนข้อมูล Firebase ไปยังการอ้างอิง FirebaseDatabase และดึงข้อมูลโดยแนบ Listener แบบอะซิงโครนัสกับการอ้างอิง โดย Listener จะทริกเกอร์ 1 ครั้งสำหรับสถานะเริ่มต้นของข้อมูล และจะทริกเกอร์อีกครั้งเมื่อข้อมูลมีการเปลี่ยนแปลง
(ไม่บังคับ) สร้างต้นแบบและทดสอบด้วย Firebase Local Emulator Suite
ก่อนที่จะพูดถึงวิธีที่แอปของคุณอ่านและเขียนไปยัง Realtime Database เราขอแนะนำชุดเครื่องมือที่คุณสามารถใช้เพื่อสร้างต้นแบบและทดสอบ ฟังก์ชันการทำงานของ Realtime Database ซึ่งก็คือ Firebase Local Emulator Suite หากคุณกำลังลองใช้โมเดลข้อมูลต่างๆ เพิ่มประสิทธิภาพกฎความปลอดภัย หรือพยายามหาวิธีที่คุ้มค่าที่สุดในการโต้ตอบกับแบ็กเอนด์ การทำงานในเครื่องโดยไม่ต้องติดตั้งใช้งานบริการแบบเรียลไทม์อาจเป็นแนวคิดที่ดี
โปรแกรมจำลอง Realtime Database เป็นส่วนหนึ่งของ Local Emulator Suite ซึ่งช่วยให้แอปของคุณโต้ตอบกับเนื้อหาและการกำหนดค่าฐานข้อมูลที่จำลองขึ้น รวมถึงทรัพยากรโปรเจ็กต์ที่จำลองขึ้น (ฟังก์ชัน ฐานข้อมูลอื่นๆ และกฎความปลอดภัย) ได้ (ไม่บังคับ)
การใช้โปรแกรมจำลอง Realtime Database มีเพียงไม่กี่ขั้นตอนดังนี้
- การเพิ่มบรรทัดโค้ดลงในการกำหนดค่าการทดสอบของแอปเพื่อเชื่อมต่อกับโปรแกรมจำลอง
- จากรูทของไดเรกทอรีโปรเจ็กต์ที่อยู่ในเครื่อง ให้เรียกใช้
firebase emulators:start - การโทรจากโค้ดต้นแบบของแอปโดยใช้ SDK ของแพลตฟอร์ม Realtime Database ตามปกติ หรือใช้ REST API ของ Realtime Database
ดูคำแนะนำแบบทีละขั้นตอนเกี่ยวกับ Realtime Database และ Cloud Functions ได้ นอกจากนี้ คุณควรดูข้อมูลเบื้องต้นเกี่ยวกับ Local Emulator Suite ด้วย
รับ DatabaseReference
หากต้องการอ่านหรือเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference ดังนี้
Kotlin
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
เขียนข้อมูล
การดำเนินการเขียนพื้นฐาน
สำหรับการดำเนินการเขียนพื้นฐาน คุณสามารถใช้ setValue() เพื่อบันทึกข้อมูลไปยังการอ้างอิงที่ระบุ โดยแทนที่ข้อมูลที่มีอยู่ในเส้นทางนั้น คุณใช้วิธีนี้เพื่อทำสิ่งต่อไปนี้ได้
- ประเภทบัตรที่สอดคล้องกับประเภท JSON ที่ใช้ได้มีดังนี้
StringLongDoubleBooleanMap<String, Object>List<Object>
- ส่งออบเจ็กต์ Java ที่กำหนดเอง หากคลาสที่กำหนดออบเจ็กต์มีคอนสตรัคเตอร์เริ่มต้นที่ไม่มีอาร์กิวเมนต์และมี Getter สาธารณะสำหรับพร็อพเพอร์ตี้ที่จะกำหนด
หากคุณใช้ออบเจ็กต์ Java ระบบจะแมปเนื้อหาของออบเจ็กต์กับตำแหน่งย่อยในลักษณะที่ซ้อนกันโดยอัตโนมัติ โดยทั่วไปแล้ว การใช้ออบเจ็กต์ Java จะทำให้โค้ดอ่านง่ายขึ้นและดูแลรักษาได้ง่ายขึ้นด้วย ตัวอย่างเช่น หากคุณมีแอปที่มีโปรไฟล์ผู้ใช้พื้นฐาน ออบเจ็กต์ User อาจมีลักษณะดังนี้
Kotlin
@IgnoreExtraProperties data class User(val username: String? = null, val email: String? = null) { // Null default values create a no-argument default constructor, which is needed // for deserialization from a DataSnapshot. }
Java
@IgnoreExtraProperties public class User { public String username; public String email; public User() { // Default constructor required for calls to DataSnapshot.getValue(User.class) } public User(String username, String email) { this.username = username; this.email = email; } }
คุณเพิ่มผู้ใช้ด้วย setValue() ได้ดังนี้
Kotlin
fun writeNewUser(userId: String, name: String, email: String) { val user = User(name, email) database.child("users").child(userId).setValue(user) }
Java
public void writeNewUser(String userId, String name, String email) { User user = new User(name, email); mDatabase.child("users").child(userId).setValue(user); }
การใช้ setValue() ในลักษณะนี้จะเขียนทับข้อมูลในตำแหน่งที่ระบุ รวมถึงโหนดย่อยทั้งหมด อย่างไรก็ตาม คุณยังคงอัปเดตรายการย่อยได้โดยไม่ต้องเขียนออบเจ็กต์ทั้งหมดใหม่ หากต้องการอนุญาตให้ผู้ใช้อัปเดตโปรไฟล์ คุณสามารถอัปเดตชื่อผู้ใช้ได้ดังนี้
Kotlin
database.child("users").child(userId).child("username").setValue(name)
Java
mDatabase.child("users").child(userId).child("username").setValue(name);
อ่านข้อมูล
อ่านข้อมูลด้วย Listener แบบถาวร
หากต้องการอ่านข้อมูลที่เส้นทางและฟังการเปลี่ยนแปลง ให้ใช้เมธอด addValueEventListener() เพื่อเพิ่ม ValueEventListener ลงใน DatabaseReference
| การส่งแบบฟอร์ม | Callback ของเหตุการณ์ | การใช้งานทั่วไป |
|---|---|---|
ValueEventListener |
onDataChange() |
อ่านและฟังการเปลี่ยนแปลงเนื้อหาทั้งหมดของเส้นทาง |
คุณสามารถใช้เมธอด onDataChange() เพื่ออ่านสแนปชอตแบบคงที่ของเนื้อหาในเส้นทางที่กำหนดตามที่มีอยู่ในขณะที่เกิดเหตุการณ์ ระบบจะเรียกใช้เมธอดนี้ 1 ครั้งเมื่อแนบ Listener และจะเรียกใช้อีกครั้งทุกครั้งที่ข้อมูล รวมถึงข้อมูลย่อย มีการเปลี่ยนแปลง ระบบจะส่งสแนปชอตที่มีข้อมูลทั้งหมดในตำแหน่งนั้น รวมถึงข้อมูลย่อย ไปยัง Callback ของเหตุการณ์ หากไม่มีข้อมูล ภาพรวมจะแสดงผลเป็น false เมื่อคุณเรียกใช้ exists() และแสดงผลเป็น null เมื่อคุณเรียกใช้ getValue()
ตัวอย่างต่อไปนี้แสดงแอปพลิเคชันบล็อกโซเชียลที่ดึงรายละเอียดของโพสต์จากฐานข้อมูล
Kotlin
val postListener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { // Get Post object and use the values to update the UI val post = dataSnapshot.getValue<Post>() // ... } override fun onCancelled(databaseError: DatabaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()) } } postReference.addValueEventListener(postListener)
Java
ValueEventListener postListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Get Post object and use the values to update the UI Post post = dataSnapshot.getValue(Post.class); // .. } @Override public void onCancelled(DatabaseError databaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()); } }; mPostReference.addValueEventListener(postListener);
Listener จะได้รับ DataSnapshot ที่มีข้อมูล ณ ตำแหน่งที่ระบุในฐานข้อมูล ณ เวลาที่เกิดเหตุการณ์ การเรียกใช้ getValue() ในสแนปชอตจะแสดงผลออบเจ็กต์ Java ของข้อมูล หากไม่มีข้อมูลในตำแหน่งดังกล่าว การเรียกใช้ getValue() จะแสดงผลเป็น null
ในตัวอย่างนี้ ValueEventListener ยังกำหนดเมธอด onCancelled() ที่จะเรียกใช้หากการอ่านถูกยกเลิกด้วย เช่น ระบบอาจยกเลิกการอ่านหากไคลเอ็นต์ไม่มีสิทธิ์อ่านจากตำแหน่งฐานข้อมูล Firebase เมธอดนี้จะส่งออบเจ็กต์ DatabaseError ที่ระบุสาเหตุที่เกิดความล้มเหลว
อ่านข้อมูลเพียงครั้งเดียว
อ่านครั้งเดียวโดยใช้ get()
SDK ได้รับการออกแบบมาเพื่อจัดการการโต้ตอบกับเซิร์ฟเวอร์ฐานข้อมูลไม่ว่าแอปของคุณจะออนไลน์หรือออฟไลน์
โดยทั่วไป คุณควรใช้เทคนิค ValueEventListener ที่อธิบายไว้ข้างต้นเพื่ออ่านข้อมูลเพื่อรับการแจ้งเตือนการอัปเดตข้อมูลจากแบ็กเอนด์ เทคนิค Listener จะช่วยลดการใช้งานและการเรียกเก็บเงิน และได้รับการเพิ่มประสิทธิภาพเพื่อมอบประสบการณ์ที่ดีที่สุดแก่ผู้ใช้เมื่อออนไลน์และออฟไลน์
หากต้องการข้อมูลเพียงครั้งเดียว คุณสามารถใช้ get() เพื่อรับภาพรวมของข้อมูลจากฐานข้อมูลได้ หาก get() ไม่สามารถแสดงผลค่าเซิร์ฟเวอร์ได้ไม่ว่าด้วยเหตุผลใดก็ตาม ไคลเอ็นต์จะตรวจสอบแคชพื้นที่เก็บข้อมูลในเครื่องและแสดงข้อผิดพลาดหากยังไม่พบค่าดังกล่าว
การใช้ get() โดยไม่จำเป็นอาจเพิ่มการใช้แบนด์วิดท์และทำให้ประสิทธิภาพลดลง ซึ่งป้องกันได้โดยใช้ Listener แบบเรียลไทม์ดังที่แสดงด้านบน
Kotlin
mDatabase.child("users").child(userId).get().addOnSuccessListener {
Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
Log.e("firebase", "Error getting data", it)
}
Java
mDatabase.child("users").child(userId).get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
@Override
public void onComplete(@NonNull Task<DataSnapshot> task) {
if (!task.isSuccessful()) {
Log.e("firebase", "Error getting data", task.getException());
}
else {
Log.d("firebase", String.valueOf(task.getResult().getValue()));
}
}
});
อ่านครั้งเดียวโดยใช้ Listener
ในบางกรณี คุณอาจต้องการให้ระบบแสดงผลค่าจากแคชในเครื่องทันที แทนที่จะตรวจสอบค่าที่อัปเดตในเซิร์ฟเวอร์ ในกรณีดังกล่าว คุณสามารถใช้ addListenerForSingleValueEvent เพื่อรับข้อมูลจากแคชดิสก์ในเครื่องได้ทันที
ซึ่งมีประโยชน์สำหรับข้อมูลที่ต้องโหลดเพียงครั้งเดียวและไม่คาดว่าจะมีการเปลี่ยนแปลงบ่อยครั้งหรือต้องมีการฟังอยู่ตลอดเวลา ตัวอย่างเช่น แอปบล็อกในตัวอย่างก่อนหน้าใช้วิธีนี้เพื่อโหลดโปรไฟล์ของผู้ใช้เมื่อเริ่มเขียนโพสต์ใหม่
การอัปเดตหรือลบข้อมูล
อัปเดตช่องที่เฉพาะเจาะจง
หากต้องการเขียนไปยังโหนดลูกที่เฉพาะเจาะจงของโหนดพร้อมกันโดยไม่เขียนทับโหนดลูกอื่นๆ ให้ใช้เมธอด updateChildren()
เมื่อเรียกใช้ updateChildren() คุณสามารถอัปเดตค่าระดับย่อยที่ต่ำกว่าได้โดยระบุเส้นทางสำหรับคีย์ หากข้อมูลจัดเก็บไว้ในหลายตำแหน่งเพื่อปรับขนาดให้ดีขึ้น คุณสามารถอัปเดตอินสแตนซ์ทั้งหมดของข้อมูลนั้นได้โดยใช้การกระจายข้อมูล ตัวอย่างเช่น แอปบล็อกโซเชียลอาจมีคลาส Post ดังนี้
Kotlin
@IgnoreExtraProperties data class Post( var uid: String? = "", var author: String? = "", var title: String? = "", var body: String? = "", var starCount: Int = 0, var stars: MutableMap<String, Boolean> = HashMap(), ) { @Exclude fun toMap(): Map<String, Any?> { return mapOf( "uid" to uid, "author" to author, "title" to title, "body" to body, "starCount" to starCount, "stars" to stars, ) } }
Java
@IgnoreExtraProperties public class Post { public String uid; public String author; public String title; public String body; public int starCount = 0; public Map<String, Boolean> stars = new HashMap<>(); public Post() { // Default constructor required for calls to DataSnapshot.getValue(Post.class) } public Post(String uid, String author, String title, String body) { this.uid = uid; this.author = author; this.title = title; this.body = body; } @Exclude public Map<String, Object> toMap() { HashMap<String, Object> result = new HashMap<>(); result.put("uid", uid); result.put("author", author); result.put("title", title); result.put("body", body); result.put("starCount", starCount); result.put("stars", stars); return result; } }
หากต้องการสร้างโพสต์และอัปเดตโพสต์นั้นไปยังฟีดกิจกรรมล่าสุดและฟีดกิจกรรมของผู้ใช้ที่โพสต์พร้อมกัน แอปพลิเคชันบล็อกจะใช้โค้ดดังนี้
Kotlin
private fun writeNewPost(userId: String, username: String, title: String, body: String) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously val key = database.child("posts").push().key if (key == null) { Log.w(TAG, "Couldn't get push key for posts") return } val post = Post(userId, username, title, body) val postValues = post.toMap() val childUpdates = hashMapOf<String, Any>( "/posts/$key" to postValues, "/user-posts/$userId/$key" to postValues, ) database.updateChildren(childUpdates) }
Java
private void writeNewPost(String userId, String username, String title, String body) { // Create new post at /user-posts/$userid/$postid and at // /posts/$postid simultaneously String key = mDatabase.child("posts").push().getKey(); Post post = new Post(userId, username, title, body); Map<String, Object> postValues = post.toMap(); Map<String, Object> childUpdates = new HashMap<>(); childUpdates.put("/posts/" + key, postValues); childUpdates.put("/user-posts/" + userId + "/" + key, postValues); mDatabase.updateChildren(childUpdates); }
ตัวอย่างนี้ใช้ push() เพื่อสร้างโพสต์ในโหนดที่มีโพสต์สำหรับผู้ใช้ทั้งหมดที่ /posts/$postid และดึงข้อมูลคีย์พร้อมกันด้วย getKey() จากนั้นคุณจะใช้คีย์เพื่อสร้างรายการที่ 2 ในโพสต์ของผู้ใช้ที่ /user-posts/$userid/$postid ได้
เมื่อใช้เส้นทางเหล่านี้ คุณจะอัปเดตหลายตำแหน่งในโครงสร้าง JSON พร้อมกันได้ด้วยการเรียก updateChildren() เพียงครั้งเดียว เช่น วิธีที่ตัวอย่างนี้สร้างโพสต์ใหม่ในทั้ง 2 ตำแหน่ง การอัปเดตพร้อมกันที่ทำด้วยวิธีนี้จะเป็นแบบอะตอม กล่าวคือ การอัปเดตทั้งหมดจะสำเร็จหรือล้มเหลวทั้งหมด
เพิ่ม Callback การดำเนินการเสร็จสมบูรณ์
หากต้องการทราบว่าระบบคอมมิตข้อมูลเมื่อใด คุณสามารถเพิ่ม Listener การดำเนินการเสร็จสมบูรณ์ได้ ทั้ง setValue() และ updateChildren() จะใช้ Listener การดำเนินการเสร็จสมบูรณ์ที่ไม่บังคับ ซึ่งจะเรียกใช้เมื่อคอมมิตการเขียนลงในฐานข้อมูลเรียบร้อยแล้ว หากการเรียกไม่สำเร็จ ระบบจะส่งออบเจ็กต์ข้อผิดพลาดที่ระบุสาเหตุของความล้มเหลวไปยัง Listener
Kotlin
database.child("users").child(userId).setValue(user) .addOnSuccessListener { // Write was successful! // ... } .addOnFailureListener { // Write failed // ... }
Java
mDatabase.child("users").child(userId).setValue(user) .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // Write was successful! // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Write failed // ... } });
ลบข้อมูล
วิธีที่ง่ายที่สุดในการลบข้อมูลคือการเรียกใช้ removeValue() ในการอ้างอิงไปยังตำแหน่งของข้อมูลนั้น
นอกจากนี้ คุณยังลบได้โดยระบุ null เป็นค่าสำหรับการดำเนินการเขียนอื่นๆ เช่น setValue() หรือ updateChildren() คุณสามารถใช้เทคนิคนี้กับ updateChildren() เพื่อลบรายการย่อยหลายรายการในการเรียก API ครั้งเดียว
แยก Listener
ระบบจะนำ Callback ออกโดยเรียกใช้เมธอด removeEventListener() ในการอ้างอิงฐานข้อมูล Firebase
หากมีการเพิ่ม Listener หลายครั้งในตำแหน่งข้อมูล ระบบจะเรียกใช้ Listener หลายครั้งสำหรับแต่ละเหตุการณ์ และคุณต้องแยก Listener ออกเป็นจำนวนครั้งเท่ากันเพื่อนำออกอย่างสมบูรณ์
การเรียกใช้ removeEventListener() ใน Listener ระดับบนสุดจะไม่นำ Listener ที่ลงทะเบียนไว้ในโหนดระดับล่างออกโดยอัตโนมัติ คุณต้องเรียกใช้ removeEventListener() ใน Listener ระดับล่างด้วยเพื่อนำ Callback ออก
บันทึกข้อมูลเป็นธุรกรรม
เมื่อทำงานกับข้อมูลที่อาจเสียหายจากการแก้ไขที่เกิดขึ้นพร้อมกัน เช่น ตัวนับที่เพิ่มขึ้น คุณสามารถใช้การดำเนินการธุรกรรมได้ คุณต้องระบุอาร์กิวเมนต์ 2 รายการสำหรับการดำเนินการนี้ ได้แก่ ฟังก์ชันอัปเดตและ Callback การดำเนินการเสร็จสมบูรณ์ (ไม่บังคับ) ฟังก์ชันอัปเดตจะใช้สถานะปัจจุบันของข้อมูลเป็นอาร์กิวเมนต์และแสดงผลสถานะใหม่ที่ต้องการซึ่งคุณต้องการเขียน หากไคลเอ็นต์อื่นเขียนไปยังตำแหน่งก่อนที่จะเขียนค่าใหม่สำเร็จ ระบบจะเรียกใช้ฟังก์ชันอัปเดตอีกครั้งด้วยค่าปัจจุบันใหม่ และจะลองเขียนอีกครั้ง
ตัวอย่างเช่น ในแอปบล็อกโซเชียลตัวอย่าง คุณสามารถอนุญาตให้ผู้ใช้ติดดาวและยกเลิกการติดดาวโพสต์ และติดตามจำนวนดาวที่โพสต์ได้รับดังนี้
Kotlin
private fun onStarClicked(postRef: DatabaseReference) { // ... postRef.runTransaction(object : Transaction.Handler { override fun doTransaction(mutableData: MutableData): Transaction.Result { val p = mutableData.getValue(Post::class.java) ?: return Transaction.success(mutableData) if (p.stars.containsKey(uid)) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1 p.stars.remove(uid) } else { // Star the post and add self to stars p.starCount = p.starCount + 1 p.stars[uid] = true } // Set value and report transaction success mutableData.value = p return Transaction.success(mutableData) } override fun onComplete( databaseError: DatabaseError?, committed: Boolean, currentData: DataSnapshot?, ) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError!!) } }) }
Java
private void onStarClicked(DatabaseReference postRef) { postRef.runTransaction(new Transaction.Handler() { @NonNull @Override public Transaction.Result doTransaction(@NonNull MutableData mutableData) { Post p = mutableData.getValue(Post.class); if (p == null) { return Transaction.success(mutableData); } if (p.stars.containsKey(getUid())) { // Unstar the post and remove self from stars p.starCount = p.starCount - 1; p.stars.remove(getUid()); } else { // Star the post and add self to stars p.starCount = p.starCount + 1; p.stars.put(getUid(), true); } // Set value and report transaction success mutableData.setValue(p); return Transaction.success(mutableData); } @Override public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot currentData) { // Transaction completed Log.d(TAG, "postTransaction:onComplete:" + databaseError); } }); }
การใช้ธุรกรรมจะป้องกันไม่ให้จำนวนดาวไม่ถูกต้องหากผู้ใช้หลายคนติดดาวโพสต์เดียวกันพร้อมกันหรือไคลเอ็นต์มีข้อมูลที่ล้าสมัย หากธุรกรรมถูกปฏิเสธ เซิร์ฟเวอร์จะแสดงผลค่าปัจจุบันไปยังไคลเอ็นต์ ซึ่งจะเรียกใช้ธุรกรรมอีกครั้งด้วยค่าที่อัปเดต ซึ่งจะทำซ้ำจนกว่าธุรกรรมจะได้รับการยอมรับหรือมีการพยายามหลายครั้งเกินไป
การเพิ่มฝั่งเซิร์ฟเวอร์แบบอะตอมมิก
ในกรณีการใช้งานข้างต้น เราจะเขียนค่า 2 ค่าลงในฐานข้อมูล ได้แก่ รหัสของผู้ใช้ที่ติดดาว/ยกเลิกการติดดาวโพสต์ และจำนวนดาวที่เพิ่มขึ้น หากเราทราบอยู่แล้วว่าผู้ใช้ติดดาวโพสต์ เราสามารถใช้การดำเนินการเพิ่มค่าแบบอะตอมแทนธุรกรรมได้
Kotlin
private fun onStarClicked(uid: String, key: String) { val updates: MutableMap<String, Any> = hashMapOf( "posts/$key/stars/$uid" to true, "posts/$key/starCount" to ServerValue.increment(1), "user-posts/$uid/$key/stars/$uid" to true, "user-posts/$uid/$key/starCount" to ServerValue.increment(1), ) database.updateChildren(updates) }
Java
private void onStarClicked(String uid, String key) { Map<String, Object> updates = new HashMap<>(); updates.put("posts/"+key+"/stars/"+uid, true); updates.put("posts/"+key+"/starCount", ServerValue.increment(1)); updates.put("user-posts/"+uid+"/"+key+"/stars/"+uid, true); updates.put("user-posts/"+uid+"/"+key+"/starCount", ServerValue.increment(1)); mDatabase.updateChildren(updates); }
โค้ดนี้ไม่ได้ใช้การดำเนินการธุรกรรม จึงไม่เรียกใช้ซ้ำโดยอัตโนมัติหากมีการอัปเดตที่ขัดแย้งกัน อย่างไรก็ตาม เนื่องจากการดำเนินการเพิ่มค่าเกิดขึ้นโดยตรงบนเซิร์ฟเวอร์ฐานข้อมูล จึงไม่มีโอกาสเกิดความขัดแย้ง
หากต้องการตรวจจับและปฏิเสธความขัดแย้งที่เฉพาะเจาะจงของแอปพลิเคชัน เช่น ผู้ใช้ติดดาวโพสต์ที่เคยติดดาวไว้ก่อนหน้านี้ คุณควรเขียนกฎความปลอดภัยที่กำหนดเองสำหรับ Use Case นั้น
ทำงานกับข้อมูลแบบออฟไลน์
หากไคลเอ็นต์ขาดการเชื่อมต่อเครือข่าย แอปจะยังคงทำงานได้อย่างถูกต้อง
ไคลเอ็นต์ทุกรายที่เชื่อมต่อกับฐานข้อมูล Firebase จะเก็บรักษาข้อมูลเวอร์ชันภายในของตนเองซึ่งมีการใช้ Listener หรือมีการแจ้งว่าให้ซิงค์กับเซิร์ฟเวอร์ เมื่อมีการอ่านหรือเขียนข้อมูล ระบบจะใช้ข้อมูลเวอร์ชันในเครื่องนี้ก่อน จากนั้นไคลเอ็นต์ Firebase จะซิงค์ข้อมูลดังกล่าวกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอ็นต์อื่นๆ โดยใช้ความพยายามอย่างเต็มที่
ด้วยเหตุนี้ การเขียนทั้งหมดลงในฐานข้อมูลจะทริกเกอร์เหตุการณ์ในเครื่องทันทีก่อนที่จะมีการโต้ตอบกับเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปจะยังคงตอบสนองได้ดีไม่ว่าเวลาในการตอบสนองของเครือข่ายหรือการเชื่อมต่อจะเป็นอย่างไร
เมื่อการเชื่อมต่อกลับมาอีกครั้ง แอปของคุณจะได้รับชุดเหตุการณ์ที่เหมาะสมเพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบันโดยไม่ต้องเขียนโค้ดที่กำหนดเอง
เราจะพูดถึงพฤติกรรมออฟไลน์เพิ่มเติมในดูข้อมูลเพิ่มเติมเกี่ยวกับความสามารถแบบออนไลน์และออฟไลน์