অ্যাসিঙ্ক্রোনাস DAO কোয়েরি লেখা

UI যাতে কোয়েরির কারণে ব্লক না হয়ে যায়, তার জন্য Room মূল থ্রেডে ডেটাবেস অ্যাক্সেস করার সুবিধা দেয় না। এই বিধিনিষেধের অর্থ হল, আপনাকে অবশ্যই DAO কোয়েরি অ্যাসিঙ্ক্রোনাস করতে হবে। অ্যাসিঙ্ক্রোনাস কোয়েরি এক্সিকিউশন প্রদান করতে Room লাইব্রেরিতে একাধিক ফ্রেমওয়ার্কের সাথে ইন্টিগ্রেশন অন্তর্ভুক্ত থাকে।

DAO কোয়েরি তিনটি বিভাগে বিভক্ত:

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

ভাষা ও ফ্রেমওয়ার্ক সংক্রান্ত বিকল্প

নির্দিষ্ট ভাষা ফিচার ও লাইব্রেরির সাথে ইন্টারঅপারেবিলিটির জন্য Room ইন্টিগ্রেশন সংক্রান্ত সহায়তা প্রদান করে। নিচের সারণীতে কোয়েরির ধরন ও ফ্রেমওয়ার্কের উপর ভিত্তি করে প্রযোজ্য রিটার্ন টাইপ দেখানো হয়েছে:

কোয়েরির ধরন Kotlin ভাষার ফিচার (নেটিভ) RxJava গুয়াভা Jetpack Lifecycle*
একবার লেখা করুটিন (suspend) Single<T>, Maybe<T>, Completable ListenableFuture<T> N/A
একবার পড়লেই চলে করুটিন (suspend) Single<T>, Maybe<T> ListenableFuture<T> N/A
পর্যবেক্ষণযোগ্য রিড Flow<T> Flowable<T>, Publisher<T>, Observable<T> N/A LiveData<T>

এই গাইড আপনার DAO-তে অ্যাসিঙ্ক্রোনাস কোয়েরি ইমপ্লিমেন্ট করার জন্য এই ইন্টিগ্রেশনগুলি ব্যবহার করার তিনটি উপায় দেখায়।

Flow ও কোরুটিন সহ Kotlin

Kotlin-এ বিল্ট-ইন ভাষা ফিচার আছে যা আপনাকে থার্ড-পার্টি ফ্রেমওয়ার্ক ছাড়াই অ্যাসিঙ্ক্রোনাস কোয়েরি লিখতে দেয়:

  • পর্যবেক্ষণযোগ্য কোয়েরি লেখার জন্য Room সরাসরি Kotlin-এর Flow সাপোর্ট করে।
  • Kotlin coroutines-এর সাথে আপনার ওয়ান-শট DAO কোয়েরি অ্যাসিঙ্ক্রোনাস করতে Room-এর suspend কীওয়ার্ড প্রয়োজন।

কোর রূম রানটাইমে সরাসরি কোরাউটিন ও ফ্লো সাপোর্ট তৈরি করা হয়, তাই কোনও অতিরিক্ত আর্টিফ্যাক্টের প্রয়োজন হয় না।

Kotlin ও Java-এর জন্য RxJava

Room 3.0, RxJava 3 রিটার্ন টাইপ কাজ করে। RxJava রিটার্ন টাইপ ব্যবহার করতে হলে, আপনাকে অবশ্যই আপনার ডেটাবেস বা DAO-তে RxJava রিটার্ন টাইপ কনভার্টার রেজিস্টার করতে হবে:

  1. আপনার বিল্ড কনফিগারেশনে androidx.room3:room3-rxjava3 আর্টিফ্যাক্ট অন্তর্ভুক্ত করুন।
  2. আপনার @Database বা @Dao ঘোষণায় @DaoReturnTypeConverters(RxDaoReturnTypeConverters::class) যোগ করুন।

Room-এ নিম্নলিখিত RxJava 3 রিটার্ন টাইপ কাজ করে:

LiveData ও Guava

