از آنجا که SQLite یک پایگاه داده رابطهای است، میتوانید روابط بین موجودیتها را تعریف کنید. اما در حالی که اکثر کتابخانههای نگاشت شیء-رابطهای به اشیاء موجودیت اجازه میدهند به یکدیگر ارجاع دهند، Room صراحتاً این کار را ممنوع میکند. برای آشنایی با استدلال فنی پشت این تصمیم، به بخش «درک اینکه چرا Room اجازه ارجاع به اشیاء را نمیدهد» مراجعه کنید.
انواع روابط
روم از انواع روابط زیر پشتیبانی میکند:
- یک به یک : نشاندهنده رابطهای است که در آن یک موجودیت واحد با موجودیت واحد دیگری مرتبط است.
- یک به چند : نشاندهنده رابطهای است که در آن یک موجودیت واحد میتواند با چندین موجودیت از نوع دیگر مرتبط باشد.
- چند به چند : نشاندهنده رابطهای است که در آن چندین موجودیت از یک نوع میتوانند به چندین موجودیت از نوع دیگر مرتبط باشند. این معمولاً به یک جدول اتصال نیاز دارد.
- روابط تو در تو با استفاده از اشیاء تعبیه شده : رابطهای را نشان میدهد که در آن یک موجودیت شامل موجودیت دیگری به عنوان یک ویژگی است و این موجودیت تو در تو میتواند شامل موجودیتهای دیگری نیز باشد. این از حاشیهنویسی
@Embeddedاستفاده میکند.
انتخاب بین دو رویکرد
در Room، دو روش برای تعریف و پرسوجوی رابطه بین موجودیتها وجود دارد. میتوانید از هر یک از موارد زیر استفاده کنید:
- یک کلاس داده میانی با اشیاء تعبیه شده، یا
- یک تابع پرسوجوی رابطهای با نوع بازگشتی چندنقشهای.
اگر دلیل خاصی برای استفاده از کلاسهای داده میانی ندارید، توصیه میکنیم از رویکرد نوع بازگشتی multimap استفاده کنید. برای کسب اطلاعات بیشتر در مورد این رویکرد، به Return a multimap مراجعه کنید.
رویکرد کلاس داده میانی به شما امکان میدهد از نوشتن کوئریهای پیچیده SQL اجتناب کنید، اما میتواند منجر به افزایش پیچیدگی کد نیز شود زیرا به کلاسهای داده اضافی نیاز دارد. به طور خلاصه، رویکرد نوع بازگشتی multimap مستلزم آن است که کوئریهای 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)
از رویکرد نوع بازگشتی چندنقشهای (multimap) استفاده کنید
در رویکرد نوع بازگشتی multimap، نیازی به تعریف هیچ کلاس داده اضافی ندارید. در عوض، یک نوع بازگشتی multimap برای تابع خود بر اساس ساختار نقشه مورد نظر خود تعریف میکنید و رابطه بین موجودیتهای خود را مستقیماً در پرس و جوی SQL خود تعریف میکنید.
برای مثال، تابع پرسوجوی زیر، نگاشتی از نمونههای User و Book را برمیگرداند تا کاربران کتابخانه را با کتابهای خاصِ امانتگرفتهشده نشان دهد:
@Query( """ SELECT * FROM user JOIN book ON user.id = book.user_id """ ) suspend fun loadUserAndBookNames(): Map<User, List<Book>>
با انواع بازگشتی multimap، میتوانید روابط یک به یک را که شامل موجودیت دیگری نمیشوند، نیز جستجو کنید. تابع جستجوی زیر، نگاشتی از 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 را نشان میدهد. برای ذخیره ستونهای تشکیلشده بهصورت جداگانه در جدول، ویژگی Address را در کلاس User با @Embedded حاشیهنویسی کنید. قطعه کد زیر این تنظیم را نشان میدهد:
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 مقدار ارائه شده را به ابتدای نام هر ستون در شیء تعبیهشده اضافه میکند.