הגדרה של קשרי גומלין 'הרבה לרבים' ושליחת שאילתות לגבי קשרים כאלה

קשר 'הרבה לרבים' בין שתי ישויות הוא קשר שבו כל מופע של הישות ההורה תואם לאפס או יותר מופעים של הישות הצאצא, וההפך נכון גם כן.

בדוגמה של אפליקציית הסטרימינג של המוזיקה, נתייחס לשירים בפלייליסטים שהוגדרו על ידי המשתמש. כל פלייליסט יכול לכלול הרבה שירים, וכל שיר יכול להיכלל בהרבה פלייליסטים שונים. לכן, יש קשר 'הרבה לרבים' בין הישות Playlist לישות Song.

כדי להגדיר יחסי 'רבים לרבים' במסד הנתונים ולבצע שאילתות לגביהן:

  1. הגדרת הקשר: מגדירים את הישויות ואת הישות המשויכת (טבלת הפניה חוזרת) כדי לייצג את הקשר 'הרבה לרבים'.
  2. שליחת שאילתות לישויות: קובעים איך שולחים שאילתות לישויות הקשורות ויוצרים כיתות נתונים שמייצגות את הפלט המיועד.

הגדרת הקשר

כדי להגדיר קשר 'הרבה לרבים', קודם צריך ליצור כיתה לכל אחת משתי הישות. קשרים 'רבים לרבים' שונים מסוגים אחרים של קשרים, כי בדרך כלל אין הפניה לישות ההורה בישות הצאצא. במקום זאת, יוצרים כיתה שלישית שמייצגת ישות אסוסיאיטיבית או טבלת הפניה חוזרת בין שתי הישויות. בטבלת הפניה ההדדית חייבות להיות עמודות למפתח הראשי של כל ישות ביחס ה'רבים לרבים' שמיוצג בטבלה. בדוגמה הזו, כל שורה בטבלת ההפניות ההדדיות תואמת להתאמה של מכונה של Playlist ומכונה של Song, שבה השיר שמצוין בפניה נכלל בפלייליסט שמצוין בפניה.

Kotlin

@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"])
data class PlaylistSongCrossRef(
    val playlistId: Long,
    val songId: Long
)

Java

@Entity
public class Playlist {
    @PrimaryKey public long playlistId;
    public String playlistName;
}

@Entity
public class Song {
    @PrimaryKey public long songId;
    public String songName;
    public String artist;
}

@Entity(primaryKeys = {"playlistId", "songId"})
public class PlaylistSongCrossRef {
    public long playlistId;
    public long songId;
}

שליחת שאילתה לגבי הישויות

השלב הבא תלוי באופן שבו רוצים לשלוח שאילתה לגבי הישות הקשורות האלה.

  • אם רוצים לשלוח שאילתה לגבי פלייליסטים ורשימה של השירים התואמים לכל פלייליסט, צריך ליצור סיווג נתונים חדש שמכיל אובייקט Playlist אחד ורשימה של כל אובייקטי ה-Song שהפלייליסט כולל.
  • אם רוצים לשלוח שאילתה לגבי שירים ולקבל רשימה של הפלייליסטים התואמים לכל אחד מהם, צריך ליצור סיווג נתונים חדש שמכיל אובייקט Song יחיד ורשימה של כל אובייקטי ה-Playlist שבהם השיר נכלל.

בכל מקרה, כדי ליצור מודל של הקשר בין הישויות, משתמשים במאפיין associateBy בהערה @Relation בכל אחת מהכיתות האלה כדי לזהות את ישות ההפניה ההדדית שמספקת את הקשר בין הישות Playlist לישות Song.

Kotlin

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

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

Java

public class PlaylistWithSongs {
    @Embedded public Playlist playlist;
    @Relation(
         parentColumn = "playlistId",
         entityColumn = "songId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Song> songs;
}

public class SongWithPlaylists {
    @Embedded public Song song;
    @Relation(
         parentColumn = "songId",
         entityColumn = "playlistId",
         associateBy = @Junction(PlaylistSongCrossref.class)
    )
    public List<Playlist> playlists;
}

לבסוף, מוסיפים שיטה לכיתה DAO כדי לחשוף את פונקציית השאילתה שנדרשת לאפליקציה.

  • getPlaylistsWithSongs: השיטה שולחת שאילתה למסד הנתונים ומחזירה את כל אובייקטי ה-PlaylistWithSongs שנוצרו.
  • getSongsWithPlaylists: השיטה שולחת שאילתה למסד הנתונים ומחזירה את כל אובייקטי ה-SongWithPlaylists שנוצרו.

כל אחת מהשיטות האלה דורשת מ-Room להריץ שתי שאילתות, לכן מוסיפים את ההערה @Transaction לשתי השיטות כדי שהפעולה כולה תתבצע באופן אטומי.

Kotlin

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

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

Java

@Transaction
@Query("SELECT * FROM Playlist")
public List<PlaylistWithSongs> getPlaylistsWithSongs();

@Transaction
@Query("SELECT * FROM Song")
public List<SongWithPlaylists> getSongsWithPlaylists();