Mengakses data menggunakan DAO Room

Selama menggunakan library persistensi Room untuk menyimpan data aplikasi, Anda berinteraksi dengan data yang disimpan dengan menentukan objek akses data, atau DAO. Setiap DAO menyertakan metode yang menawarkan akses abstrak ke database aplikasi Anda. Pada waktu kompilasi, Room otomatis akan membuat implementasi DAO yang Anda tentukan.

Dengan menggunakan DAO untuk mengakses database aplikasi, bukan builder kueri atau kueri langsung, Anda dapat mempertahankan pemisahan fokus yang merupakan prinsip penting dalam arsitektur. DAO juga memudahkan Anda meniru akses database saat menguji aplikasi.

Anatomi DAO

Anda dapat menentukan setiap DAO sebagai antarmuka atau class abstrak. Untuk kasus penggunaan dasar, Anda biasanya harus menggunakan antarmuka. Dalam kedua kasus tersebut, Anda harus selalu menganotasi DAO dengan @Dao. DAO tidak memiliki properti, tetapi menentukan satu atau beberapa metode untuk berinteraksi dengan data di database aplikasi Anda.

Kode berikut adalah contoh DAO sederhana yang menentukan metode untuk menyisipkan, menghapus, dan memilih objek User dalam database Room:

Kotlin

@Dao
interface UserDao {
    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)

    @Query("SELECT * FROM user")
    fun getAll(): List<User>
}

Java

@Dao
public interface UserDao {
    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);

    @Query("SELECT * FROM user")
    List<User> getAll();
}

Ada dua jenis metode DAO yang menentukan interaksi database:

  • Metode praktis yang memungkinkan Anda menyisipkan, memperbarui, dan menghapus baris di database tanpa harus menulis kode SQL.
  • Metode kueri yang memungkinkan untuk menulis kueri SQL Anda sendiri untuk berinteraksi dengan database.

Bagian berikut menunjukkan cara menggunakan kedua jenis metode DAO untuk menentukan interaksi database yang diperlukan aplikasi.

Metode praktis

Room menyediakan anotasi praktis untuk menentukan metode yang menjalankan penyisipan, pembaruan, dan penghapusan sederhana tanpa mengharuskan Anda menulis pernyataan SQL.

Jika Anda harus menentukan penyisipan, pembaruan, atau penghapusan yang lebih kompleks, atau jika Anda perlu membuat kueri data dalam database, gunakan metode kueri.

Sisipkan (Insert)

Anotasi @Insert memungkinkan Anda menentukan metode yang menyisipkan parameternya ke dalam tabel yang sesuai di database. Kode berikut menunjukkan contoh metode @Insert yang valid yang menyisipkan satu atau beberapa objek User ke dalam database:

Kotlin

@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users: User)

    @Insert
    fun insertBothUsers(user1: User, user2: User)

    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)
}

Java

@Dao
public interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public void insertUsers(User... users);

    @Insert
    public void insertBothUsers(User user1, User user2);

    @Insert
    public void insertUsersAndFriends(User user, List<User> friends);
}

Setiap parameter untuk metode @Insert harus berupa instance class entity data Room yang dianotasikan dengan @Entity atau kumpulan instance class entity data. Saat metode @Insert dipanggil, Room akan menyisipkan setiap instance entity yang diteruskan ke tabel database yang bersangkutan.

Jika metode @Insert menerima satu parameter, metode ini dapat menampilkan nilai long, yang merupakan rowId baru untuk item yang disisipkan. Jika parameter adalah array atau kumpulan, metode harus menampilkan array atau kumpulan nilai long sebagai gantinya, dengan setiap nilai sebagai rowId untuk salah satu item yang disisipkan. Untuk mempelajari cara menampilkan nilai rowId lebih lanjut, lihat dokumentasi referensi untuk anotasi @Insert, serta dokumentasi SQLite untuk tabel rowid.

Perbarui (Update)

