寫入非同步 DAO 查詢

為防止查詢封鎖 UI,Room 不允許在主執行緒上存取資料庫。此限制意味著您必須將 DAO 查詢設定為非同步。Room 資料庫包含多種不同架構的整合,以提供非同步查詢執行作業。

DAO 查詢分為三個類別:

  • 「單一寫入」查詢,可在資料庫中插入、更新或刪除資料。
  • 「單一讀取」查詢,只會從資料庫讀取資料一次,並傳回當時資料庫的快照結果。
  • 「可觀測讀取」查詢,可在基礎資料庫資料表變更時從資料庫讀取資料,並傳回新的值以反映這些變更。

語言和架構選項

Room 提供整合支援,以實現與特定語言功能和資料庫的互通性。下表顯示了以查詢類型和架構為基礎的適用傳回類型:

查詢類型 Kotlin 語言功能 RxJava Guava Jetpack 生命週期
單一寫入 協同程式 (suspend) Single<T>Maybe<T>Completable ListenableFuture<T> 不適用
單一讀取 協同程式 (suspend) Single<T>Maybe<T> ListenableFuture<T> 不適用
可觀測讀取 Flow<T> Flowable<T>Publisher<T>Observable<T> 不適用 LiveData<T>

本指南說明利用整合功能在 DAO 中實作非同步查詢的三種方法。

Kotlin 搭配 Flow 與協同程式

Kotlin 提供各種語言功能,可讓您在不使用第三方架構的情況下,寫入非同步查詢:

  • 在 Room 2.2 以上版本中,您可以使用 Kotlin 的 Flow 功能來撰寫可觀測的查詢。
  • 在 Room 2.1 及以上版本中,您可以使用 suspend 關鍵字,以使用 Kotlin 協同程式進行非同步 DAO 查詢。

Java 搭配 RxJava

如果您的應用程式使用 Java 程式設計語言,則可以使用 RxJava 架構中的特殊傳回類型來撰寫非同步 DAO 方法。Room 支援下列 RxJava 2 傳回類型:

此外,Room 2.3 及以上版本支援 RxJava 3。

Java 搭配 LiveData 與 Guava

如果您的應用程式使用 Java 程式設計語言,但您不想使用 RxJava 架構,則可以使用下列替代方式來撰寫非同步查詢:

  • 您可以使用 Jetpack 中的 LiveData 包裝函式類別來撰寫非同步可觀測查詢。
  • 您可以使用 Guava 的 ListenableFuture<T> 包裝函式來撰寫非同步單一查詢。

撰寫非同步單一查詢

單一查詢是指僅執行一次、擷取執行當下資料快照的資料庫作業。以下列舉幾種非同步單一查詢:

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);
}

撰寫可觀測查詢

可觀測查詢屬於讀取作業,在查詢參照的任何資料表發生變更時,即會觸發新的值。您可能會使用的一種做法是,在插入、更新或移除基礎資料庫中的項目時,讓顯示的項目清單保持在最新狀態。以下列舉幾個可觀測的查詢:

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);
}

其他資源

如要進一步瞭解非同步 DAO 查詢,請參閱以下其他資源:

網誌