Room ব্যবহার করে জটিল ডেটা রেফারেন্স করা

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

টাইপ কনভার্টার ব্যবহার করা

কখনও কখনও, আপনার অ্যাপকে একটি ডেটাবেস কলামে কাস্টম ডেটা টাইপ সেভ করতে হয়। আপনি টাইপ কনভার্টার প্রদান করে কাস্টম টাইপ কাজ করান। এগুলি হল এমন ফাংশন যা Room-কে জানায় যে কীভাবে কাস্টম ডেটার ধরনকে Room-এর সেভ করা যায় এমন পরিচিত ডেটার ধরনে পরিবর্তন করতে হয়। আপনি @ColumnTypeConverter অ্যানোটেশন ব্যবহার করে টাইপ কনভার্টার শনাক্ত করেন।

ধরে নিন, আপনার Room ডেটাবেসে Date-এর ইনস্ট্যান্স সেভ করে রাখতে হবে। Room Date অবজেক্টকে নেটিভভাবে পারসিস্ট করতে পারে না, তাই আপনাকে টাইপ কনভার্টারকে ডিফাইন করতে হবে:

object Converters {
    @ColumnTypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @ColumnTypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time
    }
}

এই উদাহরণে দুই ধরনের কনভার্টার ফাংশন সংজ্ঞায়িত করা হয়েছে: একটি Date অবজেক্টকে Long অবজেক্টে এবং অন্যটি Long অবজেক্টকে আবার Date অবজেক্টে কনভার্ট করে। যেহেতু Room Long অবজেক্ট সেভ করে রাখতে পারে, তাই এটি Date অবজেক্ট সেভ করে রাখার জন্য এইসব কনভার্টার ব্যবহার করতে পারে।

এরপরে, আপনি AppDatabase ক্লাসে @ColumnTypeConverters অ্যানোটেশন যোগ করেন যাতে Room আপনার সংজ্ঞায়িত কনভার্টার ক্লাস ব্যবহার করতে পারে:

@Database(entities = [User::class], version = 1)
@ColumnTypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

এইসব টাইপ কনভার্টারকে সংজ্ঞায়িত করার মাধ্যমে, আপনি নিজের এনটিটি ও DAO-তে কাস্টম টাইপ ব্যবহার করতে পারবেন, ঠিক যেভাবে আদিম টাইপ ব্যবহার করতেন:

@Entity
data class User(
    @PrimaryKey val id: Long,
    val name: String,
    val birthday: Date?
)

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE birthday = :targetDate")
    suspend fun findUsersBornOnDate(targetDate: Date): List<User>
}

এই উদাহরণে আপনি AppDatabase-এর সাথে @ColumnTypeConverters অ্যানোটেশন করেছেন বলে, Room সব জায়গায় সংজ্ঞায়িত টাইপ কনভার্টার ব্যবহার করতে পারবে। এর পরিবর্তে, নির্দিষ্ট এন্টিটি বা DAO-তে স্কোপ টাইপ কনভার্টার করতে, আপনার @Entity বা @Dao ক্লাসকে @ColumnTypeConverters দিয়ে অ্যানোটেট করুন।

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

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

@ProvidedColumnTypeConverter
class ExampleConverter {
    @ColumnTypeConverter
    fun stringToExample(string: String?): ExampleType? {
        return string?.let { ExampleType() }
    }

    @ColumnTypeConverter
    fun exampleToString(example: ExampleType?): String? {
        return example?.toString()
    }
}

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

val db = Room.databaseBuilder<MyDatabase>(applicationContext, "database-name")
    .addColumnTypeConverter(exampleConverterInstance)
    .build()

Room কেন অবজেক্ট রেফারেন্সের অনুমতি দেয় না তা বোঝা

গুরুত্বপূর্ণ: Room এন্টিটি ক্লাসের মধ্যে অবজেক্ট রেফারেন্সের অনুমতি দেয় না। পরিবর্তে, আপনার অ্যাপের প্রয়োজনীয় ডেটা আপনাকে স্পষ্টভাবে অনুরোধ করতে হবে।

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

