অ্যান্ড্রয়েডে ডেটা পড়ুন এবং লিখুন

এই নথিতে Firebase ডেটা পড়ার এবং লেখার মূল বিষয়গুলি অন্তর্ভুক্ত করা হয়েছে।

ফায়ারবেস ডেটা একটি FirebaseDatabase রেফারেন্সে লেখা হয় এবং রেফারেন্সের সাথে একটি অ্যাসিঙ্ক্রোনাস লিসেনার সংযুক্ত করে পুনরুদ্ধার করা হয়। লিসেনারটি ডেটার প্রাথমিক অবস্থার জন্য একবার এবং ডেটা পরিবর্তনের সময় আবার ট্রিগার করা হয়।

(ঐচ্ছিক) Firebase Local Emulator Suite দিয়ে প্রোটোটাইপ এবং পরীক্ষা করুন

আপনার অ্যাপটি Realtime Database থেকে কীভাবে পড়ে এবং লেখে তা নিয়ে কথা বলার আগে, আসুন Realtime Database কার্যকারিতা প্রোটোটাইপ এবং পরীক্ষা করার জন্য আপনি যে টুলগুলি ব্যবহার করতে পারেন তার একটি সেটের সাথে পরিচয় করিয়ে দেই: Firebase Local Emulator Suite । আপনি যদি বিভিন্ন ডেটা মডেল চেষ্টা করে দেখেন, আপনার সুরক্ষা নিয়মগুলি অপ্টিমাইজ করেন, অথবা ব্যাক-এন্ডের সাথে ইন্টারঅ্যাক্ট করার জন্য সবচেয়ে সাশ্রয়ী উপায় খুঁজে বের করার জন্য কাজ করেন, তাহলে লাইভ পরিষেবা স্থাপন না করেই স্থানীয়ভাবে কাজ করতে সক্ষম হওয়া একটি দুর্দান্ত ধারণা হতে পারে।

একটি Realtime Database এমুলেটর হল Local Emulator Suite অংশ, যা আপনার অ্যাপকে আপনার এমুলেটেড ডাটাবেস কন্টেন্ট এবং কনফিগারেশনের সাথে ইন্টারঅ্যাক্ট করতে সক্ষম করে, সেইসাথে ঐচ্ছিকভাবে আপনার এমুলেটেড প্রজেক্ট রিসোর্স (ফাংশন, অন্যান্য ডাটাবেস এবং নিরাপত্তা নিয়ম)।

Realtime Database এমুলেটর ব্যবহার করতে মাত্র কয়েকটি ধাপ জড়িত:

  1. এমুলেটরের সাথে সংযোগ স্থাপনের জন্য আপনার অ্যাপের টেস্ট কনফিগারেশনে কোডের একটি লাইন যোগ করা হচ্ছে।
  2. আপনার স্থানীয় প্রকল্প ডিরেক্টরির রুট থেকে, firebase emulators:start চালান।
  3. আপনার অ্যাপের প্রোটোটাইপ কোড থেকে যথারীতি Realtime Database প্ল্যাটফর্ম SDK ব্যবহার করে অথবা Realtime Database REST API ব্যবহার করে কল করা।

Realtime Database এবং Cloud Functions সম্পর্কে একটি বিস্তারিত ওয়াকথ্রু উপলব্ধ। আপনার Local Emulator Suite ভূমিকাটিও দেখা উচিত।

একটি ডাটাবেস রেফারেন্স পান

ডাটাবেস থেকে ডেটা পড়তে বা লিখতে, আপনার 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>
  • একটি কাস্টম জাভা অবজেক্ট পাস করুন, যদি এটি সংজ্ঞায়িত করে এমন ক্লাসে একটি ডিফল্ট কনস্ট্রাক্টর থাকে যা কোনও আর্গুমেন্ট নেয় না এবং নির্ধারিত বৈশিষ্ট্যগুলির জন্য পাবলিক গেটার থাকে।

যদি আপনি একটি জাভা অবজেক্ট ব্যবহার করেন, তাহলে আপনার অবজেক্টের বিষয়বস্তু স্বয়ংক্রিয়ভাবে একটি নেস্টেড পদ্ধতিতে চাইল্ড লোকেশনে ম্যাপ করা হবে। জাভা অবজেক্ট ব্যবহার করলে সাধারণত আপনার কোডটি আরও পঠনযোগ্য এবং রক্ষণাবেক্ষণ করা সহজ হয়। উদাহরণস্বরূপ, যদি আপনার একটি বেসিক ইউজার প্রোফাইল সহ একটি অ্যাপ থাকে, তাহলে আপনার 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);

