Menentukan dan membuat kueri hubungan bertingkat

Terkadang, Anda mungkin perlu membuat kueri kumpulan tiga tabel atau lebih yang semuanya saling berkaitan. Dalam hal ini, Anda menentukan hubungan bertingkat antara tabel.

Misalkan dalam contoh aplikasi streaming musik, Anda ingin membuat kueri semua pengguna, semua playlist untuk setiap pengguna, dan semua lagu di setiap playlist untuk setiap pengguna. Pengguna memiliki hubungan one-to-many dengan playlist, dan playlist memiliki hubungan many-to-many dengan lagu. Contoh kode berikut menunjukkan class yang mewakili entity ini serta tabel referensi silang untuk hubungan many-to-many antara playlist dan lagu:

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;
}

Pertama, buat model hubungan antara dua tabel dalam kumpulan seperti yang biasa Anda lakukan, menggunakan class data dan anotasi @Relation. Contoh berikut menunjukkan class PlaylistWithSongs yang memodelkan hubungan many-to-many antara class entity Playlist dan class entity 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;
}

Setelah Anda menentukan class data yang merepresentasikan hubungan ini, buat class data lain yang memodelkan hubungan antara tabel lain dari kumpulan dan class hubungan pertama, "menyarangkan" hubungan yang ada ke dalam hubungan yang baru. Contoh berikut menunjukkan class UserWithPlaylistsAndSongs yang memodelkan hubungan one-to-many di antara class entity User dan class hubungan 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;
}

Class UserWithPlaylistsAndSongs secara tidak langsung memodelkan hubungan antara ketiga class entity: User, Playlist, dan Song. Hal ini diilustrasikan dalam gambar 1.

UserWithPlaylistsAndSongs memodelkan hubungan antara User dan
  PlaylistWithSongs, yang kemudian memodelkan hubungan antara Playlist
  dan Song.
Gambar 1. Diagram hubungan class dalam contoh aplikasi streaming musik.

Jika ada tabel lainnya di set Anda, buat class untuk memodelkan hubungan antara setiap tabel yang tersisa dan class hubungan yang memodelkan hubungan antara semua tabel sebelumnya. Hal ini akan membuat rantai hubungan bertingkat di antara semua tabel yang ingin Anda kueri.

Terakhir, tambahkan metode ke class DAO untuk menampilkan fungsi kueri yang diperlukan aplikasi Anda. Metode ini memerlukan Room untuk menjalankan beberapa kueri, jadi tambahkan anotasi @Transaction sehingga seluruh operasi dijalankan secara atomik:

Kotlin

@Transaction
@Query("SELECT * FROM User")
fun getUsersWithPlaylistsAndSongs(): List<UserWithPlaylistsAndSongs>

Java

@Transaction
@Query("SELECT * FROM User")
public List<UserWithPlaylistsAndSongs> getUsersWithPlaylistsAndSongs();