เอกสารนี้ครอบคลุมพื้นฐานของการอ่านและการเขียนข้อมูล 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
- การโทรจากโค้ดต้นแบบของแอปโดยใช้ Realtime Database SDK ของแพลตฟอร์ม ตามปกติ หรือใช้ Realtime Database REST API
มีคำแนะนำแบบทีละขั้นตอนโดยละเอียดที่เกี่ยวข้องกับ 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 ที่ใช้ได้มีดังนี้
String
Long
Double
Boolean
Map<String, Object>
List<Object>
- ส่งออบเจ็กต์ Java ที่กำหนดเอง หากคลาสที่กำหนดออบเจ็กต์นั้นมีตัวสร้างเริ่มต้น ที่ไม่รับอาร์กิวเมนต์และมีตัวรับแบบสาธารณะสำหรับพร็อพเพอร์ตี้ ที่จะกำหนด
หากคุณใช้ออบเจ็กต์ 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
การส่งแบบฟอร์ม | การเรียกกลับของเหตุการณ์ | การใช้งานทั่วไป |
---|---|---|
ValueEventListener |
onDataChange() |
อ่านและฟังการเปลี่ยนแปลงเนื้อหาทั้งหมดของเส้นทาง |
คุณสามารถใช้วิธี onDataChange()
เพื่ออ่านสแนปชอตแบบคงที่ของ
เนื้อหาในเส้นทางที่กำหนดได้ตามที่ปรากฏในเวลาที่เกิดเหตุการณ์ เมธอดนี้
จะทริกเกอร์ 1 ครั้งเมื่อแนบ Listener และจะทริกเกอร์อีกครั้งทุกครั้งที่ข้อมูล
รวมถึงข้อมูลของบัญชีย่อยมีการเปลี่ยนแปลง ระบบจะส่งสแนปชอตที่มีข้อมูลทั้งหมดในตำแหน่งนั้น รวมถึงข้อมูลย่อยไปยังการเรียกกลับของเหตุการณ์ หากไม่มีข้อมูล สแนปชอตจะแสดง 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);
ผู้ฟังจะได้รับ DataSnapshot
ซึ่งมีข้อมูลใน
ตำแหน่งที่ระบุในฐานข้อมูล ณ เวลาที่เกิดเหตุการณ์ การเรียกใช้ getValue()
ในสแนปชอตจะแสดงการแทนข้อมูลด้วยออบเจ็กต์ Java หากไม่มีข้อมูลในตำแหน่งดังกล่าว การเรียกใช้ getValue()
จะแสดงผลเป็น null
ในตัวอย่างนี้ ValueEventListener
ยังกำหนดเมธอด onCancelled()
ที่จะเรียกใช้หากมีการยกเลิกการอ่านด้วย เช่น การอ่านอาจถูกยกเลิกหากไคลเอ็นต์ไม่มีสิทธิ์อ่านจากตำแหน่งฐานข้อมูล Firebase ระบบจะส่งDatabaseError
ออบเจ็กต์ที่ระบุสาเหตุที่ทำให้เกิดข้อผิดพลาดไปยัง
เมธอดนี้
อ่านข้อมูลครั้งเดียว
อ่านครั้งเดียวโดยใช้ get()
SDK ได้รับการออกแบบมาเพื่อจัดการการโต้ตอบกับเซิร์ฟเวอร์ฐานข้อมูลไม่ว่าแอปจะออนไลน์หรือออฟไลน์ก็ตาม
โดยทั่วไป คุณควรใช้ValueEventListener
เทคนิคที่อธิบายไว้ข้างต้น
เพื่ออ่านข้อมูลเพื่อรับการแจ้งเตือนเกี่ยวกับการอัปเดตข้อมูลจากแบ็กเอนด์ เทคนิคการฟังจะช่วยลดการใช้งานและการเรียกเก็บเงินของคุณ และได้รับการเพิ่มประสิทธิภาพเพื่อ
มอบประสบการณ์ที่ดีที่สุดแก่ผู้ใช้เมื่อออนไลน์และออฟไลน์
หากต้องการข้อมูลเพียงครั้งเดียว คุณสามารถใช้ get()
เพื่อดูภาพรวมของข้อมูลจากฐานข้อมูลได้
หาก get()
ไม่สามารถแสดงค่าเซิร์ฟเวอร์
ได้ไม่ว่าจะด้วยเหตุผลใดก็ตาม ไคลเอ็นต์จะตรวจสอบแคชในพื้นที่เก็บข้อมูลและแสดงข้อผิดพลาดหากยังไม่พบค่า
การใช้ get()
โดยไม่จำเป็นอาจเพิ่มการใช้แบนด์วิดท์และทำให้ประสิทธิภาพลดลง ซึ่งป้องกันได้โดยใช้เครื่องมือตรวจสอบแบบเรียลไทม์ตามที่แสดงด้านบน
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 สถานที่ การอัปเดตพร้อมกันที่ดำเนินการด้วยวิธีนี้
จะขึ้นอยู่กับความสมบูรณ์ของทั้งชุด: การอัปเดตทั้งหมดจะสำเร็จหรือล้มเหลวทั้งหมด
เพิ่มการเรียกกลับเมื่อเสร็จสมบูรณ์
หากต้องการทราบว่าข้อมูลได้รับการคอมมิตเมื่อใด คุณสามารถเพิ่ม
เครื่องมือฟังการดำเนินการให้เสร็จสมบูรณ์ได้ ทั้ง setValue()
และ updateChildren()
มีตัวเลือก
เครื่องมือฟังการดำเนินการให้เสร็จสมบูรณ์ซึ่งจะเรียกใช้เมื่อเขียนข้อมูล
ลงในฐานข้อมูลเรียบร้อยแล้ว หากการเรียกไม่สำเร็จ ระบบจะส่งออบเจ็กต์ข้อผิดพลาดไปยัง 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
ระบบจะนำการเรียกกลับออกโดยการเรียกใช้เมธอด removeEventListener()
ในการอ้างอิงฐานข้อมูล Firebase
หากมีการเพิ่ม Listener หลายครั้งไปยังตำแหน่งข้อมูล ระบบจะเรียกใช้ Listener หลายครั้งสำหรับแต่ละเหตุการณ์ และคุณต้องยกเลิกการเชื่อมต่อ Listener จำนวนครั้งเท่ากันเพื่อนำออกโดยสมบูรณ์
การเรียกใช้ removeEventListener()
ใน Listener ขององค์กรระดับบนจะไม่
นำ Listener ที่ลงทะเบียนในโหนดขององค์กรย่อยออกโดยอัตโนมัติ
ต้องเรียกใช้ removeEventListener()
ใน Listener ขององค์กรย่อยด้วย
เพื่อนำการเรียกกลับออก
บันทึกข้อมูลเป็นธุรกรรม
เมื่อทำงานกับข้อมูลที่อาจเสียหายจากการแก้ไขพร้อมกัน เช่น ตัวนับแบบเพิ่ม คุณสามารถใช้การดำเนินการธุรกรรมได้ คุณต้องระบุอาร์กิวเมนต์ 2 รายการสำหรับการดำเนินการนี้ ได้แก่ ฟังก์ชันอัปเดตและ การเรียกกลับเมื่อเสร็จสมบูรณ์ (ไม่บังคับ) ฟังก์ชันอัปเดตจะใช้สถานะปัจจุบันของข้อมูลเป็น อาร์กิวเมนต์ และแสดงผลสถานะใหม่ที่ต้องการซึ่งคุณต้องการเขียน หากไคลเอ็นต์อื่นเขียนไปยังตำแหน่งก่อนที่ระบบจะเขียนค่าใหม่ของคุณสำเร็จ ระบบจะเรียกฟังก์ชันอัปเดตอีกครั้งด้วยค่าปัจจุบันใหม่ และจะลองเขียนอีกครั้ง
ตัวอย่างเช่น ในแอปบล็อกโซเชียลตัวอย่าง คุณสามารถอนุญาตให้ผู้ใช้ ติดดาวและยกเลิกการติดดาวโพสต์ รวมถึงติดตามจำนวนดาวที่โพสต์ได้รับ ได้ดังนี้
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); }
โค้ดนี้ไม่ได้ใช้การดำเนินการธุรกรรม จึงไม่เรียกใช้ซ้ำโดยอัตโนมัติ หากมีการอัปเดตที่ขัดแย้งกัน อย่างไรก็ตาม เนื่องจากมีการดำเนินการเพิ่ม ในเซิร์ฟเวอร์ฐานข้อมูลโดยตรง จึงไม่มีโอกาสที่จะเกิดความขัดแย้ง
หากต้องการตรวจหาและปฏิเสธความขัดแย้งที่เฉพาะเจาะจงกับแอปพลิเคชัน เช่น ผู้ใช้ ติดดาวโพสต์ที่ติดดาวไปแล้ว คุณควรเขียน กฎความปลอดภัยที่กำหนดเองสำหรับกรณีการใช้งานนั้น
ทำงานกับข้อมูลแบบออฟไลน์
หากไคลเอ็นต์ขาดการเชื่อมต่อเครือข่าย แอปของคุณจะยังคงทำงานได้อย่างถูกต้อง
ไคลเอ็นต์ทุกรายที่เชื่อมต่อกับฐานข้อมูล Firebase จะเก็บข้อมูลเวอร์ชันภายในของตนเอง ซึ่งเป็นข้อมูลที่มีการใช้ Listener หรือมีการแจ้งว่าให้ซิงค์กับเซิร์ฟเวอร์ เมื่ออ่านหรือเขียนข้อมูล ระบบจะใช้ข้อมูลเวอร์ชันในเครื่องนี้ก่อน จากนั้นไคลเอ็นต์ Firebase จะซิงโครไนซ์ข้อมูลดังกล่าวกับ เซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอ็นต์อื่นๆ โดยใช้หลักการ "พยายามอย่างเต็มที่"
ด้วยเหตุนี้ การเขียนทั้งหมดไปยังฐานข้อมูลจึงทําให้เกิดเหตุการณ์ในเครื่องทันที ก่อนที่จะมีการโต้ตอบกับเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปจะยังคงตอบสนองได้ ไม่ว่าเครือข่ายจะมีความหน่วงหรือการเชื่อมต่อเป็นอย่างไร
เมื่อมีการเชื่อมต่ออีกครั้ง แอปจะได้รับชุดเหตุการณ์ที่เหมาะสมเพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบันโดยไม่ต้องเขียนโค้ดที่กำหนดเอง
เราจะพูดถึงพฤติกรรมออฟไลน์เพิ่มเติมในส่วนดูข้อมูลเพิ่มเติมเกี่ยวกับความสามารถออนไลน์และออฟไลน์