তথ্য পড়ুন

অবিচল শ্রোতাদের সাথে ডেটা পড়ুন

কোনও পাথে ডেটা পড়তে এবং পরিবর্তনগুলি শুনতে, addValueEventListener() পদ্ধতি ব্যবহার করে একটি DatabaseReference এ একটি ValueEventListener যোগ করুন।

শ্রোতা ইভেন্ট কলব্যাক সাধারণ ব্যবহার
ValueEventListener onDataChange() একটি পথের সম্পূর্ণ বিষয়বস্তুর পরিবর্তনগুলি পড়ুন এবং শুনুন।

আপনি onDataChange() পদ্ধতি ব্যবহার করে একটি নির্দিষ্ট পাথে থাকা কন্টেন্টের স্ট্যাটিক স্ন্যাপশট পড়তে পারেন, কারণ ইভেন্টের সময় এগুলো বিদ্যমান ছিল। শ্রোতা সংযুক্ত করার সময় এই পদ্ধতিটি একবার ট্রিগার হয় এবং প্রতিবার যখন ডেটা, শিশু সহ, পরিবর্তিত হয়। ইভেন্ট কলব্যাকে সেই অবস্থানের সমস্ত ডেটা ধারণকারী একটি স্ন্যাপশট পাঠানো হয়, যার মধ্যে শিশু ডেটাও অন্তর্ভুক্ত থাকে। যদি কোনও ডেটা না থাকে, তাহলে exists() কল করলে স্ন্যাপশটটি false এবং getValue() কল করলে null ফিরে আসবে।

নিম্নলিখিত উদাহরণটি একটি সোশ্যাল ব্লগিং অ্যাপ্লিকেশন দেখায় যা ডাটাবেস থেকে একটি পোস্টের বিবরণ পুনরুদ্ধার করে:

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() কল করলে ডেটার জাভা অবজেক্টের প্রতিনিধিত্ব ফেরত আসে। যদি অবস্থানে কোনও ডেটা না থাকে, তাহলে 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()));
        }
    }
});

শ্রোতা ব্যবহার করে একবার পড়ুন

কিছু ক্ষেত্রে আপনি সার্ভারে আপডেট করা মান পরীক্ষা করার পরিবর্তে স্থানীয় ক্যাশে থেকে মানটি অবিলম্বে ফেরত পেতে চাইতে পারেন। এই ক্ষেত্রে আপনি স্থানীয় ডিস্ক ক্যাশে থেকে তাৎক্ষণিকভাবে ডেটা পেতে addListenerForSingleValueEvent ব্যবহার করতে পারেন।

এটি এমন ডেটার জন্য কার্যকর যা কেবল একবার লোড করতে হয় এবং ঘন ঘন পরিবর্তন হওয়ার বা সক্রিয় শোনার প্রয়োজন হয় না। উদাহরণস্বরূপ, পূর্ববর্তী উদাহরণগুলিতে ব্লগিং অ্যাপটি কোনও ব্যবহারকারীর প্রোফাইল লোড করার জন্য এই পদ্ধতিটি ব্যবহার করে যখন তারা একটি নতুন পোস্ট লেখা শুরু করে।

ডেটা আপডেট করা বা মুছে ফেলা

নির্দিষ্ট ক্ষেত্রগুলি আপডেট করুন

অন্য চাইল্ড নোডগুলিকে ওভাররাইট না করে একটি নোডের নির্দিষ্ট চাইল্ড্রেনগুলিতে একই সাথে লেখার জন্য, updateChildren() পদ্ধতিটি ব্যবহার করুন।

updateChildren() কল করার সময়, আপনি কী-এর জন্য একটি পাথ নির্দিষ্ট করে নিম্ন-স্তরের চাইল্ড মান আপডেট করতে পারেন। যদি ডেটা আরও ভালোভাবে স্কেল করার জন্য একাধিক স্থানে সংরক্ষণ করা হয়, তাহলে আপনি data fan-out ব্যবহার করে সেই ডেটার সমস্ত উদাহরণ আপডেট করতে পারেন। উদাহরণস্বরূপ, একটি সোশ্যাল ব্লগিং অ্যাপে এইরকম একটি 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() দিয়ে কীটি পুনরুদ্ধার করা হয়। এরপর /user-posts/$userid/$postid এ ব্যবহারকারীর পোস্টে দ্বিতীয় এন্ট্রি তৈরি করতে কীটি ব্যবহার করা যেতে পারে।

