Mereferensikan data kompleks menggunakan Room

Room menyediakan fungsionalitas untuk melakukan konversi antara jenis klasik dan jenis yang dapat ditetapkan pada objek, tetapi tidak memungkinkan referensi objek antarentitas. Dokumen ini menjelaskan cara menggunakan konverter jenis dan alasan mengapa Room tidak mendukung referensi objek.

Menggunakan konverter jenis

Terkadang, aplikasi Anda perlu menggunakan jenis data kustom yang nilainya ingin Anda simpan dalam satu kolom database. Untuk menambahkan dukungan seperti ini ke jenis kustom, Anda perlu memberikan TypeConverter, yang akan mengonversi class kustom ke dan dari jenis yang diketahui dan dapat dipertahankan oleh Room.

Contohnya, jika ingin mempertahankan instanceDate, kita dapat menulis TypeConverter berikut untuk menyimpan stempel waktu Unix yang setara dalam database:

Kotlin

    class Converters {
        @TypeConverter
        fun fromTimestamp(value: Long?): Date? {
            return value?.let { Date(it) }
        }

        @TypeConverter
        fun dateToTimestamp(date: Date?): Long? {
            return date?.time?.toLong()
        }
    }
    

Java

    public class Converters {
        @TypeConverter
        public static Date fromTimestamp(Long value) {
            return value == null ? null : new Date(value);
        }

        @TypeConverter
        public static Long dateToTimestamp(Date date) {
            return date == null ? null : date.getTime();
        }
    }
    

Contoh sebelumnya menentukan 2 fungsi, fungsi yang mengonversi objek Date menjadi Long dan satu fungsi lain yang melakukan sebaliknya, dari Long menjadi Date. Karena sudah mengetahui cara mempertahankan objek Long, Room dapat menggunakan konverter ini untuk mempertahankan nilai dari jenis Date.

Selanjut, tambahkan anotasi @TypeConverters ke class AppDatabase agar Room dapat menggunakan konverter yang sudah Anda tentukan untuk setiap entitas dan DAO dalam AppDatabase tersebut:

AppDatabase

Kotlin

    @Database(entities = arrayOf(User::class), version = 1)
    @TypeConverters(Converters::class)
    abstract class AppDatabase : RoomDatabase() {
        abstract fun userDao(): UserDao
    }
    

Java

    @Database(entities = {User.class}, version = 1)
    @TypeConverters({Converters.class})
    public abstract class AppDatabase extends RoomDatabase {
        public abstract UserDao userDao();
    }
    

Dengan konverter ini, Anda kemudian dapat menggunakan jenis kustom dalam kueri lainnya, sebagaimana Anda menggunakan jenis klasik, seperti dalam cuplikan kode berikut:

User

Kotlin

    @Entity
    data class User(private val birthday: Date?)
    

Java

    @Entity
    public class User {
        private Date birthday;
    }
    

UserDao

Kotlin

    @Dao
    interface UserDao {
        @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
        fun findUsersBornBetweenDates(from: Date, to: Date): List<User>
    }
    

Java

    @Dao
    public interface UserDao {
        @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
        List<User> findUsersBornBetweenDates(Date from, Date to);
    }
    

Anda juga dapat membatasi @TypeConverters pada beberapa cakupan, termasuk entitas individual, DAO, dan metode DAO. Untuk detail selengkapnya, lihat dokumentasi referensi untuk anotasi @TypeConverters.

Memahami mengapa Room tidak mengizinkan referensi objek

Poin utama: Room tidak mengizinkan referensi objek antar-class entitas. Sebagai gantinya, Anda harus secara eksplisit meminta data yang dibutuhkan oleh aplikasi Anda.

Memetakan hubungan dari database ke masing-masing model objek adalah praktik yang umum dilakukan dan bekerja dengan sangat baik di sisi server. Bahkan, saat program memuat kolom ketika sedang diakses, performa server akan tetap baik.

Namun, di sisi klien, jenis pemuatan lambat ini akan menyulitkan karena biasanya akan terjadi di UI thread, dan meminta informasi tentang disk di UI thread akan menimbulkan masalah performa yang signifikan. UI thread biasanya membutuhkan waktu sekitar 16 milidetik untuk menghitung dan menggambar tata letak aktivitas yang diperbarui. Jadi, meskipun kueri hanya memerlukan waktu 5 milidetik, aplikasi Anda kemungkinan akan kehabisan waktu untuk menggambar frame sehingga gangguan visual yang cukup terlihat akan muncul. Kueri dapat memerlukan lebih banyak waktu untuk diselesaikan jika ada transaksi terpisah yang berjalan secara paralel, atau jika perangkat sedang menjalankan tugas lain yang membutuhkan banyak disk. Namun, jika Anda tidak menggunakan pemuatan lambat, aplikasi akan mengambil lebih banyak data daripada yang dibutuhkan sehingga akan timbul masalah pada penggunaan memori.

Pemetaan terkait objek biasanya menyerahkan keputusan ini kepada developer agar mereka dapat melakukan yang terbaik sesuai kasus penggunaan aplikasinya. Developer biasanya memutuskan untuk menggunakan model yang sama antara aplikasi mereka dan UI-nya. Namun, solusi ini kurang sesuai karena seiring dengan berubahnya UI dari waktu ke waktu, model bersama akan menimbulkan masalah yang sulit diantisipasi dan di-debug oleh developer.

Misalnya, terdapat UI yang memuat daftar objek Book, yang masing-masing memiliki objek Author. Awalnya, Anda mungkin mendesain kueri untuk menggunakan pemuatan lambat agar instance Book mengambil penulisnya. Pengambilan pertama kolom author akan membuat kueri pada database. Beberapa waktu kemudian, Anda menyadari bahwa nama penulis juga perlu ditampilkan dalam UI aplikasi. Anda dapat mengakses nama ini dengan mudah, seperti dalam cuplikan kode berikut:

Kotlin

    authorNameTextView.text = book.author.name
    

Java

    authorNameTextView.setText(book.getAuthor().getName());
    

Namun, perubahan yang tampaknya tidak berbahaya ini menyebabkan tabel Author ditanyakan pada thread utama.

Jika Anda meminta informasi penulis lebih awal, akan sulit untuk mengubah cara data dimuat ketika Anda tidak lagi membutuhkannya. Misalnya, jika UI aplikasi Anda tidak perlu lagi menampilkan informasi Author, aplikasi Anda akan secara efektif memuat data yang tidak lagi ditampilkan, dan menghabiskan ruang memori yang berharga. Efisiensi aplikasi Anda akan terus turun jika class Author merujuk ke tabel lain, seperti Books.

Untuk mereferensikan beberapa entitas sekaligus menggunakan Room, Anda dapat membuat POJO yang berisi setiap entitas, lalu menulis kueri yang menggabungkan tabel terkait. Model yang terstruktur dengan baik ini, bersama kemampuan validasi kueri Room yang canggih, memungkinkan aplikasi Anda menggunakan lebih sedikit resource saat memuat data sehingga performa aplikasi dan pengalaman pengguna pun akan meningkat.