
Room adalah lapisan abstraksi di atas SQLite yang menyederhanakan penyimpanan. Jika Anda baru ke Kamar, lihat artikel pengantar ini:
7 langkah untuk menggunakan Kamar. Panduan untuk memigrasi aplikasi Anda ke Kamar
Dan dalam artikel ini saya ingin berbagi beberapa tips tentang cara menggunakan Kamar seefisien mungkin.
1. Pra-mengisi basis data
Apakah Anda perlu menambahkan data default ke database Anda segera setelah dibuat atau pada saat akses pertama ke sana? Gunakan RoomDatabase # Callback . Panggil metode addCallback saat membuat database Anda dan timpa onCreate atau onOpen .
onCreate
akan dipanggil ketika database pertama kali dibuat, segera setelah tabel dibuat. onOpen
dipanggil saat database dibuka. Karena akses ke DAO hanya mungkin setelah metode ini selesai, kami membuat aliran baru di mana kami mendapatkan tautan ke basis data, lalu kami mendapatkan DAO dan memasukkan data yang diperlukan.
Room.databaseBuilder(context.applicationContext, DataDatabase::class.java, "Sample.db") // prepopulate the database after onCreate was called .addCallback(object : Callback() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db)
Lihat contoh lengkapnya di sini .
Catatan: jika Anda menggunakan pendekatan ioThread
dan aplikasi mogok saat pertama kali Anda mulai, antara membuat database dan menyisipkan, data tidak akan pernah dimasukkan.
2. Menggunakan Kemampuan Warisan DAO
Apakah Anda memiliki beberapa tabel dalam database Anda dan menyalin metode yang sama memasukkan , memperbarui dan menghapus ? DAOs mendukung warisan, jadi buat kelas BaseDao<T>
dan tentukan metode umum Anda @Insert
, @Update
dan @Delete
. Biarkan setiap DAO memperluas BaseDao
dan menambahkan metode khusus untuk masing-masing.
interface BaseDao<T> { @Insert fun insert(vararg obj: T) } @Dao abstract class DataDao : BaseDao<Data>() { @Query("SELECT * FROM Data") abstract fun getData(): List<Data> }
Lihat detailnya di sini .
DAO harus berupa antarmuka atau kelas abstrak karena Room menghasilkan implementasinya pada waktu kompilasi, termasuk metode dari BaseDao
.
3. Menjalankan permintaan dalam transaksi tanpa kode boilerplate
Membuat anotasi metode menggunakan @Transaction memastikan bahwa semua operasi database yang Anda lakukan dalam metode ini dijalankan dalam transaksi yang sama. Transaksi tidak akan dieksekusi jika pengecualian terjadi di tubuh metode.
@Dao abstract class UserDao { @Transaction open fun updateData(users: List<User>) { deleteAllUsers() insertAll(users) } @Insert abstract fun insertAll(users: List<User>) @Query("DELETE FROM Users") abstract fun deleteAllUsers() }
Anda mungkin ingin menggunakan anotasi @Transaction
untuk metode @Transaction
yang menggunakan pernyataan select
dalam kasus:
- Ketika hasil permintaan cukup besar. Dengan membuat permintaan dalam satu transaksi, Anda menjamin bahwa jika hasil dari permintaan tidak sesuai dengan satu "bagian" dari kursor, maka itu tidak akan rusak karena perubahan dalam database antara permutasi kursor .
- Ketika hasil permintaan adalah POJO dengan bidang
@Relation
. Setiap bidang adalah permintaan tersendiri, jadi menjalankannya dalam satu transaksi memastikan hasil yang konsisten di antara permintaan.
Metode @Delete
, @Update
dan @Insert
, yang memiliki beberapa parameter, diluncurkan secara otomatis di dalam transaksi.
4. Hanya membaca apa yang Anda butuhkan
Ketika Anda membuat permintaan ke database, apakah Anda menggunakan semua bidang yang Anda dapatkan dalam respons? Jaga jumlah memori yang digunakan oleh aplikasi Anda, dan unduh hanya bidang yang akhirnya akan Anda gunakan. Ini juga akan meningkatkan kecepatan permintaan Anda dengan mengurangi biaya I / O. Kamar akan melakukan pemetaan antara kolom dan objek untuk Anda.
Pertimbangkan objek User
kompleks ini:
@Entity(tableName = "users") data class User(@PrimaryKey val id: String, val userName: String, val firstName: String, val lastName: String, val email: String, val dateOfBirth: Date, val registrationDate: Date)
Pada beberapa layar kita tidak perlu menampilkan semua informasi ini. Jadi, sebagai gantinya, kita bisa membuat objek UserMinimal
yang hanya berisi data yang diperlukan.
data class UserMinimal(val userId: String, val firstName: String, val lastName: String)
Di kelas DAO, kami mendefinisikan kueri dan memilih kolom yang benar dari tabel users
.
@Dao interface UserDao { @Query(“SELECT userId, firstName, lastName FROM Users) fun getUsersMinimal(): List<UserMinimal> }
5. Kontrol ketergantungan antara entitas dengan kunci asing
Meskipun Room tidak secara langsung mendukung hubungan antar entitas, Kamar memungkinkan Anda untuk menentukan dependensi antara objek menggunakan kunci asing.
Kamar memiliki anotasi @ForeignKey , yang merupakan bagian dari anotasi @Entity. Secara fungsional, ini mirip dengan kunci asing di SQLite . Ini menjamin pelestarian hubungan antara entitas ketika perubahan dilakukan ke database. Untuk menambahkannya, tentukan objek yang akan direferensikan, serta kolom dalam objek saat ini dan volume yang Anda maksud.
Pertimbangkan User
dan Pet
kelas. Pet
memiliki pemilik - ID pengguna yang dirujuk oleh kunci asing.
@Entity(tableName = "pets", foreignKeys = arrayOf( ForeignKey(entity = User::class, parentColumns = arrayOf("userId"), childColumns = arrayOf("owner")))) data class Pet(@PrimaryKey val petId: String, val name: String, val owner: String)
Jika mau, Anda dapat menentukan tindakan apa yang harus diambil ketika orang tua dihapus atau diperbarui dalam database. Anda dapat memilih salah satu opsi berikut: NO_ACTION
, RESTRICT
, SET_NULL
, SET_DEFAULT
atau CASCADE
, yang berperilaku sama seperti di SQLite .
Catatan: di Kamar, SET_DEFAULT
berfungsi seperti SET_NULL
, karena Kamar belum memungkinkan Anda untuk menetapkan nilai default untuk kolom.
6. Sederhanakan pertanyaan satu-ke-banyak dengan @Relation
Dalam User
- Pet
contoh sebelumnya, kita dapat mengatakan bahwa ada hubungan satu-ke-banyak : pengguna dapat memiliki beberapa hewan peliharaan. Misalkan kita ingin mendapatkan daftar pengguna dengan hewan peliharaan mereka: List<UserAndAllPets>
.
data class UserAndAllPets (val user: User, val pets: List<Pet> = ArrayList())
Untuk melakukan ini secara manual, kami membutuhkan 2 pertanyaan: satu untuk mendapatkan daftar semua pengguna dan yang lainnya untuk mendapatkan daftar hewan peliharaan berdasarkan ID pengguna.
@Query(“SELECT * FROM Users”) public List<User> getUsers(); @Query(“SELECT * FROM Pets where owner = :userId”) public List<Pet> getPetsForUser(String userId);
Kemudian kita akan mengulangi daftar pengguna dan mengakses tabel Pets
setiap waktu.
@Relation annotation akan membuat hidup kita lebih mudah: ia akan secara otomatis meminta objek terkait. @Relation
hanya dapat diterapkan ke List
atau Set
. Perbarui kelas UserAndAllPets:
class UserAndAllPets { @Embedded var user: User? = null @Relation(parentColumn = “userId”, entityColumn = “owner”) var pets: List<Pet> = ArrayList() }
Di DAO, kami mendefinisikan satu kueri, dan Kamar akan meminta tabel Users
dan Pets
dan memetakan objek sendiri.
@Transaction @Query(“SELECT * FROM Users”) List<UserAndAllPets> getUsers();
7. Menghindari pemberitahuan palsu untuk permintaan yang dapat diobservasi
Misalkan Anda ingin mendapatkan pengguna dengan pengenalnya menggunakan permintaan yang dapat diamati:
@Query(“SELECT * FROM Users WHERE userId = :id) fun getUserById(id: String): LiveData<User>
Anda akan menerima objek User
baru setiap kali diperbarui. Tetapi Anda juga akan menerima objek ini ketika perubahan lain terjadi di tabel Users
(penghapusan, pembaruan, atau sisipan) yang tidak ada hubungannya dengan pengguna yang Anda minati, yang akan menyebabkan pemberitahuan palsu. Selain itu, jika kueri Anda menyertakan beberapa tabel, Anda akan menerima pesan baru setiap kali ada perubahan pada salah satu dari mereka.
Inilah yang terjadi di balik layar:
- SQLite telah memicu pemicu tersebut setiap kali
DELETE
, UPDATE
atau INSERT
muncul dalam sebuah tabel. - Room membuat InvalidationTracker yang menggunakan
Observers
yang melacak semua perubahan dalam tabel yang diamati. LiveData
dan Flowable
bergantung pada InvalidationTracker.Observer # pada pemberitahuan yang Divalidasi . Ketika diterima, permintaan berulang terjadi.
Kamar hanya tahu bahwa tabel telah diubah, tetapi tidak tahu mengapa dan apa yang telah berubah. Oleh karena itu, setelah permintaan kedua, hasil permintaan dikirimkan menggunakan LiveData
atau Flowable
. Karena Kamar tidak menyimpan data apa pun di memori; Kamar tidak dapat menentukan apakah itu data yang sama atau tidak.
Anda harus memastikan bahwa DAO Anda memfilter permintaan dan hanya menanggapi objek yang diperlukan.
Jika kueri diamati diimplementasikan menggunakan Flowables
, gunakan Flowable # diverUntilChanged .
@Dao abstract class UserDao : BaseDao<User>() { @Query(“SELECT * FROM Users WHERE userid = :id”) protected abstract fun getUserById(id: String): Flowable<User> fun getDistinctUserById(id: String): Flowable<User> = getUserById(id) .distinctUntilChanged() }
Jika kueri Anda mengembalikan LiveData
, Anda dapat menggunakan MediatorLiveData
, yang hanya akan menerima objek yang diinginkan dari sumber.
fun <T> LiveData<T>.getDistinct(): LiveData<T> { val distinctLiveData = MediatorLiveData<T>() distinctLiveData.addSource(this, object : Observer<T> { private var initialized = false private var lastObj: T? = null override fun onChanged(obj: T?) { if (!initialized) { initialized = true lastObj = obj distinctLiveData.postValue(lastObj) } else if ((obj == null && lastObj != null) || obj != lastObj) { lastObj = obj distinctLiveData.postValue(lastObj) } } }) return distinctLiveData }
Di DAO Anda, metode yang mengembalikan LiveData
bersifat public
, dan metode yang meminta basis data protected
.
@Dao abstract class UserDao : BaseDao<User>() { @Query(“SELECT * FROM Users WHERE userid = :id”) protected abstract fun getUserById(id: String): LiveData<User> fun getDistinctUserById(id: String): LiveData<User> = getUserById(id).getDistinct() }
Lihat contoh kode lengkap di sini .
Catatan: jika Anda meminta daftar untuk ditampilkan, perhatikan Paging Library , yang akan mengembalikan LivePagedListBuilder . Perpustakaan akan membantu Anda secara otomatis menghitung perbedaan antara item daftar dan memperbarui antarmuka pengguna Anda.
Lihat juga: 7 langkah untuk menggunakan Kamar. Panduan untuk memigrasi aplikasi Anda ke Kamar