এই পাথগুলি ব্যবহার করে, আপনি JSON ট্রিতে একাধিক অবস্থানে updateChildren() কলের মাধ্যমে একযোগে আপডেট করতে পারেন, যেমন এই উদাহরণটি কীভাবে উভয় অবস্থানে নতুন পোস্ট তৈরি করে। এইভাবে করা একযোগে আপডেটগুলি পারমাণবিক: হয় সমস্ত আপডেট সফল হয় অথবা সমস্ত আপডেট ব্যর্থ হয়।

একটি সমাপ্তি কলব্যাক যোগ করুন

আপনার ডেটা কখন কমিট করা হয়েছে তা জানতে চাইলে, আপনি একটি কমপ্লিশন লিসেনার যোগ করতে পারেন। setValue() এবং updateChildren() উভয়ই একটি ঐচ্ছিক কমপ্লিশন লিসেনার নেয় যা ডাটাবেসে লেখা সফলভাবে কমিট করা হলে কল করা হয়। যদি কলটি ব্যর্থ হয়, তাহলে লিসেনারকে একটি ত্রুটি বস্তু পাঠানো হয় যা নির্দেশ করে যে ব্যর্থতা কেন ঘটেছে।

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() কল করা।

আপনি setValue() অথবা updateChildren() মতো অন্য লেখার অপারেশনের জন্য null মান নির্দিষ্ট করে মুছে ফেলতে পারেন। আপনি updateChildren() এর সাথে এই কৌশলটি ব্যবহার করে একটি একক API কলে একাধিক শিশু মুছে ফেলতে পারেন।

শ্রোতাদের আলাদা করুন

আপনার Firebase ডাটাবেস রেফারেন্সে removeEventListener() পদ্ধতিটি কল করে কলব্যাকগুলি সরানো হয়।

যদি কোনও শ্রোতাকে কোনও ডেটা লোকেশনে একাধিকবার যুক্ত করা হয়, তবে প্রতিটি ইভেন্টের জন্য এটি একাধিকবার কল করা হয় এবং এটি সম্পূর্ণরূপে অপসারণ করতে আপনাকে একই সংখ্যক বার এটি বিচ্ছিন্ন করতে হবে।

প্যারেন্ট লিসেনারের উপর removeEventListener() কল করলে তার চাইল্ড নোডে নিবন্ধিত লিসেনারের উপর স্বয়ংক্রিয়ভাবে কল করা হয় না; কলব্যাকটি সরানোর জন্য যেকোনো চাইল্ড লিসেনারের উপর removeEventListener() কল করা আবশ্যক।

লেনদেন হিসেবে ডেটা সংরক্ষণ করুন

যখন আপনি এমন ডেটা নিয়ে কাজ করেন যা সমসাময়িক পরিবর্তনের মাধ্যমে দূষিত হতে পারে, যেমন ইনক্রিমেন্টাল কাউন্টার, তখন আপনি একটি লেনদেন অপারেশন ব্যবহার করতে পারেন। আপনি এই অপারেশনটিকে দুটি আর্গুমেন্ট দেন: একটি আপডেট ফাংশন এবং একটি ঐচ্ছিক সমাপ্তি কলব্যাক। আপডেট ফাংশনটি ডেটার বর্তমান অবস্থাকে একটি আর্গুমেন্ট হিসাবে নেয় এবং আপনি যে নতুন পছন্দসই অবস্থাটি লিখতে চান তা ফেরত দেয়। যদি আপনার নতুন মান সফলভাবে লেখার আগে অন্য কোনও ক্লায়েন্ট অবস্থানে লেখে, তাহলে আপনার আপডেট ফাংশনটি নতুন বর্তমান মান সহ আবার কল করা হবে এবং লেখার পুনরায় চেষ্টা করা হবে।

উদাহরণস্বরূপ, উদাহরণ সোশ্যাল ব্লগিং অ্যাপে, আপনি ব্যবহারকারীদের পোস্টগুলিকে তারকাচিহ্নিত এবং তারকাচিহ্নিত করার অনুমতি দিতে পারেন এবং একটি পোস্ট কতগুলি তারকা পেয়েছে তা ট্র্যাক রাখতে পারেন:

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);
        }
    });
}

