Memilih jenis hubungan antar-objek

Karena SQLite merupakan database relasional, Anda dapat menentukan hubungan antara entity. Namun, 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.

Jenis hubungan

Room mendukung jenis hubungan berikut:

  • One-to-one: Mewakili hubungan saat satu entity terkait dengan satu entity lainnya.
  • One-to-many: Mewakili hubungan saat satu entity dapat terkait dengan beberapa entity dari jenis lain.
  • Many-to-many: Mewakili hubungan saat beberapa entity dari satu jenis dapat terkait dengan beberapa entity dari jenis lain. Hal ini biasanya memerlukan tabel gabungan.
  • Hubungan bertingkat menggunakan objek tersemat: Mewakili hubungan saat satu entity berisi entity lain sebagai properti, dan entity bertingkat ini dapat berisi entity lain lebih lanjut. Hal ini menggunakan anotasi @Embedded.

Memilih antara dua pendekatan

Di Room, ada dua cara untuk menentukan dan membuat kueri hubungan antar-entity. Anda dapat menggunakan:

  • Class data perantara dengan objek tersemat, atau
  • Fungsi kueri relasional dengan jenis nilai yang ditampilkan multimap.

Jika Anda tidak memiliki alasan khusus untuk menggunakan class data perantara, sebaiknya gunakan pendekatan jenis nilai yang ditampilkan multimap. Untuk mempelajari pendekatan ini lebih lanjut, lihat Menampilkan multimap.

Pendekatan class data perantara memungkinkan Anda untuk tidak menulis kueri SQL yang kompleks, tetapi juga dapat mengakibatkan peningkatan kerumitan kode karena memerlukan class data tambahan. Singkatnya, pendekatan jenis nilai yang ditampilkan multimap memerlukan kueri SQL Anda untuk melakukan lebih banyak pekerjaan, dan pendekatan class data perantara memerlukan kode Anda untuk melakukan lebih banyak pekerjaan.

Menggunakan pendekatan class data perantara

Dalam pendekatan class data perantara, Anda menentukan class data yang memodelkan hubungan antara entity Room. Class data ini menyimpan penyambungan antara instance satu entity dan instance entity lain sebagai objek tersemat. Fungsi kueri Anda kemudian dapat menampilkan instance class data ini untuk digunakan dalam aplikasi.

Misalnya, Anda dapat menentukan class data UserBook untuk mewakili pengguna library dengan buku tertentu yang diperiksa, dan menentukan fungsi kueri untuk mengambil daftar instance UserBook dari database:

@Dao
interface UserBookDao {
    @Query(
        """
        SELECT user.name AS userName, book.name AS bookName
        FROM user JOIN book ON user.id = book.user_id
        """
    )
    fun loadUserAndBookNames(): LiveData<List<UserBook>>
}

data class UserBook(val userName: String, val bookName: String)

Menggunakan pendekatan jenis nilai yang ditampilkan multimap

Dalam pendekatan jenis nilai yang ditampilkan multimap, Anda tidak perlu menentukan class data tambahan apa pun. Sebagai gantinya, Anda harus menentukan jenis nilai yang ditampilkan multimap untuk fungsi Anda berdasarkan struktur peta yang diinginkan, dan tentukan hubungan antara entity Anda secara langsung dalam kueri SQL.

Misalnya, fungsi kueri berikut menampilkan pemetaan instance User dan Book untuk mewakili pengguna library dengan buku tertentu yang diperiksa:

@Query(
    """
    SELECT *
    FROM user JOIN book ON user.id = book.user_id
    """
)
suspend fun loadUserAndBookNames(): Map<User, List<Book>>

Dengan jenis nilai yang ditampilkan multimap, Anda juga dapat membuat kueri hubungan one-to-one yang tidak melibatkan entity lain. Fungsi kueri berikut menampilkan pemetaan User dan jumlah buku yang telah mereka periksa menggunakan @MapColumn anotasi:

@Query(
    """
    SELECT user.*, COUNT(book.id) AS book_count
    FROM user LEFT JOIN book ON user.id = book.user_id
    GROUP BY user.id
    """
)
suspend fun loadUserAndBookCount(): Map<User, @MapColumn(columnName = "book_count") Int>

Membuat objek yang disematkan

Terkadang, Anda perlu menyatakan entity atau objek data sebagai satu kesatuan yang kohesif dalam logika database meskipun objek berisi beberapa properti. Dalam situasi ini, gunakan @Embedded anotasi untuk menguraikan objek ke subpropertinya dalam tabel. Kemudian, Anda dapat membuat kueri properti tersemat seperti yang Anda lakukan untuk kolom lainnya.

Misalnya, class User dapat menyertakan properti Address yang merepresentasikan susunan properti street, city, state, dan postCode. Untuk menyimpan kolom yang disusun secara terpisah dalam tabel, anotasi properti Address di class User dengan @Embedded. Cuplikan kode berikut menunjukkan penyiapan ini:

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

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 properti tersemat dari jenis yang sama, Anda dapat menjaga setiap kolom tetap unik dengan menetapkan properti prefix. Room kemudian menambahkan nilai yang diberikan ke awal setiap nama kolom dalam objek yang tersemat.