Anotasi @Update memungkinkan Anda menentukan metode yang memperbarui baris tertentu dalam tabel database. Serupa dengan metode @Insert, metode @Update menerima instance entity data sebagai parameter. Kode berikut menunjukkan contoh metode @Update yang mencoba memperbarui satu atau beberapa objek User di database:

Kotlin

@Dao
interface UserDao {
    @Update
    fun updateUsers(vararg users: User)
}

Java

@Dao
public interface UserDao {
    @Update
    public void updateUsers(User... users);
}

Room menggunakan kunci utama untuk mencocokkan instance entity yang diteruskan ke baris dalam database. Jika tidak ada baris dengan kunci utama yang sama, Room tidak akan membuat perubahan.

Metode @Update secara opsional dapat menampilkan nilai int yang menunjukkan jumlah baris yang berhasil diperbarui.

Hapus (Delete)

Anotasi @Delete memungkinkan Anda menentukan metode yang menghapus baris tertentu dari tabel database. Serupa dengan metode @Insert, metode @Delete menerima instance entity data sebagai parameter. Kode berikut menunjukkan contoh metode @Delete yang mencoba menghapus satu atau beberapa objek User dari database:

Kotlin

@Dao
interface UserDao {
    @Delete
    fun deleteUsers(vararg users: User)
}

Java

@Dao
public interface UserDao {
    @Delete
    public void deleteUsers(User... users);
}

Room menggunakan kunci utama untuk mencocokkan instance entity yang diteruskan ke baris dalam database. Jika tidak ada baris dengan kunci utama yang sama, Room tidak akan membuat perubahan.

Metode @Delete secara opsional dapat menampilkan nilai int yang menunjukkan jumlah baris yang berhasil dihapus.

Metode kueri

Anotasi @Query memungkinkan Anda menulis pernyataan SQL dan menampilkannya sebagai metode DAO. Gunakan metode kueri ini untuk mengueri data dari database aplikasi, atau ketika perlu melakukan penyisipan, pembaruan, dan penghapusan yang lebih kompleks.

Room memvalidasi kueri SQL pada waktu kompilasi. Ini artinya bahwa akan terjadi error kompilasi, bukan kegagalan runtime, jika ada masalah dengan kueri Anda.

Kueri sederhana

Kode berikut menentukan metode yang menggunakan kueri SELECT sederhana untuk menampilkan semua objek User dalam database:

Kotlin

@Query("SELECT * FROM user")
fun loadAllUsers(): Array<User>

Java

@Query("SELECT * FROM user")
public User[] loadAllUsers();

Bagian berikut menunjukkan cara memodifikasi contoh ini untuk kasus penggunaan umum.

Menampilkan subset kolom tabel

Biasanya, Anda hanya perlu menampilkan subkumpulan kolom dari tabel yang sedang dikueri. Misalnya, UI Anda mungkin hanya menampilkan nama depan dan nama belakang pengguna, bukan setiap detail pengguna tersebut. Untuk menghemat resource dan menyederhanakan eksekusi kueri, sebaiknya Anda hanya mengueri kolom yang diperlukan.

Dengan Room, Anda dapat menampilkan objek sederhana dari kueri mana pun selama Anda dapat memetakan kumpulan kolom hasil ke objek yang ditampilkan. Misalnya, Anda dapat menentukan objek berikut untuk menyimpan nama depan dan nama belakang pengguna:

Kotlin

