Room を使用してローカル データベースにデータを保存する

Room は、SQLite 全体を対象とする抽象化レイヤを提供し、SQLite を最大限に活用しつつ、スムーズなデータベース アクセスを可能にします。

比較的大量の構造化データを処理するアプリは、そのデータをローカルに永続化することで大きなメリットを得ることができます。最も一般的なユースケースは、関連データのキャッシュを保存することです。これにより、デバイスがネットワークにアクセスできない場合でも、ユーザーはオフラインでコンテンツを閲覧することができます。ユーザーがコンテンツに変更を加えた場合は、デバイスがオンラインに戻った後にサーバーに同期されます。

Room を使用すれば、このような処理が自動的に実行されるため、SQLite の代わりに Room を使用することを強くおすすめします。SQLite API を直接使用したい場合は、SQLite を使用してデータを保存するをご覧ください。

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

  • データベース: データベース ホルダーを格納し、アプリの永続化リレーショナル データに対する基盤接続のメイン アクセス ポイントとして機能します。

    @Database アノテーション付きのクラスは、以下の条件を満たす必要があります。

    • RoomDatabase を拡張する抽象クラスであること。
    • データベースに関連付けられているエンティティのリストをアノテーション内に含ること。
    • 引数が 0 で、@Dao アノテーション付きのクラスを返す抽象メソッドを含むこと。

    実行時に Room.databaseBuilder() または Room.inMemoryDatabaseBuilder() を呼び出すことで、Database のインスタンスを取得できます。

  • エンティティ: データベース内のテーブルを示します。

  • DAO: データベースにアクセスする際に使用するメソッドを格納します。

アプリは Room データベースを使用して、そのデータベースに関連付けられたデータアクセス オブジェクト(DAO)を取得します。次に、アプリは各 DAO を使用してデータベースからエンティティを取得し、各エンティティに加えた変更をデータベースに保存します。最後に、アプリはエンティティを使用して、データベース内のテーブルの列に対応する値を取得、設定します。

Room の各コンポーネント間の関係を図 1 に示します。

図 1: Room アーキテクチャ図

1 つのエンティティと 1 つの DAO を持つサンプル データベース構成の例を次のコード スニペットに示します。

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

UserDao

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

AppDatabase

Kotlin

    @Database(entities = arrayOf(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();
    }
    

上記のファイルを作成した後は、次のコードを使用することで、作成したデータベースのインスタンスを取得できます。

Kotlin

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

Java

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

注: シングル プロセスで実行するアプリの場合は、AppDatabase オブジェクトをインスタンス化する際にシングルトン設計パターンに従ってください。各 RoomDatabase インスタンスは非常に高コストであり、単一のプロセス内で複数のインスタンスにアクセスする必要はほとんどありません。

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

Room を実践的に体験したい場合は、「Android Room でビューを使用する」コードラボや「Android 永続化」コードラボをお試しください。Room のサンプルコードについては、Android Architecture Components サンプルをご覧ください。