Menentukan hubungan antar-objek

Karena SQLite merupakan database relasional, Anda dapat menentukan hubungan antara entity. Meskipun sebagian besar library pemetaan terkait objek memungkinkan objek entity saling mereferensikan satu sama lain, Room secara eksplisit melarang hal ini. Untuk mempelajari alasan teknis di balik keputusan ini, lihat Memahami mengapa Room tidak mengizinkan referensi objek.

Membuat objek yang disematkan

Terkadang, Anda perlu menyatakan entity atau objek data sebagai satu kesatuan yang kohesif dalam logika database meskipun objek berisi beberapa kolom. Dalam situasi ini, Anda dapat menggunakan anotasi @Embedded untuk merepresentasikan objek yang ingin didekompresi ke subkolomnya dalam tabel. Anda kemudian dapat meminta kolom tersemat tersebut seperti saat meminta kolom individual lainnya.

Misalnya, class User dapat menyertakan kolom dari jenis Address, yang merepresentasikan susunan kolom bernama street, city, state, dan postCode. Untuk menyimpan kolom tersusun secara terpisah dalam tabel, sertakan kolom Address di class User yang dianotasikan dengan @Embedded, seperti yang ditunjukan dalam cuplikan kode berikut:

Kotlin

    data class Address(
        val street: String?,
        val state: String?,
        val city: String?,
        @ColumnInfo(name = "post_code") val postCode: Int
    )

    @Entity
    data class User(
        @PrimaryKey val id: Int,
        val firstName: String?,
        @Embedded val address: Address?
    )
    

Java

    public class Address {
        public String street;
        public String state;
        public String city;

        @ColumnInfo(name = "post_code") public int postCode;
    }

    @Entity
    public class User {
        @PrimaryKey public int id;

        public String firstName;

        @Embedded public Address address;
    }
    

Tabel yang merepresentasikan objek User kemudian akan berisi kolom dengan nama berikut: id, firstName, street, state, city, dan post_code.

Jika suatu entity memiliki beberapa kolom tersemat yang jenisnya sama, Anda dapat mengatur setiap kolom agar tetap unik dengan menetapkan properti prefix. Room kemudian menambahkan nilai yang diberikan ke awal setiap nama kolom dalam objek yang tersemat.

Menentukan hubungan one-to-one

Hubungan one-to-one antara dua entity adalah hubungan di mana setiap instance entity induk berkaitan dengan tepat satu instance entity turunan, dan sebaliknya.

Misalnya, pertimbangkan aplikasi streaming musik tempat pengguna memiliki koleksi lagu yang mereka miliki. Setiap pengguna hanya memiliki satu library, dan setiap library berkaitan dengan tepat satu pengguna. Oleh karena itu, harus ada hubungan one-to-one antara entity User dan entity Library.

Pertama, buat class untuk dua entitas Anda. Salah satu entitas harus menyertakan variabel yang merupakan referensi ke kunci utama entitas lainnya.

Kotlin

    @Entity
    data class User(
        @PrimaryKey val userId: Long,
        val name: String,
        val age: Int
    )

    @Entity
    data class Library(
        @PrimaryKey val libraryId: Long,
        val userOwnerId: Long
    )
    

Java

    @Entity
    public class User {
        @PrimaryKey public long userId;
        public String name;
        public int age;
    }

    @Entity
    public class Library {
        @PrimaryKey public long libraryId;
        public long userOwnerId;
    }
    

Untuk mengkueri daftar pengguna dan library terkait, Anda harus terlebih dahulu memodelkan hubungan one-to-one antara dua entity. Untuk melakukannya, buat class data baru di mana setiap instance menyimpan instance entity induk dan instance entity turunan yang sesuai. Tambahkan anotasi @Relation ke instance entity turunan, dengan parentColumn ditetapkan ke nama kolom kunci utama dari entity induk dan entityColumn ditetapkan ke nama kolom entity turunan yang mereferensikan kunci utama entity induk.

Kotlin

    data class UserAndLibrary(
        @Embedded val user: User,
        @Relation(
             parentColumn = "userId",
             entityColumn = "userOwnerId"
        )
        val library: Library
    )
    

Java

    public class UserAndLibrary {
        @Embedded public User user;
        @Relation(
             parentColumn = "userId",
             entityColumn = "userOwnerId"
        )
        public Library library;
    }
    

Terakhir, tambahkan metode ke class DAO yang menampilkan semua instance class data yang menghubungkan entity induk dan entity turunan. Metode ini memerlukan Room untuk menjalankan dua kueri, jadi tambahkan anotasi @Transaction ke metode ini untuk memastikan seluruh operasi dijalankan secara atomik.

Kotlin

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersAndLibraries(): List<UserAndLibrary>
    

Java

    @Transaction
    @Query("SELECT * FROM User")
    public List<UserAndLibrary> getUsersAndLibraries();
    

Menentukan hubungan one-to-many

