রুম ব্যবহার করে জটিল তথ্য উল্লেখ করা

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
}

এই টাইপ কনভার্টারগুলো সংজ্ঞায়িত করা থাকলে, আপনি আপনার এনটিটি এবং ডিএও-তে আপনার কাস্টম টাইপ ঠিক সেভাবেই ব্যবহার করতে পারবেন, যেভাবে আপনি প্রিমিটিভ টাইপ ব্যবহার করেন:

@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-এর শক্তিশালী কোয়েরি ভ্যালিডেশন ক্ষমতার সাথে মিলিত হয়ে, ডেটা লোড করার সময় আপনার অ্যাপকে কম রিসোর্স ব্যবহার করতে সাহায্য করে, যা আপনার অ্যাপের পারফরম্যান্স এবং ব্যবহারকারীর অভিজ্ঞতা উন্নত করে।