Room 3.0, কনভার্টার ব্যবহার করে LiveData ও Guava ListenableFuture রিটার্ন টাইপ কাজ করে:

  • LiveData: androidx.room3:room3-livedata আর্টিফ্যাক্ট যোগ করুন এবং @DaoReturnTypeConverters(LiveDataDaoReturnTypeConverter::class)-এর মাধ্যমে আপনার ডেটাবেস বা DAO-কে অ্যানোটেট করুন।
  • পেয়ারা: androidx.room3:room3-guava আর্টিফ্যাক্ট যোগ করুন এবং আপনার ডেটাবেস বা DAO-কে @DaoReturnTypeConverters(GuavaDaoReturnTypeConverter::class) দিয়ে অ্যানোটেট করুন।

অ্যাসিঙ্ক্রোনাস ওয়ান-শট কোয়েরি লেখা

ওয়ান-শট কোয়েরি হল ডেটাবেস অপারেশন যা শুধুমাত্র একবারই রান করে এবং এক্সিকিউশনের সময় ডেটার স্ন্যাপশট ক্যাপচার করে। অ্যাসিঙ্ক্রোনাস ওয়ান-শট কোয়েরির কিছু উদাহরণ এখানে দেওয়া হল:

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE id = :id")
    suspend fun loadUserById(id: Int): User

    @Query("SELECT * from user WHERE region IN (:regions)")
    suspend fun loadUsersByRegion(regions: List<String>): List<User>
}

পর্যবেক্ষণযোগ্য কোয়েরি লেখা

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

@Dao
interface ObservableUserDao {
    @Query("SELECT * FROM user WHERE id = :id")
    fun loadUserById(id: Int): Flow<User>

    @Query("SELECT * from user WHERE region IN (:regions)")
    fun loadUsersByRegion(regions: List<String>): Flow<List<User>>
}

ম্যানুয়ালি ডেটাবেস ইনভ্যালিডেশন ট্র্যাক করা

আপনাকে যখন ম্যানুয়ালি অবজার্ভেবল ডেটাবেস অপারেশন তৈরি করতে হবে, তখন আপনি createFlow InvalidationTracker API ব্যবহার করতে পারবেন। এই API আপনাকে এমন একটি Flow তৈরি করতে দেয় যা নির্দিষ্ট টেবিলের পরিবর্তন ট্র্যাক করে এবং সেইসব টেবিল পরিবর্তন হলে বিজ্ঞপ্তি পাঠায়।

fun getArtistTours(db: RoomDatabase, from: Date, to: Date): Flow<Map<Artist, TourState>> {
    return db.invalidationTracker.createFlow("Artist").map { _ ->
        val artists = artistsDao.getAllArtists()
        val tours = tourService.fetchStates(artists.map { it.id })
        associateTours(artists, tours, from, to)
    }
}

ডিফল্ট হিসেবে, রিটার্ন করা Flow স্ট্রিম শুরু করার জন্য রেজিস্টার করা সব টেবিল সহ প্রাথমিক ভ্যালু এমিট করে। emitInitialState প্যারামিটারকে false হিসেবে সেট করে আপনি এই আচরণ বন্ধ করতে পারবেন।

কাস্টম DAO রিটার্ন টাইপ কনভার্টার

Room বা এর এক্সটেনশন লাইব্রেরি সরাসরি সাপোর্ট করে না এমন ধরনের জন্য, অতিরিক্ত রিটার্ন টাইপ সাপোর্ট করতে, আপনি কাস্টম DAO রিটার্ন টাইপ কনভার্টার ডিফাইন করতে পারবেন। DAO ফাংশনের ফলাফলকে আপনার কাস্টম টাইপে পরিবর্তন করতে, @DaoReturnTypeConverter দিয়ে কনভার্টার ফাংশনকে অ্যানোটেট করুন।

যেমন, আপনি এমন একটি কনভার্টারকে সংজ্ঞায়িত করতে পারেন যা কাস্টম TracedQuery-এর মধ্যে এক্সিকিউশনকে র‍্যাপ করে পারফর্ম্যান্স-সংবেদনশীল কোয়েরি মনিটর করার জন্য কোয়েরি এক্সিকিউশনের চারপাশে ট্রেস বিভাগ যোগ করতে androidx.tracing ব্যবহার করে:

class TracedQuery<T>(val result: T)

