Room を使用してローカル データベースにデータを保存する Android Jetpack の一部。

比較的大量の構造化データを処理するアプリは、そのデータをローカルに永続化することで大きなメリットを得ることができます。最も一般的なユースケースは、デバイスがネットワークにアクセスできない場合でも、ユーザーがオフラインの間にコンテンツをブラウジングできるように、関連するデータをキャッシュに保存することです。

Room 永続ライブラリは SQLite 全体に抽象化レイヤを提供することで、データベースへのスムーズなアクセスを可能にし、SQLite を最大限に活用できるようにします。特に、Room には次のようなメリットがあります。

  • SQL クエリのコンパイル時検証。
  • 繰り返しが多く間違いを犯しやすいボイラープレート コードを最小限に抑える便利なアノテーション。
  • 効率的なデータベース移行パス。

こうしたことから、SQLite API を直接使用するのではなく、Room を使用することを強くおすすめします。

セットアップ

アプリで Room を使用するには、アプリの build.gradle ファイルに次の依存関係を追加します。

Groovy

dependencies {
    def room_version = "2.6.1"

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"

    // To use Kotlin annotation processing tool (kapt)
    kapt "androidx.room:room-compiler:$room_version"
    // To use Kotlin Symbol Processing (KSP)
    ksp "androidx.room:room-compiler:$room_version"

    // optional - RxJava2 support for Room
    implementation "androidx.room:room-rxjava2:$room_version"

    // optional - RxJava3 support for Room
    implementation "androidx.room:room-rxjava3:$room_version"

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"

    // optional - Test helpers
    testImplementation "androidx.room:room-testing:$room_version"

    // optional - Paging 3 Integration
    implementation "androidx.room:room-paging:$room_version"
}

Kotlin

dependencies {
    val room_version = "2.6.1"

    implementation("androidx.room:room-runtime:$room_version")
    annotationProcessor("androidx.room:room-compiler:$room_version")

    // To use Kotlin annotation processing tool (kapt)
    kapt("androidx.room:room-compiler:$room_version")
    // To use Kotlin Symbol Processing (KSP)
    ksp("androidx.room:room-compiler:$room_version")

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation("androidx.room:room-ktx:$room_version")

    // optional - RxJava2 support for Room
    implementation("androidx.room:room-rxjava2:$room_version")

    // optional - RxJava3 support for Room
    implementation("androidx.room:room-rxjava3:$room_version")

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation("androidx.room:room-guava:$room_version")

    // optional - Test helpers
    testImplementation("androidx.room:room-testing:$room_version")

    // optional - Paging 3 Integration
    implementation("androidx.room:room-paging:$room_version")
}

主要コンポーネント

Room は、次の 3 つの主要コンポーネントで構成されます。

データベース クラスは、そのデータベースに関連付けられている DAO のインスタンスをアプリに提供します。アプリはこの DAO を使用して、関連するデータ エンティティ オブジェクトのインスタンスとしてデータベースからデータを取得できます。また、定義されたデータ エンティティを使用して、対応するテーブルの行を更新したり、挿入用の新しい行を作成したりできます。図 1 に、Room のさまざまなコンポーネントの関係を示します。

図 1. Room ライブラリのアーキテクチャの図。

実装例

このセクションでは、1 つのデータ エンティティと 1 つの DAO で Room データベースを実装する例を示します。

データ エンティティ

次のコードは、User データ エンティティを定義しています。User の各インスタンスは、アプリのデータベースにある user テーブルの行を表します。

Kotlin

@Entity
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

Java

@Entity
public class User {
    @PrimaryKey
    public int uid;

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

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

Room のデータ エンティティの詳細については、Room エンティティを使用してデータを定義するをご覧ください。

データ アクセス オブジェクト(DAO)

次のコードは、UserDao という DAO を定義しています。UserDao は、user テーブルのデータを操作するためにアプリの他の部分が使用するメソッドを提供します。

Kotlin

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    fun findByName(first: String, last: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)
}

Java

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    User findByName(String first, String last);

    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);
}

DAO の詳細については、Room DAO を使用してデータにアクセスするをご覧ください。

データベース

次のコードは、データベースを保持するために AppDatabase クラスを定義しています。AppDatabase はデータベース構成を定義し、永続データに対するアプリのメイン アクセス ポイントとして機能します。データベース クラスは次の条件を満たす必要があります。

  • クラスには、データベースに関連付けられたすべてのデータ エンティティをリストする entities 配列を含む @Database アノテーションを付ける必要があります。
  • クラスは、RoomDatabase を拡張する抽象クラスである必要があります。
  • データベースに関連付けられた DAO クラスごとに、引数ゼロで DAO クラスのインスタンスを返す抽象メソッドを定義する必要があります。

Kotlin

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

Java

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

注: 単一のプロセスで動作するアプリの場合は、AppDatabase オブジェクトをインスタンス化する際にシングルトン デザイン パターンに従う必要があります。各 RoomDatabase インスタンスは非常に高コストであり、単一のプロセス内で複数のインスタンスにアクセスする必要はほとんどありません。

複数のプロセスで動作するアプリの場合は、データベース ビルダーの呼び出しに enableMultiInstanceInvalidation() を組み込みます。各プロセスに AppDatabase のインスタンスがある場合、あるプロセスで共有データベース ファイルを無効化すると、他のプロセス内の AppDatabase のインスタンスにも自動的に反映されます。

使用方法

データ エンティティ、DAO、データベース オブジェクトを定義したら、次のコードを使用してデータベースのインスタンスを作成できます。

Kotlin

val db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java, "database-name"
        ).build()

Java

AppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();

その後、AppDatabase の抽象メソッドを使用して、DAO のインスタンスを取得できます。次に、DAO インスタンスのメソッドを使用して、データベースを操作できます。

Kotlin

val userDao = db.userDao()
val users: List<User> = userDao.getAll()

Java

UserDao userDao = db.userDao();
List<User> users = userDao.getAll();

参考情報

Room の詳細については、以下の参考情報をご覧ください。

サンプル

Codelabs

ブログ