তবে, ক্লায়েন্ট-সাইডে এই ধরনের লেজি লোডিং সম্ভব নয় কারণ এটি সাধারণত UI থ্রেডে ঘটে এবং UI থ্রেডে ডিস্ক থেকে কোয়েরি করা তথ্য পারফর্ম্যান্স সংক্রান্ত গুরুতর সমস্যা তৈরি করে। UI থ্রেডের কাছে সাধারণত কোনও অ্যাক্টিভিটির আপডেট করা লেআউট গণনা ও ড্র করার জন্য প্রায় ১৬ মিলি সেকেন্ড সময় থাকে, তাই কোনও কোয়েরি ৫ মিলি সেকেন্ড সময় নিলেও, আপনার অ্যাপের ফ্রেম ড্র করার জন্য সময় ফুরিয়ে যাওয়ার সম্ভাবনা থাকে, যার ফলে চোখে পড়ার মতো ভিজ্যুয়াল গ্লিচ হতে পারে। সমান্তরালভাবে আলাদা ট্রানজ্যাকশন চললে অথবা ডিভাইসে ডিস্ক-ইনটেনসিভ টাস্ক চললে, কোয়েরি সম্পূর্ণ হতে আরও বেশি সময় লাগতে পারে। তবে, আপনি লেজি লোডিং ব্যবহার না করলে, আপনার অ্যাপের প্রয়োজনীয়তার চেয়ে বেশি ডেটা ফেচ করে, যার ফলে মেমরি কনজাম্পশন সংক্রান্ত সমস্যা হয়।

অবজেক্ট-রিলেশনাল ম্যাপিং সাধারণত এই সিদ্ধান্ত ডেভেলপারদের উপর ছেড়ে দেয় যাতে তারা তাদের অ্যাপের ব্যবহারের ক্ষেত্রে যা সবচেয়ে ভাল হয় তা করতে পারে। ডেভেলপাররা সাধারণত তাদের অ্যাপ ও UI-এর মধ্যে মডেল শেয়ার করার সিদ্ধান্ত নেন। এই সমাধানটি ভালভাবে স্কেল করে না, তবে, কারণ সময়ের সাথে সাথে UI পরিবর্তিত হওয়ার সাথে সাথে, শেয়ার করা মডেলটি এমন সমস্যা তৈরি করে যা ডেভেলপারদের পক্ষে অনুমান করা এবং ডিবাগ করা কঠিন।

যেমন, এমন একটি UI-এর কথা বিবেচনা করুন যা Book অবজেক্টের একটি তালিকা লোড করে, যেখানে প্রতিটি বই একটি Author অবজেক্ট। আপনি হয়ত প্রথমে এমনভাবে কোয়েরি ডিজাইন করবেন যাতে লেজি লোডিং ব্যবহার করে Book লেখকের ইনস্ট্যান্স রিট্রিভ করা যায়। author প্রপার্টি কোয়েরির প্রথম রিট্রিভাল ডেটাবেস কোয়েরি করে। কিছু সময় পরে, আপনি বুঝতে পারলেন যে আপনার অ্যাপের UI-তেও লেখকের নাম দেখাতে হবে। আপনি নিচের কোড স্নিপেটে দেখানো এই নাম অ্যাক্সেস করতে পারবেন:

Text(text = book.author.name)

তবে, এই আপাতদৃষ্টিতে নিরীহ পরিবর্তনটি Author টেবিলকে মূল থ্রেডে কোয়েরি করার কারণ হয়।

আপনি যদি আগে থেকেই লেখকের তথ্য কোয়েরি করেন কিন্তু সেটির প্রয়োজন না হয়, তাহলে ডেটা লোড করার পদ্ধতি পরিবর্তন করা কঠিন হয়ে যায়। যেমন, আপনার অ্যাপের UI-কে যদি আর Author তথ্য দেখাতে না হয়, তাহলে আপনার অ্যাপ এমন ডেটা লোড করে যা সেটি দেখায় না, এর ফলে মূল্যবান মেমরি স্পেস নষ্ট হয়। Author ক্লাস অন্য টেবিলের রেফারেন্স দিলে, যেমন Books, আপনার অ্যাপের কার্যকারিতা আরও খারাপ হয়ে যায়।

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