0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JetpackでRoom

Posted at

Room

Roomは、アプリケーションコードとSQLiteストレージの間で機能するラッパー(抽象化コンポーネント)である。SQLiteは、アプリケーションのあらゆるデータを端末内のファイルとして保持するデータベースと捉えられる。Roomは、このSQLite上のデータに対するCRUD操作を一貫して処理すると同時に、データ定義やデータの扱い方を記述できる抽象化レイヤーを提供する。

Roomの詳細に関しては以下参照。
https://developer.android.com/training/data-storage/room?hl=ja

Roomを構成する主要コンポーネント

エンティティ(Entity)

  • 役割:テーブルの構造を定義し、テーブル行のデータを保持するクラス
  • 主なアノテーション
    • @Entity:テーブルを定義する
      • foreignKeys:外部キーの設定
    • @ColumnInfo:特定のカラムの設定を定義する
    • @PrimaryKey:エンティティ内で一意性を保証する主キーを指定する
    • @Embedded:テーブルの一貫性を保ちつつ、フィールドの多いオブジェクトの入れ子化を回避する

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

  • 役割:データに対して行う操作(CRUDなど)を規定するインターフェース/クラス
  • 主なアノテーション
    • @Dao:DAO であることを宣言
    • @Insert:挿入操作を宣言
    • @Update:更新操作を宣言
    • @Delete:削除操作を宣言
    • @Query:クエリ操作を宣言

データベース(RoomDatabase)

  • @Databaseデータベースに格納するエンティティバージョンを指定する
  • 各 DAO に対して、RoomDatabase 内で抽象メソッドを定義する。これによりビルドシステムは、これらのメソッドの実装を提供するサブクラスを生成する

Roomの注意点

  • RoomはUIスレッド上での操作を一切許可しない
  • Roomはコルーチン、RxJava、LiveDataなど複数のライブラリやフレームワークとの互換性を備える

Roomのマイグレーション

  • Roomには標準でマイグレーション機能が備わっている
  • 新しいコードをリリースした後、データベース構築コードが実行されると、保存データのバージョンとクラスで指定されたバージョンを比較して差異を検知する
  • 最新バージョンに到達するまで指定されたマイグレーションを順次実行する

以下のコードはバージョン1からバージョン2にマイグレーションする例

/**
 * Room データベース定義。
 * - entities: この DB で管理するテーブル(User)
 * - version : スキーマのバージョン。変更したら MIGRATION を用意して上げる
 */
@Database(entities = [User::class], version = 2)
abstract class UserDatabase : RoomDatabase() {

    /** User テーブルに対する型安全な操作を提供する DAO の入口 */
    abstract fun userDao(): UserDao

    companion object {
        /** アプリ全体で 1 つだけ使い回すインスタンス */
        private lateinit var userDatabase: UserDatabase

        /**
         * v1 -> v2 への移行定義。
         * users テーブルに role カラム(INT・NOT NULL・デフォルト0)を追加する。
         */
        private val MIGRATION_1_2 = object: Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                // 元コードの "users ADD COLUMN ..." は誤り。ALTER TABLE が必要。
                database.execSQL(
                    "ALTER TABLE users ADD COLUMN role INTEGER NOT NULL DEFAULT 0"
                )
            }
        }

        /**
         * アプリケーションコンテキストを用いて DB を生成/取得する。
         * - Activity/Fragment の Context を直接渡さない(リーク防止)
         * - 初回だけビルドして以降は同じインスタンスを返す
         */
        fun getDatabase(applicationContext: Context): UserDatabase {
            if (!::userDatabase.isInitialized) {
                userDatabase = Room.databaseBuilder(
                    applicationContext,
                    UserDatabase::class.java,
                    "user-db" // 端末内に作られるファイル名
                )
                    .addMigrations(MIGRATION_1_2) // 版上げ時の移行手順を登録
                    .build()
            }
            return userDatabase
        }
    }
}

TypeConverterインスタンス

@TypeConverterアノテーション

  • Room がそのまま保存できない型を、SQLite が扱える基本型に変換/逆変換する関数へ付けるアノテーション
  • @TypeConverterアノテーションを使用すると、Roomは変換が行われる関数を識別しやすくなる

以下、Java/KotlinとSQLiteの型対応表

Java/Kotlin SQLite
String TEXT
Byte, Short, Integer, Long, Boolean INTEGER
Double, Float REAL
Array BLOB

以下実装例

/**
 * Roomがそのままでは保存できない Date 型を
 * Long(UNIXエポックミリ秒) と相互変換するコンバータ。
 *
 * 使い方:
 *  - エンティティ/DAO/Database に @TypeConverters(DateConverter::class) を付ける
 *  - または RoomDatabase.Builder#addTypeConverter(...) で登録
 */
class DateConverter {

    /**
     * Long(ミリ秒) -> Date に変換
     *
     * @param value DBから読み出したエポックミリ秒(null なら未設定)
     * @return Date オブジェクト。value が null の場合は null を返す
     */
    @TypeConverter
    fun from(value: Long?): Date? {
        // value が null ならそのまま null、値があれば Date に包んで返す
        return value?.let { millis -> Date(millis) }
    }

    /**
     * Date -> Long(ミリ秒) に変換(DBへ保存可能な型)
     *
     * @param date アプリ内で扱う Date(null なら未設定)
     * @return 1970/01/01 00:00:00 UTC からの経過ミリ秒。date が null なら null
     */
    @TypeConverter
    fun to(date: Date?): Long? {
        // date が null なら null、値があればミリ秒を返す
        return date?.time
    }
@Databse(entities = [User::class], version = 1)
// DateとLongの型変換を全体適用
@TypeConverts(DateConverter::class)
abstract class UserDatabase : RoomDatabase() {
    // 省略
}

サンプルアプリケーション

Roomを使用したショートテキストの保存・読み込み・削除のサンプルアプリケーションは以下
https://github.com/motojimay/Room-test-android

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?