data class NameTuple(
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

Java

public class NameTuple {
    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    @NonNull
    public String lastName;
}

Kemudian, Anda dapat menampilkan objek sederhana tersebut dari metode kueri:

Kotlin

@Query("SELECT first_name, last_name FROM user")
fun loadFullName(): List<NameTuple>

Java

@Query("SELECT first_name, last_name FROM user")
public List<NameTuple> loadFullName();

Room memahami bahwa kueri tersebut menampilkan nilai untuk kolom first_name dan last_name, serta bahwa nilai ini dapat dipetakan ke dalam kolom class NameTuple. Jika kueri menampilkan kolom yang tidak dipetakan ke kolom dalam objek yang ditampilkan, Room akan menampilkan peringatan.

Meneruskan parameter sederhana ke kueri

Sering kali, metode DAO harus menerima parameter agar dapat menjalankan operasi pemfilteran. Room mendukung penggunaan parameter metode sebagai parameter binding di kueri Anda.

Misalnya, kode berikut menentukan metode yang menampilkan semua pengguna di atas usia tertentu:

Kotlin

@Query("SELECT * FROM user WHERE age > :minAge")
fun loadAllUsersOlderThan(minAge: Int): Array<User>

Java

@Query("SELECT * FROM user WHERE age > :minAge")
public User[] loadAllUsersOlderThan(int minAge);

Anda juga dapat meneruskan beberapa parameter atau mereferensikan parameter yang sama beberapa kali dalam kueri, seperti yang ditunjukkan pada kode berikut:

Kotlin

@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
fun loadAllUsersBetweenAges(minAge: Int, maxAge: Int): Array<User>

@Query("SELECT * FROM user WHERE first_name LIKE :search " +
       "OR last_name LIKE :search")
fun findUserWithName(search: String): List<User>

Java

@Query("SELECT * FROM user WHERE age BETWEEN :minAge AND :maxAge")
public User[] loadAllUsersBetweenAges(int minAge, int maxAge);

@Query("SELECT * FROM user WHERE first_name LIKE :search " +
       "OR last_name LIKE :search")
public List<User> findUserWithName(String search);

Meneruskan kumpulan parameter ke kueri

Beberapa metode DAO mungkin mengharuskan Anda meneruskan parameter dengan jumlah bervariasi yang tidak diketahui hingga runtime. Room dapat mengetahui saat suatu parameter merepresentasikan sebuah kumpulan, dan akan otomatis memperluasnya saat runtime berdasarkan jumlah parameter yang diberikan.

Misalnya, kode berikut menentukan metode yang menampilkan informasi semua pengguna dari subset region:

Kotlin

@Query("SELECT * FROM user WHERE region IN (:regions)")
fun loadUsersFromRegions(regions: List<String>): List<User>

Java

@Query("SELECT * FROM user WHERE region IN (:regions)")
public List<User> loadUsersFromRegions(List<String> regions);

Membuat kueri beberapa tabel

Beberapa kueri mungkin memerlukan akses ke beberapa tabel untuk menghitung hasilnya. Anda dapat menggunakan klausa JOIN dalam kueri SQL untuk mereferensikan lebih dari satu tabel.

Kode berikut menentukan metode yang menggabungkan tiga tabel untuk menampilkan buku yang saat ini dipinjamkan kepada pengguna tertentu:

Kotlin

@Query(
    "SELECT * FROM book " +
    "INNER JOIN loan ON loan.book_id = book.id " +
    "INNER JOIN user ON user.id = loan.user_id " +
    "WHERE user.name LIKE :userName"
)
fun findBooksBorrowedByNameSync(userName: String): List<Book>

Java

@Query("SELECT * FROM book " +
       "INNER JOIN loan ON loan.book_id = book.id " +
       "INNER JOIN user ON user.id = loan.user_id " +
       "WHERE user.name LIKE :userName")
public List<Book> findBooksBorrowedByNameSync(String userName);

Anda juga dapat menentukan objek sederhana untuk menampilkan subset kolom dari beberapa tabel gabungan seperti yang dibahas dalam Menampilkan subset kolom tabel. Kode berikut menentukan DAO dengan metode yang menampilkan nama pengguna dan nama buku yang telah dipinjam:

Kotlin

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

    // You can also define this class in a separate file.
    data class UserBook(val userName: String?, val bookName: String?)
}

Java

@Dao
public interface UserBookDao {
   @Query("SELECT user.name AS userName, book.name AS bookName " +
          "FROM user, book " +
          "WHERE user.id = book.user_id")
   public LiveData<List<UserBook>> loadUserAndBookNames();

