تعریف و پُرسمان روابط چندبه‌چند

رابطه چند به چند بین دو نهاد رابطه‌ای است که در آن هر نمونه از نهاد والد با صفر یا چند نمونه از نهاد فرزند مطابقت دارد و برعکس آن نیز صادق است.

در مثال برنامه جاری‌سازی موسیقی، آهنگ‌های موجود در فهرست‌های پخش تعریف‌شده توسط کاربر را درنظر بگیرید. هر فهرست پخش می‌تواند شامل آهنگ‌های زیادی باشد و هر آهنگ می‌تواند متعلق به فهرست‌های پخش زیادی باشد. بنابراین، بین نهادهای Playlist و Song رابطه چندبه‌چند وجود دارد.

برای تعریف و پُرسمان کردن روابط چندبه‌چند در پایگاه داده‌تان، این مراحل را دنبال کنید:

  1. تعریف رابطه: نهادها و نهاد انجمنی، یا جدول ارجاع متقابل، را برای نشان دادن رابطه چند به چند ایجاد کنید.
  2. پُرسمان کردن نهادها: مشخص کنید چگونه می‌خواهید نهادهای مرتبط را پُرسمان کنید و کلاس‌های داده‌ای برای نشان دادن برونداد موردنظر ایجاد کنید.

تعریف رابطه

برای تعریف رابطه چندبه‌چند، ابتدا برای هریک از دو نهاد خود کلاس ایجاد کنید. روابط چند به چند با انواع دیگر روابط متفاوت است زیرا به‌طورکلی هیچ ارجاعی به نهاد والد در نهاد فرزند وجود ندارد. به‌جای آن، کلاس سومی ایجاد کنید که نشان‌دهنده نهاد انجمنی، یا جدول ارجاع متقابل، بین دو نهاد باشد. جدول مرجع متقابل باید ستون‌هایی برای کلید اصلی از هر موجودیت در رابطه چند به چند داشته باشد که در جدول نشان داده شده است. در این مثال، هر ردیف در جدول مرجع متقابل مربوط به جفت‌سازی یک نمونه Playlist و یک نمونه Song است که فهرست پخش ارجاع‌شده شامل آهنگ ارجاع‌شده است.

@Entity
data class Playlist(
    @PrimaryKey val playlistId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "songId"], indices = [Index("playlistId", "songId")])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

پُرسمان نهادها

مرحله بعدی به نحوه پُرسمان کردن این نهادهای مرتبط بستگی دارد.

  • اگر می‌خواهید فهرست‌های پخش و فهرستی از آهنگ‌های مربوط به هر فهرست پخش را پُرسمان کنید، کلاس داده جدیدی ایجاد کنید که حاوی یک Playlist شیء و فهرستی از اشیای Song باشد که فهرست پخش شامل آن‌ها است.
  • اگر می‌خواهید آهنگ‌ها و فهرستی از فهرست‌های پخش مربوط به هر آهنگ را پُرسمان کنید، کلاس داده جدیدی ایجاد کنید که حاوی یک شیء Song و فهرستی از اشیای Playlist شامل آهنگ باشد.

در هر دو مورد، بااستفاده از دارایی associateBy در @Relation گزارمان در هریک از این کلاس‌ها، رابطه بین نهادها را مدل‌سازی کنید تا نهاد مرجع متقابل ارائه‌دهنده رابطه بین نهاد Playlist و نهاد Song را شناسایی کنید.

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
        parentColumns = ["playlistId"],
        entityColumns = ["songId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)

data class SongWithPlaylists(
    @Embedded val song: Song,
    @Relation(
        parentColumns = ["songId"],
        entityColumns = ["playlistId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val playlists: List<Playlist>
)

درنهایت، تابعی به کلاس شیء دسترسی به داده (DAO) اضافه کنید تا تابع پُرسمان موردنیاز برنامه‌تان را نمایان کند.

getPlaylistsWithSongs
پایگاه داده را پُرسمان می‌کند و همه PlaylistWithSongs شیء حاصل را برمی‌گرداند.
getSongsWithPlaylists
پایگاه داده را پُرسمان می‌کند و همه SongWithPlaylists شیء حاصل را برمی‌گرداند.

هر تابع برای اجرای دو پُرسمان به Room نیاز دارد. برای اطمینان از اینکه عملیات به‌صورت اتمی اجرا می‌شود، @Transaction گزارمان را به هر دو تابع اضافه کنید.

@Transaction
@Query("SELECT * FROM Playlist")
suspend fun getPlaylistsWithSongs(): List<PlaylistWithSongs>

@Transaction
@Query("SELECT * FROM Song")
suspend fun getSongsWithPlaylists(): List<SongWithPlaylists>

کلیدهای ترکیبی

اگر رابطه را بااستفاده از کلیدهای ترکیبی تعریف می‌کنید، چندین ستون را در parentColumns و entityColumns از گزارمان @Relation مشخص کنید.

اگر لازم است ستون‌هایی را در Junction مشخص کنید، از parentColumns و entityColumns در گزارمان Junction نیز استفاده کنید.

در مثال زیر، Playlist کلید اصلی ترکیبی دارد که از playlistId و creatorId تشکیل شده است. جدول ارجاع متقابل PlaylistSongCrossRef همچنین شامل این ستون‌ها برای ارجاع به فهرست پخش است.

@Entity(primaryKeys = ["playlistId", "creatorId"])
data class Playlist(
    val playlistId: Long,
    val creatorId: Long,
    val playlistName: String
)

@Entity
data class Song(
    @PrimaryKey val songId: Long,
    val songName: String,
    val artist: String
)

@Entity(primaryKeys = ["playlistId", "creatorId", "songId"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val creatorId: Long,
    val songId: Long
)

data class PlaylistWithSongs(
    @Embedded val playlist: Playlist,
    @Relation(
        parentColumns = ["playlistId", "creatorId"],
        entityColumns = ["songId"],
        associateBy = Junction(PlaylistSongCrossRef::class)
    )
    val songs: List<Song>
)