object TracingDaoReturnTypeConverter {
    @DaoReturnTypeConverter([OperationType.READ])
    suspend fun <T> convert(
        rawQuery: RoomRawQuery,
        executeAndConvert: suspend () -> T
    ): TracedQuery<T> {
        val result = trace("TracedQuery: ${rawQuery.sql}") {
            executeAndConvert()
        }
        return TracedQuery(result)
    }
}

কনভার্টার ব্যবহার করতে, আপনার ডেটাবেস বা DAO-কে @DaoReturnTypeConverters দিয়ে অ্যানোটেট করুন:

@Dao
@DaoReturnTypeConverters(TracingDaoReturnTypeConverter::class)
interface MusicDao {
    @Query("SELECT * FROM Song")
    suspend fun getAllSongs(): TracedQuery<List<Song>>
}

DAO রিটার্ন টাইপ কনভার্টার ইনিশিয়ালাইজেশন কন্ট্রোল করা

সাধারণত, Room DAO রিটার্ন টাইপ কনভার্টার ইনস্ট্যানশিয়েট করে। তবে, আপনার কনভার্টার ক্লাসে অতিরিক্ত ডিপেন্ডেন্সি পাস করতে হলে, আপনার অ্যাপকে অবশ্যই সেগুলির ইনিশিয়ালাইজেশন সরাসরি নিয়ন্ত্রণ করতে হবে। যদি তাই হয়, তাহলে @ProvidedDaoReturnTypeConverter দিয়ে আপনার কনভার্টার ক্লাসকে অ্যানোটেট করুন:

@ProvidedDaoReturnTypeConverter
class TracingDaoReturnTypeConverter(val tracer: Tracer) {
    @DaoReturnTypeConverter([OperationType.READ])
    suspend fun <T> convert(
        rawQuery: RoomRawQuery,
        executeAndConvert: suspend () -> T
    ): TracedQuery<T> {
        val result = tracer.trace("TracedQuery: ${rawQuery.sql}") {
            executeAndConvert()
        }
        return TracedQuery(result)
    }
}

তারপর, @DaoReturnTypeConverters-এ আপনার কনভার্টার ক্লাস ঘোষণা করার পাশাপাশি, RoomDatabase বিল্ডারে আপনার কনভার্টার ক্লাসের একটি ইনস্ট্যান্স পাস করতে RoomDatabase.Builder.addDaoReturnTypeConverter ফাংশন ব্যবহার করুন:

val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name")
    .addDaoReturnTypeConverter(TracingDaoReturnTypeConverter(myLoggerInstance))
    .build()

কনভার্টার ফাংশন সংক্রান্ত প্রয়োজনীয়তা

@DaoReturnTypeConverter ফাংশনকে অবশ্যই একাধিক শর্ত পূরণ করতে হবে:

  • এর শেষ আর্গুমেন্ট হিসেবে একটি কার্যকরী প্যারামিটার থাকতে হবে, সাধারণত এর নাম executeAndConvert হয়। এই প্যারামিটার হল একটি suspend ল্যাম্বডা যা কোয়েরি এক্সিকিউট করতে এবং ফলাফল পার্স করতে Room জেনারেট করে।
    • কনভার্টারকে যদি কোয়েরি ট্রান্সফর্ম করতে হয়, যেমন পেজিং, তাহলে ল্যাম্বডা একটি RoomRawQuery প্যারামিটার নিতে পারে।
  • এটি ঐচ্ছিকভাবে ল্যাম্বডার আগে নিম্নলিখিত প্যারামিটার গ্রহণ করতে পারে:
    • db: RoomDatabase: ডেটাবেস ইনস্ট্যান্স অ্যাক্সেস করে, যা কোরাউটিন স্কোপ পাওয়া বা অতিরিক্ত অপারেশন পারফর্ম করার জন্য উপযোগী।
    • tableNames: Array<String> বা List<String>: কোয়েরি অ্যাক্সেস করা টেবিলের নাম দেয়, যা পর্যবেক্ষণযোগ্য ধরনের জন্য উপযোগী।
    • rawQuery: RoomRawQuery: কোয়েরির রানটাইম ইনস্ট্যান্স প্রদান করে।
    • inTransaction: Boolean: কোনও ট্রানজ্যাকশনের মধ্যে কোয়েরি এক্সিকিউট করা হচ্ছে কিনা তা বোঝায়।