تحديد العلاقات بين عناصر متعددة وطلبات البحث عنها

علاقة أطراف بأطراف بين كيانين هي علاقة يرتبط فيها كل مثيل من الكيان الرئيسي بصفر أو أكثر من مثيلات الكيان التابع، والعكس صحيح أيضًا.

في مثال تطبيق بث الموسيقى، ضع في اعتبارك الأغاني في قوائم التشغيل التي يحدّدها المستخدم. يمكن أن تتضمّن كل قائمة تشغيل العديد من الأغاني، ويمكن أن تنتمي كل أغنية إلى العديد من قوائم التشغيل. لذلك، هناك علاقة أطراف بأطراف بين الكيانات 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>
)