Menentukan dan membuat kueri hubungan many-to-many

Hubungan many-to-many antara dua entity adalah hubungan saat setiap instance parent entity tidak berkaitan sama sekali atau berkaitan dengan beberapa instance entity turunan, dan hal sebaliknya juga berlaku.

Dalam contoh aplikasi streaming musik, pertimbangkan lagu dalam playlist yang ditentukan pengguna. Setiap playlist dapat berisi banyak lagu, dan setiap lagu dapat menjadi bagian dari banyak playlist. Oleh karena itu, ada hubungan many-to-many antara entity Playlist dan Song.

Ikuti langkah-langkah berikut untuk menentukan dan membuat kueri hubungan many-to-many di database Anda:

  1. Menentukan hubungan: Buat entity dan entity asosiatif, atau tabel referensi silang, untuk merepresentasikan hubungan many-to-many.
  2. Membuat kueri entity: Tentukan cara Anda ingin membuat kueri entity terkait, dan buat class data untuk merepresentasikan output yang diinginkan.

Menentukan hubungan

Untuk menentukan hubungan many-to-many, buat class untuk kedua entity Anda terlebih dahulu. Hubungan many-to-many berbeda dengan jenis hubungan lainnya, karena secara umum tidak ada referensi ke parent entity dalam entity turunan. Sebaliknya, buat class ketiga untuk merepresentasikan entity asosiatif, atau tabel referensi silang, antara dua entity. Tabel referensi silang harus memiliki kolom untuk kunci utama dari setiap entity dalam hubungan many-to-many yang direpresentasikan dalam tabel. Dalam contoh ini, setiap baris dalam tabel referensi silang berkaitan dengan pasangan instance Playlist dan instance Song tempat playlist yang dirujuk menyertakan lagu yang dirujuk.

@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
)

Membuat kueri entity

Langkah berikutnya bergantung pada cara Anda ingin membuat kueri entity terkait ini.

  • Jika Anda ingin membuat kueri playlist dan daftar lagu terkait untuk setiap playlist, buat class data baru yang berisi satu objek Playlist dan daftar objek Song yang disertakan dalam playlist.
  • Jika Anda ingin membuat kueri lagu dan daftar playlist terkait untuk setiap lagu, buat class data baru yang berisi satu objek Song dan daftar objek Playlist yang menyertakan lagu.

Dalam kedua kasus tersebut, buat model hubungan antara entity dengan menggunakan properti associateBy dalam anotasi @Relation di setiap class ini untuk mengidentifikasi entity referensi silang yang menyediakan hubungan antara entity Playlist dan entity 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>
)

Terakhir, tambahkan fungsi ke class objek akses data (DAO) untuk menampilkan fungsi kueri yang diperlukan oleh aplikasi Anda.

getPlaylistsWithSongs
Membuat kueri database dan menampilkan semua objek PlaylistWithSongs yang dihasilkan.
getSongsWithPlaylists
Membuat kueri database dan menampilkan semua objek SongWithPlaylists yang dihasilkan.

Setiap fungsi memerlukan Room untuk menjalankan dua kueri. Tambahkan @Transaction anotasi ke kedua fungsi untuk memastikan operasi berjalan secara atomik.

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

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

Kunci komposit

Jika Anda menentukan hubungan menggunakan kunci komposit, tentukan beberapa kolom di parentColumns dan entityColumns dari anotasi @Relation.

Jika Anda perlu menentukan kolom di Junction, gunakan parentColumns dan entityColumns dalam anotasi Junction juga.

Dalam contoh berikut, Playlist memiliki kunci utama komposit yang terdiri dari playlistId dan creatorId. Tabel referensi silang PlaylistSongCrossRef juga menyertakan kolom ini untuk mereferensikan playlist.

@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>
)