   // You can also define this class in a separate file, as long as you add the
   // "public" access modifier.
   static class UserBook {
       public String userName;
       public String bookName;
   }
}

Menampilkan multimap

Di Room 2.4 dan lebih tinggi, Anda juga dapat membuat kueri kolom dari beberapa tabel tanpa menentukan class data tambahan dengan menulis metode kueri yang menampilkan multimap.

Pertimbangkan contoh dari bagian Membuat kueri beberapa tabel. Daripada menampilkan daftar instance dari class data kustom yang menyimpan pasangan instance User dan Book, Anda dapat menampilkan pemetaan User dan Book secara langsung dari metode kueri Anda:

Kotlin

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

Java

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

Saat metode kueri menampilkan multimap, Anda dapat menulis kueri yang menggunakan klausa GROUP BY agar dapat memanfaatkan kemampuan SQL untuk penghitungan dan pemfilteran lanjutan. Misalnya, Anda dapat memodifikasi metode loadUserAndBookNames() untuk hanya menampilkan pengguna yang meminjam tiga buku atau lebih:

Kotlin

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id" +
    "GROUP BY user.name WHERE COUNT(book.id) >= 3"
)
fun loadUserAndBookNames(): Map<User, List<Book>>

Java

@Query(
    "SELECT * FROM user" +
    "JOIN book ON user.id = book.user_id" +
    "GROUP BY user.name WHERE COUNT(book.id) >= 3"
)
public Map<User, List<Book>> loadUserAndBookNames();

Jika tidak perlu memetakan keseluruhan objek, Anda juga dapat menampilkan pemetaan antara kolom tertentu dalam kueri dengan menetapkan keyColumn dan valueColumn dalam anotasi @MapInfo pada metode kueri Anda:

Kotlin

@MapInfo(keyColumn = "userName", valueColumn = "bookName")
@Query(
    "SELECT user.name AS username, book.name AS bookname FROM user" +
    "JOIN book ON user.id = book.user_id"
)
fun loadUserAndBookNames(): Map<String, List<String>>

Java

@MapInfo(keyColumn = "userName", valueColumn = "bookName")
@Query(
    "SELECT user.name AS username, book.name AS bookname FROM user" +
    "JOIN book ON user.id = book.user_id"
)
public Map<String, List<String>> loadUserAndBookNames();

Jenis nilai yang ditampilkan khusus

Room menyediakan beberapa jenis nilai yang ditampilkan khusus untuk integrasi dengan library API lainnya.

Kueri yang dipaginasi dengan library Paging

Room mendukung kueri yang dipaginasi melalui integrasi dengan library Paging. Pada Room 2.3.0-alpha01 dan lebih tinggi, DAO dapat menampilkan objek PagingSource agar digunakan dengan Paging 3.

Kotlin

@Dao
interface UserDao {
  @Query("SELECT * FROM users WHERE label LIKE :query")
  fun pagingSource(query: String): PagingSource<Int, User>
}

Java

@Dao
interface UserDao {
  @Query("SELECT * FROM users WHERE label LIKE :query")
  PagingSource<Integer, User> pagingSource(String query);
}

Untuk informasi selengkapnya tentang memilih parameter jenis untuk PagingSource, lihat Memilih jenis kunci dan nilai.

Akses kursor langsung

Jika logika aplikasi memerlukan akses langsung ke baris hasil, Anda dapat menulis metode DAO untuk menampilkan objek Cursor seperti yang ditunjukkan pada contoh berikut:

Kotlin

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    fun loadRawUsersOlderThan(minAge: Int): Cursor
}

Java

@Dao
public interface UserDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    public Cursor loadRawUsersOlderThan(int minAge);
}

Referensi lainnya

Untuk mempelajari lebih lanjut cara mengakses data menggunakan DAO Room, lihat referensi tambahan berikut.

Contoh

Codelab