যদি একাধিক ব্যবহারকারী একই পোস্টে একই সময়ে তারকাচিহ্নিত করেন অথবা ক্লায়েন্টের কাছে পুরনো ডেটা থাকে, তাহলে লেনদেন ব্যবহার করলে তারকাচিহ্নিত গণনা ভুল হওয়া থেকে রক্ষা পায়। যদি লেনদেনটি প্রত্যাখ্যান করা হয়, তাহলে সার্ভার ক্লায়েন্টকে বর্তমান মান ফেরত দেয়, যা আপডেট করা মান সহ লেনদেনটি আবার চালায়। লেনদেনটি গৃহীত না হওয়া পর্যন্ত বা অনেক প্রচেষ্টা না করা পর্যন্ত এটি পুনরাবৃত্তি হয়।

পারমাণবিক সার্ভার-সাইড বৃদ্ধি

উপরের ব্যবহারের ক্ষেত্রে আমরা ডাটাবেসে দুটি মান লিখছি: পোস্টটি তারকাচিহ্নিত/আনস্টারচিহ্নিতকারী ব্যবহারকারীর আইডি এবং বর্ধিত তারকা গণনা। যদি আমরা ইতিমধ্যেই জানি যে ব্যবহারকারী পোস্টটি তারকাচিহ্নিত করছে, তাহলে আমরা লেনদেনের পরিবর্তে একটি পারমাণবিক বৃদ্ধি অপারেশন ব্যবহার করতে পারি।

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);
}

এই কোডটি কোনও লেনদেন অপারেশন ব্যবহার করে না, তাই কোনও বিরোধপূর্ণ আপডেট থাকলে এটি স্বয়ংক্রিয়ভাবে পুনরায় চালানো হয় না। তবে, যেহেতু ইনক্রিমেন্ট অপারেশনটি সরাসরি ডাটাবেস সার্ভারে ঘটে, তাই কোনও বিরোধের সম্ভাবনা নেই।

যদি আপনি অ্যাপ্লিকেশন-নির্দিষ্ট দ্বন্দ্ব সনাক্ত করতে এবং প্রত্যাখ্যান করতে চান, যেমন একজন ব্যবহারকারী এমন একটি পোস্টকে তারকাচিহ্নিত করেছেন যা তারা ইতিমধ্যেই তারকাচিহ্নিত করেছে, তাহলে আপনার সেই ব্যবহারের ক্ষেত্রে কাস্টম সুরক্ষা নিয়ম লিখতে হবে।

অফলাইনে ডেটা নিয়ে কাজ করুন

যদি কোনও ক্লায়েন্ট তার নেটওয়ার্ক সংযোগ হারিয়ে ফেলে, তাহলে আপনার অ্যাপটি সঠিকভাবে কাজ করতে থাকবে।

ফায়ারবেস ডাটাবেসের সাথে সংযুক্ত প্রতিটি ক্লায়েন্ট শ্রোতাদের ব্যবহার করা হচ্ছে এমন যেকোনো ডেটার নিজস্ব অভ্যন্তরীণ সংস্করণ বজায় রাখে বা সার্ভারের সাথে সিঙ্ক করার জন্য পতাকাঙ্কিত করা হয়। যখন ডেটা পড়া বা লেখা হয়, তখন ডেটার এই স্থানীয় সংস্করণটি প্রথমে ব্যবহার করা হয়। তারপর ফায়ারবেস ক্লায়েন্ট সেই ডেটাকে "সর্বোত্তম প্রচেষ্টা" ভিত্তিতে দূরবর্তী ডাটাবেস সার্ভার এবং অন্যান্য ক্লায়েন্টদের সাথে সিঙ্ক্রোনাইজ করে।

ফলস্বরূপ, ডাটাবেসে লেখা সমস্ত লেখা সার্ভারের সাথে কোনও মিথস্ক্রিয়া করার আগেই স্থানীয় ইভেন্টগুলিকে ট্রিগার করে। এর অর্থ হল নেটওয়ার্ক লেটেন্সি বা সংযোগ নির্বিশেষে আপনার অ্যাপটি প্রতিক্রিয়াশীল থাকে।

একবার সংযোগ পুনঃস্থাপিত হয়ে গেলে, আপনার অ্যাপটি উপযুক্ত ইভেন্টের সেট গ্রহণ করে যাতে ক্লায়েন্টটি কোনও কাস্টম কোড না লিখেই বর্তমান সার্ভারের অবস্থার সাথে সিঙ্ক করে।

আমরা "অনলাইন এবং অফলাইন ক্ষমতা সম্পর্কে আরও জানুন" বিভাগে অফলাইন আচরণ সম্পর্কে আরও আলোচনা করব।

পরবর্তী পদক্ষেপ