অবজেক্টের মধ্যে সম্পর্কের ধরন বেছে নিন

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

সম্পর্কের ধরন

Room-এ নিম্নলিখিত ধরনের সম্পর্ক কাজ করে:

  • এক-এক: এমন সম্পর্ককে বোঝায় যেখানে একটি এন্টিটি অন্য একটি এন্টিটির সাথে সম্পর্কিত।
  • এক থেকে একাধিক: এমন সম্পর্ককে বোঝায় যেখানে একটি এন্টিটি অন্য ধরনের একাধিক এন্টিটির সাথে সম্পর্কিত হতে পারে।
  • একের সাথে অনেকের: এমন সম্পর্ককে বোঝায় যেখানে এক ধরনের একাধিক এন্টিটি অন্য ধরনের একাধিক এন্টিটির সাথে সম্পর্কিত হতে পারে। এর জন্য সাধারণত একটি জাংশন টেবিল প্রয়োজন হয়।
  • এম্বেড করা অবজেক্ট ব্যবহার করে নেস্টেড সম্পর্ক: এমন একটি সম্পর্ককে বোঝায় যেখানে একটি এন্টিটি প্রপার্টি হিসেবে অন্য একটি এন্টিটিকে কন্টেন করে এবং এই নেস্টেড এন্টিটি আরও অন্যান্য এন্টিটিকে কন্টেন করতে পারে। এটি @Embedded অ্যানোটেশন ব্যবহার করে।

দুটি পদ্ধতির মধ্যে থেকে বেছে নিন

Room-এ, এন্টিটিগুলির মধ্যে সম্পর্ককে সংজ্ঞায়িত ও কোয়েরি করার দুটি উপায় রয়েছে। আপনি এগুলির মধ্যে যেকোনও একটি ব্যবহার করতে পারেন:

  • এম্বেড করা অবজেক্ট সহ ইন্টারমিডিয়েট ডেটা ক্লাস অথবা
  • মাল্টিম্যাপ রিটার্ন টাইপ সহ রিলেশনাল কোয়েরি ফাংশন।

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

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

ইন্টারমিডিয়েট ডেটা ক্লাস অ্যাপ্রোচ ব্যবহার করা

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

যেমন, আপনি লাইব্রেরির ব্যবহারকারীদের প্রতিনিধিত্ব করার জন্য UserBook ডেটা ক্লাস নির্ধারণ করতে পারেন, যারা নির্দিষ্ট বই চেক-আউট করেছেন এবং ডেটাবেস থেকে UserBook ইনস্ট্যান্সের একটি তালিকা রিট্রিভ করার জন্য একটি কোয়েরি ফাংশন নির্ধারণ করতে পারেন:

@Dao
interface UserBookDao {
    @Query(
        """
        SELECT user.name AS userName, book.name AS bookName
        FROM user JOIN book ON user.id = book.user_id
        """
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

data class UserBook(val userName: String, val bookName: String)

মাল্টিম্যাপ রিটার্ন টাইপ অ্যাপ্রোচ ব্যবহার করা

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

যেমন, নিম্নলিখিত কোয়েরি ফাংশন, নির্দিষ্ট বই চেক-আউট করা লাইব্রেরি ব্যবহারকারীদের বোঝাতে User এবং Book ইনস্ট্যান্সের একটি ম্যাপিং রিটার্ন করে:

@Query(
    """
    SELECT *
    FROM user JOIN book ON user.id = book.user_id
    """
)
suspend fun loadUserAndBookNames(): Map<User, List<Book>>

মাল্টিম্যাপ রিটার্ন টাইপের সাহায্যে, আপনি এমন ওয়ান-টু-ওয়ান কোয়েরিও করতে পারবেন যেগুলিতে অন্য কোনও এন্টিটি অন্তর্ভুক্ত থাকে না। নিম্নলিখিত কোয়েরি ফাংশন User-এর ম্যাপিং এবং @MapColumn অ্যানোটেশন ব্যবহার করে চেক-আউট করা বইয়ের সংখ্যা রিটার্ন করে:

@Query(
    """
    SELECT user.*, COUNT(book.id) AS book_count
    FROM user LEFT JOIN book ON user.id = book.user_id
    GROUP BY user.id
    """
)
suspend fun loadUserAndBookCount(): Map<User, @MapColumn(columnName = "book_count") Int>

এম্বেড করা অবজেক্ট তৈরি করা

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

যেমন, আপনার User ক্লাসে এমন Address প্রপার্টি থাকতে পারে যা street, city, state ও postCode প্রপার্টির কম্পোজিশনকে রিপ্রেজেন্ট করে। টেবিলে কম্পোজ করা কলাম আলাদাভাবে স্টোর করতে, User ক্লাসে @Embedded দিয়ে Address প্রপার্টি অ্যানোটেট করুন। নিচে দেওয়া কোড স্নিপেট থেকে এই সেট-আপ সম্পর্কে জানতে পারবেন:

data class Address(
    val street: String?,
    val state: String?,
    val city: String?,
    @ColumnInfo(name = "post_code") val postCode: Int
)

@Entity
data class User(
    @PrimaryKey val id: Int,
    val firstName: String,
    @Embedded val address: Address?
)

User অবজেক্টের প্রতিনিধিত্বকারী টেবিলে নিম্নলিখিত নামের কলাম থাকে: id, firstName, street, state, city এবং post_code.

কোনও এন্টিটিতে একই ধরনের একাধিক এম্বেড করা প্রপার্টি থাকলে, আপনি prefix প্রপার্টি সেট করে প্রতিটি কলামকে অনন্য রাখতে পারবেন। এম্বেড করা অবজেক্টের প্রতিটি কলাম নামের শুরুতে Room প্রদান করা ভ্যালু যোগ করে।