לפעמים צריך להריץ שאילתה על קבוצה של שלוש טבלאות או יותר שכולן קשורות זו לזו. במקרה כזה, צריך להגדיר יחסי עץ בין הטבלאות.
נניח שבדוגמה של אפליקציית הסטרימינג של המוזיקה, רוצים לשלוח שאילתה על כל המשתמשים, על כל הפלייליסטים של כל משתמש ועל כל השירים בכל פלייליסט של כל משתמש. למשתמשים יש יחס אחד-לרבים עם פלייליסטים, ולפלייליסטים יש יחס רבים-לרבים עם שירים. בדוגמת הקוד הבאה מוצגים הכיתות שמייצגות את הישויות האלה, וגם טבלת ההפניות ההדדיות לקשר ה'רבים לרבים' בין פלייליסטים לשירים:
Kotlin
@Entity
data class User(
@PrimaryKey val userId: Long,
val name: String,
val age: Int
)
@Entity
data class Playlist(
@PrimaryKey val playlistId: Long,
val userCreatorId: 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 User {
@PrimaryKey public long userId;
public String name;
public int age;
}
@Entity
public class Playlist {
@PrimaryKey public long playlistId;
public long userCreatorId;
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;
}
קודם כול, יוצרים מודל של הקשר בין שתי הטבלאות בקבוצה, כמו שמקובל לעשות, באמצעות סיווג נתונים והערה מסוג @Relation
. בדוגמה הבאה מוצגת כיתה PlaylistWithSongs
שמציגה מודל של יחס 'רבים לרבים' בין כיתה הישות Playlist
לכיתה הישות Song
:
Kotlin
data class PlaylistWithSongs(
@Embedded val playlist: Playlist,
@Relation(
parentColumn = "playlistId",
entityColumn = "songId",
associateBy = Junction(PlaylistSongCrossRef::class)
)
val songs: List<Song>
)
Java
public class PlaylistWithSongs {
@Embedded public Playlist playlist;
@Relation(
parentColumn = "playlistId",
entityColumn = "songId",
associateBy = Junction(PlaylistSongCrossRef.class)
)
public List<Song> songs;
}
אחרי שמגדירים סיווג נתונים שמייצג את הקשר הזה, יוצרים סיווג נתונים נוסף שמתאר את הקשר בין טבלה אחרת מהקבוצה לבין סיווג הקשר הראשון, 'מוטמעים' את הקשר הקיים בתוך הקשר החדש. בדוגמה הבאה מוצגת כיתה UserWithPlaylistsAndSongs
שמציגה מודל של קשר אחד-ל-רבים בין כיתה הישות User
לבין כיתה הקשר PlaylistWithSongs
:
Kotlin
data class UserWithPlaylistsAndSongs(
@Embedded val user: User
@Relation(
entity = Playlist::class,
parentColumn = "userId",
entityColumn = "userCreatorId"
)
val playlists: List<PlaylistWithSongs>
)
Java
public class UserWithPlaylistsAndSongs {
@Embedded public User user;
@Relation(
entity = Playlist.class,
parentColumn = "userId",
entityColumn = "userCreatorId"
)
public List<PlaylistWithSongs> playlists;
}
הכיתה UserWithPlaylistsAndSongs
מייצגת באופן עקיף את היחסים בין שלוש כיתות הישויות: User
, Playlist
ו-Song
. איור 1 ממחיש את זה.
אם יש עוד טבלאות בקבוצה, יוצרים כיתה כדי ליצור מודל של הקשר בין כל טבלה שנותרה לבין כיתה היחסים שמציגה את היחסים בין כל הטבלאות הקודמות. כך נוצרת שרשרת של יחסי עץ בין כל הטבלאות שרוצים להריץ עליהן שאילתה.
לבסוף, מוסיפים שיטה לכיתה DAO כדי לחשוף את פונקציית השאילתה שנדרשת לאפליקציה. כדי לבצע את השיטה הזו, Room צריך להריץ כמה שאילתות, לכן צריך להוסיף את ההערה @Transaction
כדי שהפעולה כולה תתבצע באופן אטומי:
Kotlin
@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>
Java
@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();