Hubungan one-to-many antara dua entity adalah hubungan di mana setiap entity induk berkaitan dengan nol atau lebih instance entity turunan lainnya, tetapi setiap instance entity turunan hanya berkaitan dengan tepat satu instance entity induk.

Dalam contoh aplikasi streaming musik, misalnya pengguna memiliki kemampuan untuk mengatur lagu mereka ke dalam playlist. Setiap pengguna dapat membuat playlist sebanyak yang diinginkan, tetapi setiap playlist dibuat oleh satu pengguna saja. Oleh karena itu, harus ada hubungan one-to-many antara entity User dan entity Playlist.

Pertama, buat class untuk dua entity Anda. Seperti pada contoh sebelumnya, entity turunan harus menyertakan variabel yang merupakan referensi ke kunci utama dari entity induk.

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
    )
    

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

Untuk mengkueri daftar pengguna dan playlist terkait, Anda harus terlebih dahulu memodelkan hubungan one-to-many di antara dua entitas. Untuk melakukannya, buat class data baru di mana setiap instance memiliki instance entitas induk dan daftar semua instance entitas turunan yang sesuai. Tambahkan anotasi @Relation ke instance entitas turunan, dengan parentColumn ditetapkan ke nama kolom kunci utama dari entitas induk dan entityColumn ditetapkan ke nama kolom entitas turunan yang mereferensikan kunci utama entitas induk.

Kotlin

    data class UserWithPlaylists(
        @Embedded val user: User,
        @Relation(
              parentColumn = "userId",
              entityColumn = "userCreatorId"
        )
        val playlists: List<Playlist>
    )
    

Java

    public class UserWithPlaylists {
        @Embedded public User user;
        @Relation(
             parentColumn = "userId",
             entityColumn = "userCreatorId"
        )
        public List<Playlist> playlists;
    }
    

Terakhir, tambahkan metode ke class DAO yang menampilkan semua instance class data yang menghubungkan entity induk dan entity turunan. Metode ini memerlukan Room untuk menjalankan dua kueri, jadi tambahkan anotasi @Transaction ke metode ini untuk memastikan seluruh operasi dijalankan secara atomik.

Kotlin

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersWithPlaylists(): List<UserWithPlaylists>
    

Java

    @Transaction
    @Query("SELECT * FROM User")
    public List<UserWithPlaylists> getUsersWithPlaylists();
    

Menentukan hubungan many-to-many

Hubungan many-to-many antara dua entity adalah hubungan di mana setiap entity induk berkaitan dengan nol atau lebih instance entity turunan lainnya, dan sebaliknya.

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

Pertama, buat class untuk dua entity Anda. Hubungan many-to-many berbeda dengan jenis hubungan lainnya, karena secara umum tidak ada referensi ke entity induk 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 lagu yang dirujuk disertakan dalam playlist yang direferensikan.

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

Langkah berikutnya bergantung pada bagaimana Anda ingin mengkueri entity terkait ini.

  • Jika Anda ingin mengkueri playlist dan daftar lagu terkait untuk setiap playlist, buat class data baru yang berisi satu objek Playlist dan daftar semua objek Song yang disertakan dalam playlist.
  • Jika Anda ingin mengkueri lagu dan daftar playlist yang sesuai untuk setiap lagu, buat class data baru yang berisi satu objek Song dan daftar semua objek Playlist tempat lagu disertakan.

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.

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

Terakhir, tambahkan metode ke class DAO untuk menampilkan fungsi kueri yang diperlukan aplikasi Anda.

  • getPlaylistsWithSongs: Metode ini mengkueri database dan menampilkan semua objek PlaylistWithSongs yang dihasilkan.
  • getSongsWithPlaylists: Metode ini mengkueri database dan menampilkan semua objek SongWithPlaylists yang dihasilkan.

Masing-masing metode ini memerlukan Room untuk menjalankan dua kueri, jadi tambahkan anotasi @Transaction ke kedua metode tersebut untuk memastikan bahwa seluruh operasi dilakukan secara atomik.

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();
    

Menentukan hubungan bertingkat

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

Misalkan dalam contoh aplikasi streaming musik, Anda ingin mengkueri 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, dengan 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, "sarangkan" 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 kumpulan Anda, buat class untuk memodelkan hubungan antara setiap tabel yang tersisa dan class hubungan yang memodelkan hubungan antara semua tabel sebelumnya. Ini akan membuat rantai hubungan bertingkat di antara semua tabel yang ingin Anda kueri.

Terakhir, tambahkan metode ke class DAO untuk menampilkan fungsionalitas kueri yang diperlukan oleh aplikasi Anda. Metode ini memerlukan Room untuk menjalankan beberapa kueri, jadi tambahkan anotasi @Transaction untuk memastikan 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();
    

Referensi Tambahan

Untuk mempelajari lebih lanjut cara menentukan hubungan antara entity di Room, lihat referensi tambahan berikut.

Contoh

Video

Blog