Ghi các truy vấn DAO không đồng bộ

Để tránh trường hợp các truy vấn chặn giao diện người dùng, Room không cho phép truy cập cơ sở dữ liệu trên luồng chính. Hạn chế này có nghĩa là bạn phải tạo truy vấn DAO ở chế độ không đồng bộ. Thư viện Room bao gồm quá trình tích hợp với nhiều khung để thực thi truy vấn không đồng bộ.

Chúng tôi phân loại truy vấn DAO thành 3 danh mục sau:

  • Truy vấn ghi một lần: chèn, cập nhật hoặc xoá dữ liệu trong cơ sở dữ liệu.
  • Truy vấn đọc một lần: chỉ đọc dữ liệu từ cơ sở dữ liệu một lần và trả về kết quả kèm theo thông tin tổng quan nhanh về cơ sở dữ liệu tại thời điểm đó.
  • Truy vấn đọc có thể quan sát: đọc dữ liệu từ cơ sở dữ liệu mỗi khi bảng cơ sở dữ liệu cơ bản thay đổi và tạo ra giá trị mới để phản ánh những thay đổi đó.

Tuỳ chọn khung và ngôn ngữ

Room hỗ trợ quá trình tích hợp để có khả năng tương tác với các tính năng và thư viện ngôn ngữ cụ thể. Bảng sau đây trình bày các loại dữ liệu trả về (nếu có) dựa trên loại truy vấn và khung:

Loại truy vấn Tính năng ngôn ngữ Kotlin RxJava Guava Vòng đời Jetpack
Ghi một lần Coroutine (suspend) Single<T>, Maybe<T>, Completable ListenableFuture<T> Không có
Đọc một lần Coroutine (suspend) Single<T>, Maybe<T> ListenableFuture<T> Không có
Đọc có thể quan sát Flow<T> Flowable<T>, Publisher<T>, Observable<T> Không có LiveData<T>

Hướng dẫn này trình bày 3 cách mà bạn có thể sử dụng những quá trình tích hợp này để triển khai truy vấn không đồng bộ trong DAO của mình.

Kotlin với Flow và coroutine

Kotlin cung cấp những tính năng ngôn ngữ cho phép bạn ghi các truy vấn không đồng bộ mà không cần khung của bên thứ ba:

  • Trong Room 2.2 trở lên, bạn có thể dùng chức năng Flow của Kotlin để ghi các truy vấn có thể quan sát.
  • Trong Room 2.1 trở lên, bạn có thể dùng từ khoá suspend để tạo truy vấn DAO ở chế độ không đồng bộ bằng coroutine của Kotlin.

Java với RxJava

Nếu ứng dụng của bạn dùng ngôn ngữ lập trình Java, thì bạn có thể sử dụng các loại dữ liệu trả về chuyên biệt từ khung RxJava để ghi các phương thức DAO không đồng bộ. Room hỗ trợ các kiểu dữ liệu trả về của RxJava 2 sau đây:

Ngoài ra, Room 2.3 trở lên cũng hỗ trợ RxJava 3.

Java với LiveData và Guava

Nếu ứng dụng của bạn dùng ngôn ngữ lập trình Java và bạn không muốn dùng khung RxJava, thì bạn có thể sử dụng những phương án thay thế sau để ghi các truy vấn không đồng bộ:

  • Bạn có thể dùng lớp trình bao bọc LiveData của Jetpack để ghi các truy vấn có thể quan sát không đồng bộ.
  • Bạn có thể dùng trình bao bọc ListenableFuture<T> của Guava để ghi các truy vấn một lần không đồng bộ.

Ghi các truy vấn một lần không đồng bộ

Truy vấn một lần là các thao tác đối với cơ sở dữ liệu chỉ chạy một lần và lấy thông tin tổng quan nhanh về dữ liệu tại thời điểm thực thi. Dưới đây là một số ví dụ về truy vấn một lần không đồng bộ:

Kotlin

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

    @Update
    suspend fun updateUsers(vararg users: User)

    @Delete
    suspend fun deleteUsers(vararg users: User)

    @Query("SELECT * FROM user WHERE id = :id")
    suspend fun loadUserById(id: Int): User

    @Query("SELECT * from user WHERE region IN (:regions)")
    suspend fun loadUsersByRegion(regions: List<String>): List<User>
}

Java

@Dao
public interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public Completable insertUsers(List<User> users);

    @Update
    public Completable updateUsers(List<User> users);

    @Delete
    public Completable deleteUsers(List<User> users);

    @Query("SELECT * FROM user WHERE id = :id")
    public Single<User> loadUserById(int id);

    @Query("SELECT * from user WHERE region IN (:regions)")
    public Single<List<User>> loadUsersByRegion(List<String> regions);
}

Java

@Dao
public interface UserDao {
    // Returns the number of users inserted.
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public ListenableFuture<Integer> insertUsers(List<User> users);

    // Returns the number of users updated.
    @Update
    public ListenableFuture<Integer> updateUsers(List<User> users);

    // Returns the number of users deleted.
    @Delete
    public ListenableFuture<Integer> deleteUsers(List<User> users);

    @Query("SELECT * FROM user WHERE id = :id")
    public ListenableFuture<User> loadUserById(int id);

    @Query("SELECT * from user WHERE region IN (:regions)")
    public ListenableFuture<List<User>> loadUsersByRegion(List<String> regions);
}

Ghi các truy vấn có thể quan sát

Truy vấn có thể quan sát là các thao tác đọc tạo ra giá trị mới mỗi khi có thay đổi đối với các bảng mà truy vấn tham chiếu đến. Một lợi ích của loại truy vấn này là giúp bạn luôn cập nhật danh sách các mục hiển thị khi các mục trong cơ sở dữ liệu cơ bản được chèn, cập nhật hoặc xoá. Dưới đây là một số ví dụ về truy vấn có thể quan sát:

Kotlin

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE id = :id")
    fun loadUserById(id: Int): Flow<User>

    @Query("SELECT * from user WHERE region IN (:regions)")
    fun loadUsersByRegion(regions: List<String>): Flow<List<User>>
}

Java

@Dao
public interface UserDao {
    @Query("SELECT * FROM user WHERE id = :id")
    public Flowable<User> loadUserById(int id);

    @Query("SELECT * from user WHERE region IN (:regions)")
    public Flowable<List<User>> loadUsersByRegion(List<String> regions);
}

Java

@Dao
public interface UserDao {
    @Query("SELECT * FROM user WHERE id = :id")
    public LiveData<User> loadUserById(int id);

    @Query("SELECT * from user WHERE region IN (:regions)")
    public LiveData<List<User>> loadUsersByRegion(List<String> regions);
}

Tài nguyên khác

Để tìm hiểu thêm về các truy vấn DAO không đồng bộ, hãy xem những tài nguyên bổ sung sau đây:

Blog