非同期 DAO クエリを作成する

クエリが UI をブロックしないように、Room はメインスレッドでのデータベース アクセスを許可しません。この制限は、DAO クエリを非同期にする必要があることを意味します。非同期クエリ実行を提供するために、Room ライブラリにはさまざまなフレームワークとの統合が含まれています。

DAO クエリは次の 3 つのカテゴリに分類されます。

  • データベースへのデータの挿入、更新、または削除を行うワンショット書き込みクエリ。
  • データベースからデータを 1 回だけ読み取り、その時点のデータベースのスナップショットで結果を返すワンショット読み取りクエリ。
  • 基になるデータベース テーブルが変更されるたびにデータベースからデータを読み取り、その変更を反映するために新しい値を出力するオブザーバブル読み取りクエリ。

言語とフレームワークのオプション

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 に非同期クエリを実装する方法を 3 つ紹介します。

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> ラッパーを使用して、非同期ワンショット クエリを作成できます。

非同期ワンショット クエリを作成する

ワンショット クエリは、1 回だけ実行され、実行時にデータのスナップショットを取得するデータベース オペレーションです。非同期ワンショット クエリの例を次に示します。

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 クエリの詳細については、以下の参考情報をご覧ください。

サンプル

ブログ